user3076049
user3076049

Reputation:

Symfony - get an entity object in a field constraint class

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.

Upvotes: 3

Views: 6857

Answers (4)

mlwacosmos
mlwacosmos

Reputation: 4541

The answer is simple. Write :

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

and u have the object.

Upvotes: 1

Marcos Regis
Marcos Regis

Reputation: 864

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.

Upvotes: 2

Matteo
Matteo

Reputation: 39380

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

Upvotes: 0

Petro Popelyshko
Petro Popelyshko

Reputation: 1379

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.

Upvotes: 6

Related Questions