Nullable embedded value object with not nullable fields - symfony

I created an entity "Person" in Doctrine2, and I added to it an Adress entity, which is a value object (embeddable).
I want to allow a Person creation, without an Address, so I tag my embedded as "nullable = true". But on the other hand, my Address entity, if it exists, SHOULD contains at least some information (like city, zip code, etc...). So it has "nullable = false" attributes.
Address:
type: embeddable
fields:
[...]
city:
type: string
length: 255
nullable: false
Person:
type: entity
table: null
embedded:
address:
class: Address
nullable: true
It seems that the "nullable = true" of the embedded object was not working.
Do you know if it's a normal Doctrine behaviour ?
Do I have to put all my embeddable attributes to nullable = true ?

Related

GORM creates duplicates on many2many

I am using GORM in my Go Gin API and want to create an association between my User and the Role table. So far, I have simply followed the documentation, but I am running into a problem.
Here are my used models:
type User struct {
gorm.Model
FirstName string
LastName string
Email string `gorm:"unique"`
Password []byte
Roles []Role `gorm:"many2many:user_roles"`
}
type Role struct {
gorm.Model
Name string
}
While I'm still developing, I want to write data directly to the database when I start the API. For this I create the following:
Db.Create(&User{
FirstName:"Jon",
LastName:"Doe",
Email: "jon#doe.local",
Password: encoded,
Roles: []Role{
{Name: "admin"},
{Name: "service"},
},
})
Db.Create(&User{
FirstName:"Jon",
LastName:"Doe",
Email: "jon2#doe.local",
Password: encoded,
Roles: []Role{
{Name: "admin"},
{Name: "service"},
},})
This way I want each name to appear only once in the role table. The corresponding ID should be used in the automatically created user_roles table each time such an entry is created.
What happens, however, is this:
Roles Table:
id
name
1
admin
2
service
3
admin
4
service
User_Role Table:
user_id
role_id
1
1
1
2
2
3
2
4
I have read through https://gorm.io/docs/associations.html and tried Omit but nothing worked yet.
I hope someone has an idea or already solved the same problem. Thanks a lot!
Edit:
When trying "try sending just the role IDs instead of role names" i do it like this:
Db.Create(&User{
FirstName:"Jon",
LastName:"Doe",
Email: "jon2#doe.local",
Password: encoded,
Roles: []Role{
{ID: 1},
{ID: 2},
},})
This gives me the error
cannot use promoted field Model.ID in struct literal of type Role
2. Edit
The ID needs to be listed seperated in the Role model, it does not work just with the gorm.Model
type Role struct {
gorm.Model
ID uint `gorm:"primarykey"`
Name string
}

Dynamoose model update with hash key

I'm trying to execute an update against a dynamoose model. Here's the docs on calling model.update
Model.update(key[, updateObj[, settings]],[ callback])
key can be a string representing the hashKey or an object containing the hashKey & rangeKey.
My schema has both a hash key (partition key) and range key (sort key) like this:
// create model
let model = dynamoose.model(
"SampleStatus",
{
id: {
type: String,
hashKey: true,
},
date: {
type: Date,
rangeKey: true,
},
status: String,
});
I've created an object like this (with a fixed timestamp for demoing)
let timestamp = 1606781220842; // Date.Now()
model.create({
id: "1",
date: new Date(timestamp),
status: "pending",
});
I'd like to be able to update the status property by referencing just the id property like this:
model.update({id: "1"}, {status: "completed"})
// err: The provided key element does not match the schema
model.update("1", {status: "completed"})
// err: Argument of type 'string' is not assignable to parameter of type 'ObjectType'
But both result in the shown errors:
I can pass in the full composite key if I know the timestamp, so the following will work:
let timestamp = 1606781220842; // Date.Now()
model.update({ id: "1", date: timestamp }, { status: "completed" });
However, that requires me holding onto the timestamp and persisting alongside the id.
The ID field, in my case, should, by itself, be unique, so I don't need both to create a key, but wanted to add the date as a range key so it was sortable. Should I just update my schema so there's only a single hash key? I was thinking the docs that said a "`key can be a string representing the hashkey" would let me just pass in the ID, but that throws an error on compile (in typescript).
Any suggestions?
The solution here is to remove the rangeKey from the date property.
This is because in DynamoDB every document/item must have a unique “key”. This can either be the hashKey or hashKey + rangeKey.
Since you mention that your id property is unique, you probably want to use just the hashKey as the key, which should fix the issue.
In your example there could have been many documents with that id, so DynamoDB wouldn’t know which to update.
Don’t forget that this causes changes to your table so you might have to delete and recreate the table. But that should fix the problem you are running into.
Logically there is nothing stopping you than inserting more than 1 entry into the same partition (in your case the unique id). You could insert more than one item with the same id, if it had a different date.
Therefore if you want to get an item by only its partition key, which is really a unique ID, you need to use a query to retrieve the item (as opposed to a GET), but the return signature will be a collection of items. As you know you only have one item in the partition, you can take the first item, and specify a limit of 1 to save RCU.
// create model
let model = dynamoose.model(
"SampleStatus",
{
id: {
type: String,
hashKey: true,
"index": {
"name": "index_name",
"rangeKey": "date",
}
},
date: {
type: Date
},
status: String,
});
You have to tell the schema that hashKey and range are one partition key.
Ref: https://dynamoosejs.com/guide/Schema#index-boolean--object--array

Separate form validation with Meteor

I'm using collection2 and I'm trying to get it to handle validation is a specific way. I have a profile schema which looks kind of like this:
Schema.UserProfile = new SimpleSchema({
name: {
type: String,
optional: false
}
location: {
type: String,
optional: true
}
gender: {
type: String,
optional: false
}
});
Schema.User = new SimpleSchema({
username: {
type: String,
optional: true
},
emails: {
type: Array,
optional: true
},
"emails.$": {
type: Object
},
"emails.$.address": {
type: String,
regEx: SimpleSchema.RegEx.Email
},
"emails.$.verified": {
type: Boolean
},
createdAt: {
type: Date
},
profile: {
type: Schema.UserProfile,
optional: true
},
services: {
type: Object,
optional: true,
blackbox: true
},
roles: {
type: [String],
optional: true
},
heartbeat: {
type: Date,
optional: true
}
});
Meteor.users.attachSchema(Schema.User);
Now, on my registration form I'm requiring the user to select their gender and then later once they log in, users are presented with a separate form asking for their name and location. Here's the problem:
The registration form works and everything goes through with saving. When they try to save the internal form with location and name though I get an error:
Error invoking Method 'updateProfile': Gender is required [400]
I know it's happening because it's required in the schema but I've already obtained this information. How do I not require that? Or do I set up validation per form?
You have to add validation through jquery or you can use toaster for display the error on client side.
Read this also : link
I assume you use aldeed:autoform for your forms. When you use normal type in the form, all fields, even those already filled marked as mandatory have to be submitted. Two ways to fix this:
Dirty way: set hidden field with the prefilled value.
You can also set your form type as update as seen in the doc. This way, simple-schema will validate your newDoc already filled with your previous entries without screaming.
The solution number two is the one I use in most cases. This plus autoform's hooks give you enough flexibility to adapt to most use-cases you might encounter.
I don't know whether or not it is a more elegant solution, but we've stopped attaching simpleSchemas to documents in our current project.
We instead have different schemas in each collection's namespace, one for checking user input on insert, one on update, and and one to be used to fill defaultValue when inserting a new doc (which can be done either by the client or the server, in which case we don't check for input). We call .validate() or .clean() depending on what we want to do.
With clever use of the possibility to build schemas from array of schemas, we're not writing bigger schemas in the end (there's more of them though), but we have total control on when we check, and which fields are checked.
From the SimpleSchema docs:
Say you have a required key "friends.address.city" but
"friends.address" is optional. If "friends.address" is set in the
object you're validating, but "friends.address.city" is not, there is
a validation error. However, if "friends.address" is not set, then
there is no validation error for "friends.address.city" because the
object it belongs to is not present.
So the error happens because you're including profile on both forms and gender is not optional within profile. I can think of two ways to solve this:
Have additional objects under profile that are both optional and contain required fields for name/location on one (though it seems like location might be optional in both scenarios based on your code) and a required field for gender on the other. I don't particularly like this solution but it prevents needing form validation.
Use jQuery form validation (I use the package themeteorchef:jquery-validation) and make all your fields in profile optional.
It also looks like SimpleSchema accepts a function for the optional property, so you could use some custom logic there - maybe you get arguments or a context in that function that will allow you to do what you want?
Hope that helps!

autoform won't render select option field

I have an issue regarding collection2 with relationships and autoform.
I try to implement an 1:n relationship, where each object has exactly 1 objectType, while to each objectType multiple objects can be referred to.
My schema looks as follows:
// register collections
Objects = new Mongo.Collection('objects');
ObjectTypes = new Mongo.Collection('objectTypes');
// define schema
var Schemas = {};
Schemas.ObjectType = new SimpleSchema({ // object type schema
name: {
type: String
}
});
Schemas.Object = new SimpleSchema({ // object schema
type: {
type: ObjectTypes.Schema,
optional: true
},
title: {
type: String
}
});
// attach schemas
ObjectTypes.attachSchema(Schemas.ObjectType);
Objects.attachSchema(Schemas.Object);
My autoform looks like this:
{{> quickForm collection="Objects" id="insertTestForm" type="insert"}}
I actually would expect a select option field for my type attribute, however, a text input appears. Anyone knows why?
According to the documentation [1], it should be a select option field:
If you use a field that has a type that is a Mongo.Collection instance, autoform will automatically provide select options based on _id and name fields from the related Mongo.Collection. You may override with your own options to use a field other than name or to show a limited subset of all documents. You can also use allowedValues to limit which _ids should be shown in the options list.
[1] https://github.com/aldeed/meteor-collection2/blob/master/RELATIONSHIPS.md#user-content-autoform
EDIT
If I use
type: ObjectTypes,
instead of
type: ObjectTypes.Schema,
my app crashes, throwing the following error:
Your app is crashing. Here's the latest log.
/Users/XXX/.meteor/packages/meteor-tool/.1.1.3.ik16id++os.osx.x86_64+web.browser+web.cordova/mt-os.osx.x86_64/dev_bundle/server-lib/node_modules/fibers/future.js:245
throw(ex);
^
RangeError: Maximum call stack size exceeded
Exited with code: 8
Your application is crashing. Waiting for file change.
Your type isn't "a Mongo.Collection instance" like the documentation says; it's a Schema. Try this:
Schemas.Object = new SimpleSchema({
type: {
type: ObjectTypes,
optional: true
},
...
Since nobody could help me solve this incident, I came up with an alternate solution:
// register collections
Objects = new Mongo.Collection('objects');
ObjectTypes = new Mongo.Collection('objectTypes');
// define schema
var Schemas = {};
Schemas.Object = new SimpleSchema({ // object schema
type: {
type: String,
optional: true,
autoform: {
return ObjectTypes.find().map(function(c) {
return{label: c.name, value: c._id}
});
}
},
// ...
});
// attach schema
Objects.attachSchema(Schemas.Object);
As u can see, I manually map the attributes I need from the objectTypes collection into the autoform attribute. Since it returns an array of objects, containing the label and value attributes, autoform will automatically render a select option.

Naming a relation in Doctrine 2 ORM?

How can i set the name of the foreign key (edit: not the name of the attribute itself) for the many-to-one relation "region" using YAML?
SWA\TestBundle\Entity\Province:
type: entity
table: province
uniqueConstraints:
UNIQUE_PROVINCE_CODE:
columns: code
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
code:
type: integer
name:
type: string
length: 255
short_name:
type: string
length: 2
manyToOne:
region:
targetEntity: Region
inversedBy: provinces
Look at the getCreateConstraintSQL method in the AbstractPlatform class to see how the name of the foreign key is chosen (line 1088).
It is taken directly from the constraint name. Influencing constraint name will influence the foreign key name.
As a workaround you could drop the constraint and re-create it with a new name in a doctrine migration.
Due to #JakubZalas answer, I had taken a look to the code in Github, and have seen that changing the framework code for doing what you want is really easy.
If you check the folder where AbstractPlatform class is, you'll see that there is a ForeignKeyConstraint class. In it you'll see it inherits from AbstractAsset.
Now AbstractAsset class has a _generateIdentifierName method. If you check this method in github you'll see that it has a commented part that does just what you want. You just uncomment this part, comment the actual active part, change the $prefix parameter to $postfix and you're done. The constraint name will be generated using the table and column names with a corresponding postfix.
The AbstractAsset.php file is the the this folder: Symfony/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema
I tried in my project ant it worked fine.
One final information: at least for my project the commented part I mention above is only in github, not in the file at my local machine.

Resources