3

I created my custom constraint validator:

class CustomConstraint extends Constraint
{
    public $message = '';
}

class CustomConstraintValidator extends ConstraintValidator
{
    public function validate($value, Constraint $constraint)
    {
         exit($this->context->getObject()); // returns null
    }
}

In docs it is stated that:

Returns the currently validated object.

, but for me it returns NULL instead.

P.S. I do not want to assign this constraint to Entity, only to certain forms or fields.

My form property which is validated:

->add('rejectReasons', null, array(
        'property' => 'name',
        'multiple' => true,
        'constraints' => array(
            new CustomConstraint(array(
                'message' => 'Application can not be refused.'
            )),
        )
    ));

Property in entity:

/**
 * @ORM\ManyToMany(targetEntity="RejectReason")
 * @ORM\JoinTable(name="relationship_application_reject_reasons",
 *      joinColumns={@ORM\JoinColumn(name="application_id", referencedColumnName="id", onDelete="CASCADE")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="reject_reason_id", referencedColumnName="id")}
 *      )
 */
private $rejectReasons;

UPDATE

I tried putting constraint on other string property, I still get NULL.

2
  • Describe a specific use-case example to better understand what you want to do. Commented May 25, 2017 at 10:32
  • I want to create a constraint that check the entity if it is valid to execute some action. It should not be applied all the time to the entity, only to a certain forms. I can pass entity as an option (like a message is passed), but it is bad practice since symfony should be handling this functionnality using getObject() Commented May 25, 2017 at 11:03

4 Answers 4

6

Look at ExecutionContextInterface it says that:

getObject() Returns the currently validated object.

If the validator is currently validating a class constraint, the object of that class is returned. If it is a validating a property or getter constraint, the object that the property/getter belongs to is returned.

In other cases, null is returned.

So as you can see, you have to assign to a class or a property or getter. Otherwise you will get null.

Sign up to request clarification or add additional context in comments.

2 Comments

It is validating a property of entity which is ArrayCollection. See the update.
@IgnasDamunskis in your case, i suggest you to create a rejectReasons form type, and add data_class your entity, with cascade validation.
2

For those making form validation using dependencies itself this can help. I assume that the Symfony version is 3.4 or 4.1 and you have symfony/form on your project.

Build your CustomConstraintValidator

The best way to deal with Symfony Form Validators with some kind of dependency are using CustomValidators

Above is a example that I use to work with them.

Supposed that we have an Entity like

// src/Entity/myEntity.php
namespace App\Entity;
...

class myEntity
{
    private $id;
    private $name; // string, required
    private $canDrive; // bool, not required (default=false)
    private $driveLicense; // string, not required (default = null)

    public function __construct()
    {
        $this->canDrive = false;
    }
    // getters and setters
}

We don't need to populate $driveLicense (cause the attribute its not mandatory), but if $canDrivechange from false to true, now $driveLicense must have a value.

$driveLicense is $canDrive dependent.

To build a form for that and validate $driveLicense correctly on the FormType (the best practice) we need to build a CustomConstraintValidator.

Building CanDriveValidator

// src/Validator/Constraints/CanDrive.php
namespace App\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

class CanDrive extends Constraint
{
    public $message = 'invalid_candrive_args'; // I like translators :D
}

Translator file - optional

//src/translators/validators.en.yaml // 
invalid_candrive_args: When "{{ candrivelabel }} " field is checked you must fill "{{ drivelicenselabel }}"

The validator

// src/Validator/Constraints/CanDriveValidator.php
namespace App\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class CanDriveValidator extends ConstraintValidator
{

    /**
     * Checks if the passed value is valid.
     *
     * @param mixed $value The value that should be validated
     * @param Constraint $constraint The constraint for the validation
     */
    public function validate($value, Constraint $constraint)
    {
        $canDriveField = $this->context->getObject(); // the Field using this validator
        $form = $canDriveField->getParent(); // the formType where the Field reside
        $myEntity = $form->getData(); // The Entity mapped by formType                
            
        if ($myEntity->getCanDrive() == true && $myEntity->getDriveLicense() == null) {
            $this->context->buildViolation($constraint->message)
                ->setParameter('{{ candrivelabel }}', 'Can Drive')
                ->setParameter('{{ drivelicenselabel }}', 'Drive License')
                ->addViolation();
        }            
    } 
}

The form myEntityType

//src/Form/myEntityType.php
namespace App\Form;
use App\Entity\myEntity;
use App\Validator\Constraints\CanDrive;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;


class myEntityType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name') 
            ->add('canDrive', CheckBoxType::class, [
                    'required' => false,
                    'constraints' => array(new canDrive()),                    
                    ]                    
                )
            ->add('driveLicense', TextType::class, ['required' => false])
        ;

    }


    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(['data_class' => myEntity::class]);
    }        
}

Now, when use call isValid() method of myEntityType form and the canDrive field was checked and driveLicense is blank, a Violation will be fired on canDrive field. If canDrive is set to false (not checked, not submitted), nothing happens and form will be valid even when driveLicense is blank.

Comments

1

The answer is simple. Write :

this->context->getRoot()->getData()

and u have the object.

Comments

0

If you are develep a Class Constraint Validator remember to add the getTargets method as example:

public function getTargets()
{
    return self::CLASS_CONSTRAINT;
}

As described here in the doc

4 Comments

It is a property constraint validator not a class.
hi @IgnasDamunskis if you need the whole object in order to validate the single field you need to use a class validator constraint
Doesn't that mean that class will be validated with each entity update? I need validation only in few cases
hi @IgnasDamunskis should indicate only the scope of application, make a try and see what happen :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.