ZF2 + DoctrineORMModule + Boolean field - doctrine-orm

I'm setting up a form through annotations using
/**
* #ORM\Column(type="boolean", nullable=false, name="is_public")
* #Annotation\Required(false)
* #Annotation\AllowEmpty()
* #Annotation\Attributes({"placeholder":"Is Public"})
* #Annotation\Filter({"name":"boolean"})
* #Annotation\Options({"label":"Is Public"})
*/
private $isPublic;
This form is built using the doctrine annotation builder and the doctrine entity hydrator. The entity is then bound to this form. There is an issue when passing a boolean field, in that any value is treated as false, except 1, passing 0 results in an error message of "cannot be empty".
Can somebody please advise as to how I can use boolean fields properly using this method? Ideally I'd like to be able to use the filter before the field is validated? Not only that, but the validation is ignoring the AllowEmpty() and Required(false) fields.
Kind Regards,
ise

What you say is happening seems correct. Checkbox on forms submit nothing when unchecked, but the hydrator obviously needs to be able to know when the user intends to clear the value (mark false).
Pretty sure hydrator should work with empty string for false too. Required and AllowEmpty don't really make sense with a Boolean, especially in your case, because you also put nullable=false

This way I solved it $form->getInputFilter()->get('isPublic')->setContinueIfEmpty(true); just before $form->isValid()

Related

A2LiX Translations validation not working when all fields are submited as empty

When I don't provide any of the translatable properties through my submit form, then I get no validation checking, even when I have implemented:
/**
* #Assert\Valid
*/
protected $translations;
In config.yml I have:
default_locale: cs
required_locales: [cs]
All topics about this problem were giving importance on the #Assert/Valid on $translations property, which I have implemented (I have even tried validation.yml configuration).
Now I realise, that I forgot to add, that I am displaying and submiting the form through Easy Admin bundle. I am not building the form myself. Just configuring Easy Admin settings for my entity. Maybe there's some glitch.
Please refer the following answer link related to same question:
Collection array name fields validation:
A2Lix form validation for translations field
try adding required option to your easy admin type settings
- { property: 'translations', type: 'a2lix_translations', type_options: { required: true} }

how to consider annotation groups in a ObjectConstructor

JMSSerializer comes with a Doctrine Object Constructor that does its job, but imagine an Entity with two properties forming a primary key:
UserBase
prop annotated with #ORM\Id and #Serializer\Groups({"1"})
- username
prop annotated with #ORM\Id and #Serializer\Groups({"2"})
- email
User extends UserBase
- other props here, no Id defined.
one property key is excluded by using group=1 while deserializing. The client potentially still sends both email and username. email should not be considered though.
Unfortunately, if you pass the two properties in the body, DoctrineObjectConstructor does not check if something is excluded by deserialization, so it tries to load the Entity from the DB, according to the two values:
foreach ($classMetadata->getIdentifierFieldNames() as $name) {
if ( ! array_key_exists($name, $data)) {
return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
}
$identifierList[$name] = $data[$name];
}
What I would like to do is taking into account my annotated groups, so as to use the fallbackConstructor in case some property forming the identifier is missing.
As a starter this is a good point: I created my own service, by passing along the annotationDriver. Then, if the property forming the identifier is not associated with the actual group:
$classMetadata = $this->annotationDriver->loadMetadataForClass($metadata->reflection);
$classMetadata->properties //here groups are listed for each property
I can fall back to the fallbackConstructor, as if I had not passed that property in the body
...not so fast! My Entity User extends a UserBase where all my identifier are, so I should take into account hierarchy, possibly in a generic way.
Any hint?
Alright, JMSSerializer's Object Constructor does not consider serialization groups when determing identifiers. Hence, if you include all the IDs in your object, whether they are part of the actual context group or not, they will be counted in.
I created an alternative version of the Object in order to fix this misbehavior (at least for me). Hope it helps

Django: Order of validation in Models

Recently I found out that its possible to define Django form validation directly in the models.py file. This can be done the following way:
fev1_liter = models.DecimalField(validators=[MaxValueValidator(8.2),
MinValueValidator(0.3)],
max_digits=3, decimal_places=2)
This is an awesome alternative to validation in forms.py, but I do have a very annoying problem:
How can I control in which order the validation is executed?
In this example Django will first validate if the inputs digits is in the format x.xx and thereafter min and max value. This results in some very confusing error messages.
Thanks in advance!
For each model field, field.clean() first performs field validation via field.validate(), then via field.run_validators(), validators are called in order they are returned from the field.validators iterator.
This makes sense, because in the general case you can expect your validators to fail if the field validation failed, so it makes for easier debugging. Remember that field validators are non-obligatory, so field.validate() takes precedence. If you want to change the behavior, you'll have to create your own Field classes and override the field.clean() behavior.
You can inspect the field sources for more details.

datepicker posts now() even if empty ZF2 Doctrine2

I have a form with two dates where I use bootstrap3 datepicker. The dates are not mandatory. When I submit the form and one or both of the dates are not filled out, the posted entity contains the actual datetime instead of beeing NULL. When I check the $_POST the dates are NULL.
I use doctrine2 and ZF2. Maybe you need more informations... Thanks in advance for any help.
Solution for me was to add filter Zend\Filter\Null to the form element so that empty strings were converted to NULL.
array(
'name' => 'Zend\Filter\Null',
'options' => array(
'type' => 'string',
),
),
Cause of the problem:
If your form is set to BindOnValidate then a hydrator will be used, which can manipulate the values. It converts strings to DateTime objects if the entity property is a DateTime. Test this using
$form->setBindOnValidate(false);
Which should mean that your value remains an empty string '' immediately after calling isValid().
I found that DoctrineObject hydrator->handleTypeConversions() was converting any string (e.g. from my form POST) into a DateTime object for consistency with the entity field. An empty string results in a DateTime representing time() i.e. current time.
When I intercepted the form POST and converted empty string '' to null, the hydrator left that field as null.
I'm not sure whether it is best to manipulate the POST data within the controller before validation, or to modify the hydration strategy but I presumably either could work?
EDIT: I tried adding a custom strategy to the DoctrineObject hydrator to convert empty string '' to NULL but the DoctrineObject does not trigger the custom strategy, it does the type conversion irrespective.
Copy of the DoctrineObject hydrator code that was making the change:
protected function handleTypeConversions($value, $typeOfField)
{
switch($typeOfField) {
case 'datetime':
case 'time':
case 'date':
if (is_int($value)) {
$dateTime = new DateTime();
$dateTime->setTimestamp($value);
$value = $dateTime;
} elseif (is_string($value)) {
$value = new DateTime($value);
}
break;
default:
}
return $value;
}
You should define the date fields with a default value of NULL and then add the nullable=true option.
/**
* #ORM\Column(type="date", nullable=true)
*/
private $someDateField= null;
Take a look at the documentation for more details.
I discovered something new:
In my controller, the $form->isValid() function is the cause that the date fields are populated with the current date time.
I wrote out the fields values just BEFORE the isValid() and they were NULL as expected, because not filled out in the form.
I wrote out the fields values just AFTER the isValid() and they were NOT NULL, but an object.
The code is quite simple:
if ($form->isValid()) {
$objectManager->persist($group);
$objectManager->flush();
}
I really don't know ghow to solve this...

Auto-truncating fields at max_length in Django CharFields

I have a field that has a max_length set. When I save a model instance, and the field's value is greater than max_length, Django enforces that max_length at the database level. (See Django docs on models: http://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.CharField.max_length)
However, since I am using Postgres, I receive a DatabaseError exception like this:
DatabaseError: value too long for type character varying(1000)
I would prefer to instead auto-truncate the value (so I don't have an exception). Now, I can do this manually, but what I would really want is to have all of my models auto-truncate the value. (Not necessarily intelligently. Just cutting it off at the 999th character is fine.)
Should I just write a custom class that imports from models.Model and override the save() method, looping through each _meta.field, checking for the max_length, and then truncating? That seems inelegant and there must be a better way.
You could create a custom field that auto-truncates the field (I think this code should work, but double-check it):
class TruncatingCharField(models.CharField):
def get_prep_value(self, value):
value = super(TruncatingCharField,self).get_prep_value(value)
if value:
return value[:self.max_length]
return value
Then, instead of using models.CharField in your models.py file, you'd just use TruncatingCharField instead.
get_prep_value prepares the value for a field for insertion in the database, so it's the ideal place to truncate.
Why don't you use a TextField? From the manual:
For large amounts of text, use
TextField.
Why don't you use ModelForm. ModelForm enforces a validation, setting its default max_length to model field's max_length property, and raising proper validation error when form.is_valid() is called. That way you don't have to save the form, until form is validated.
Or, if you want to silently pass the validation and truncate suits best to you, write a simple django form, and write a clean method that truncates input string to the max_length and return stripped data. Take data from form.cleaned_data after form is validated and save the object.
All considering the fact, Forms are designed to validate data before going to DB.
That seems inelegant and there must be a better way.
The only reason the truncate behavior ever happens in the first place is because of MySQL's failure to adhere to the SQL Standard. The throwing of an exception is the correct response when attempting to INSERT a string into a VARCHAR field that is not wide enough to hold it. MySQL truncates and inserts instead.
If you want to silently corrupt your data, then you'll have to do it manually somehow--PostgreSQL will never do it for you.