Symfony2 constraint violation code definition on annotation - php

Due to my project requirements, I must add a numeric code to each and every constraint violation, additional to the message. I have looked through the ConstraintViolation class and seen that it does indeed implement a $code property, so that would be perfect for my needs... except there is no easy way that I could find to specify this code on the annotations.
For instance, looking at how either the Email constraint or its parent class are implemented, there's no sign of that code anywhere, which makes it impossible for the annotations to define it.
The validators also inhibit the coder from setting the $code, as they don't pass the fifth parameter to addValidation, which is the code itself.
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
So, my question is simple: am I missing something? Is there a way to set the constraint violation code via annotations that is not a pain? Ideally, I would like to do something like that:
/**
* #Assert\NotBlank(code=400)
* #Assert\Email(code=401)
*/
protected $email;
Editing Constraint.php to add the $code property (for being a valid annotation code) and all the constraint validators to pass the fifth parameter to addviolation seems a terrible idea!
Of course I could implement my very own validators, but what is the point on having a set of predefined validators then? It's just a one-line change.

I think you have a slight mistake. The code property in the validator is not the one you should look at. I think you should just create your own Validator constraints which are very simple classes:
https://github.com/symfony/Validator/blob/master/Constraints/Choice.php
And just toss in a public code property which then you can set via annotation or PHP. You dont need a new validation service, just that extra property. This propery will be added automatically when the constraint is constructed:
https://github.com/symfony/Validator/blob/master/Constraint.php
Here is more info on how to create the custom constraint:
http://symfony.com/doc/2.1/cookbook/validation/custom_constraint.html

Related

How to use dependency injection in validator constraints with Doctrine & Symfony

I'm working on an platform which contains lot of page with form and i'm looking for a way to inject (DI) service in my Entity/Constraint Validator.
I tried many way, but never managed to do 100% what i wanted it.
Let me explain en detail :
my stack : Php 8.0, Symfony 5.4, Doctrine 2
To make it simple, i will take the example of create page Form.
The form is build with a dedicated FormType (let's say DogType).
The Entiy related to this FormType is Dog
I'm using static loader to load constraints loadValidatorMetadata()
The field dog_race has a constraint ChoiceType.
The data of choice should be filled with a method from a service AnimalManager, method getRaces. The constructor of this service is also using DI to get some other service such as Logger etc.
So, i know we can do something like that :
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
[...]
$metadata->addPropertyConstraints('dog_race',
[
new Assert\Choice([
'callback' => [DogManager::class, 'getRaces'],
'message' => 'doc.incorrect_race'
])
]
)
}
But this callback works only with static method.
If i'm not mistaken, we can use only static loader, php attribut and annotation, right ?
But in all cases, i don't see how i can inject a service for the constraint.
My question is, how can i inject my service DogManager to be used in the constraint ?
If not possible, any recommendation/alternative to do something similar ?
I have the feeling i have to create custom constraint just to be able to inject my service.. :(
Thanks

Using custom ParamConverter for POST Request in Symfony 2

I'm using Symfony 2.6 and the FOS Rest Bundle.
Param converters for PATCH , DELETE and GET requests work nicely and reduce the code in the controller actions. However for POST requests I have a problem. The default \Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter gets called every time. This results in an exception:
Unable to guess how to get a Doctrine instance from the request information.
I checked the \Sensio\Bundle\FrameworkExtraBundle\EventListener\ParamConverterListener and saw that it's always including the Doctrine param converter in the onKernelController method. From the documentation it seems that the doctrine param converter is automatically applied for all type hinted controller actions unless you set it to off:
sensio_framework_extra:
request:
converters: true
auto_convert: false
I found a kind of hacky way to prevent this. The array of param converters to be applied will be indexed by the name of the type hinted argument in the controller method (which symfony gets by reflection). If I just name my param converter the same as this type hint then the default doctrine param converter will not be added to the list of param converters. For example:
...
* #ParamConverter(
* "MyEntity",
* class="Foo\Bar\MyEntity",
* converter="my_custom_converter"
* )
*
* #param MyEntity $myEntity
* #return MyEntity
*/
public function postMyEntityAction(MyEntity $myEntity)
{
I sort of wrote this question as I was digging deeper into the code and I'm not even really sure what my question is anymore. I guess it's "Is it logical to apply multiple param converters?" or would also like to know if it's possible to turn off param converters for certain actions. Maybe my logic is completely wrong here and this isn't what param converters were intended for.
I'd appreciate any advice.
Alright, I realized where I was going wrong. It was a simple case of not returning true from my custom paramConverter apply method. If it does return true then the doctrine param converter won't be applied.

symfony2 find field in form errors

I have a complicated form which has lots of fields in it and it was created by an other developer. Now when I submit the form I get the very common error
This value should not be blank.
I need to find out what is the value. I know about Entities mapping and "nullable=false" property, what I actually need is, the best practice/technique or just a solution, to find out what is this value that is missing. It is a complicated Form and if there is a different path to take rather than go through entity and all subfields that are added to it, I would really appreciate it.
I am pretty sure Symfony devs would have considered that and I need to find how to solve this?
If you are doing this for debugging purpose, you could do something like:
{{ dump(form.getErrors(TRUE,TRUE)) }}
As for the production, you need to put appropriate validation constraint's messages. For example, using annotaions:
use Symfony\Component\Validator\Constraints\NotBlank;
/**
* #NotBlank(message="Title should not be empty")
*/
private $title;
After that, error should display the message above instead of generic one.
There may be new NotBlank() attached to form field in formtype
class.
There may be #Assert\NotBlank attached to field property in entity
class.
There may be #Assert\NotBlank attached to field property in model
class.
So if you set update them as below, you'll know what field it is.
new NotBlank('message' => 'Code is required.')
#Assert\NotBlank(message="Code is required.")
Or just work with Form error handling class.

How do I pass a parameter to a validation constraint in Symfony2 - in yml

I am trying to add a bundle-wide parameter to my application so that I can add it to my Validation Constraint file (validation.yml):
myApp\myBundle\Entity\Contact:
properties:
name:
- NotBlank: { message: "%myvariable%" }
I added my parameter normally in config.yml:
parameters:
# Validation config
myvariable: Please tell us your name.
But the page just renders the %myvariable% text, rather than the desired string. I also wish to use this parameter in my FormBuilderInterface when adding the validation messages to the page for usage in JavaScript. Does yml allow this? If not, how do I include such a parameter at a higher level?
No, it's not currently possible.
It has nothing to do with YAML or XML or even service definitions. Validator component reads validation rules by itself - as you can see, the structure is quite different than for service definitions. Unfortunately, it does not replace the parameters in constraints.
The main logic resides in \Symfony\Component\Validator\Mapping\Loader\YamlFileLoader which is created by \Symfony\Component\Validator\ValidatorBuilder::getValidator.
You could make this happen by:
Overriding definition of validator.builder service.
It's constructed using %validator.builder.factory.class%::createValidatorBuilder, but as you have to get parameter bag somehow, there is not enough dependencies - class factory is in use, not service factory.
Creating new class, which extends ValidatorBuilder.
It should take parameter bag into constructor or via setter. It should be configured in step (1) to be passed here.
This class would create file loaders of another class (see 3), also pass that parameter bag into it.
Creating new classes for YamlFileLoader and YamlFilesLoader. Additional 2 for each format that you would want to support.
It would additionally take parameter bag into constructor and override some functionality. For example, I think all parameter handling could be done in newConstraint method - iterate through options, resolve parameters, then call parent method with replaced options.
It's nice that Symfony could be extended like that (possibly not so nicely in this use-case), but I guess it would be easier to just write your own constraint with custom constraint validator, which would inject that parameter into it.
Also consider a wrapper around validator service - if you just need to replace the validation messages, you could replace the validator service, injecting original one into it. See http://symfony.com/doc/current/service_container/service_decoration.html for more information.

Doctrine 2: Force scheduleForUpdate on a non-changed entity

How can I schedule an entity for update, manually, when no property is actually changed?
I tried $entityManager->getUnitOfWork()->scheduleForUpdate($entity) but it gave an error in the core, and I have no intetion of debuging Doctrine.
The entity is managed if it matters: $entity = $repository->findOne(1)
I need this so doctrine would call my EventSubscriber on flush().
I've also tried something like $entityManager->getEventManager()->dispatchEvent(\Doctrine\ORM\Events::preUpdate), but then my listener's preUpdate() receives EventArgs instead of PreUpdateEventArgs.
Any help is appreciated!
Method mentioned by Wpigott not working for me (at least in doctrine/orm v2.4.2), instead I'm using this:
$entityManager->getUnitOfWork()->setOriginalEntityProperty(spl_object_hash($entity), 'field_name', '__fake_value__');
Where field_name existent property name.
The solution is a bit hacky, but I was able to achieve this by doing something like the following.
$objectManager->getUnitOfWork()->setOriginalDocumentData($object, array('__fake_field'=>'1'));
This essentially causes Doctrine to think the document has changed from the original, and it computes it as a change which will cause the events to be executed on flush.
The example is for the MongoODM solution, but the same technique should work for ORM like below.
$objectManager->getUnitOfWork()->setOriginalEntityData($object, array('__fake_field'=>'1'));
Even though this question is quite a bit old, I just found a way much more elegant to solve this problem I want to share here using Doctrine ORM 2.6.2:
You can simply tell the ClassMetadataInfo object of your table to just state those fields as dirty that you pass to the propertyChanged function of the UnitOfWork. The important parts here are the setChangeTrackingPolicy and propertyChanged calls:
$unitOfWork = $entityManager->getUnitOfWork();
$classMeta = $entityManager->getClassMetadata('Your\Table\Class');
$ctp = $classMeta->changeTrackingPolicy; # not a function but a variable
# Tell the table class to not automatically calculate changed values but just
# mark those fields as dirty that get passed to propertyChanged function
$classMeta->setChangeTrackingPolicy(ORM\ClassMetadataInfo::CHANGETRACKING_NOTIFY);
# tell the unit of work that an value has changed no matter if the value
# is actually different from the value already persistent
$oldValue = $entity->getValue('fieldName'); # some custom implementation or something
$newValue = $oldValue;
$unitOfWork->propertyChanged($entity, 'fieldName', $oldValue, $newValue);
# field value will be updated regardless of whether its PHP value has actually changed
$entityManager->flush();
# set the change tracking policy back to the previous value to not mess things up
$classMeta->setChangeTrackingPolicy($ctp);
You may also want to have a look at the Doctrine\Common\NotifyPropertyChanged interface.
I hope this will be useful for someone.
What's the goal? If there is no property changed, why would you plan an update?

Categories