2
votes

I have some difficulties about applying validation for only one associated entity.

So I have two entities, News and NewsTranslation. A news could be translated in multiple languages. But I would like to apply validation only if locale is en.

// AppBundle/Entity/News.php
class News
{
    use ORMBehaviors\Translatable\Translatable;
    use ORMBehaviors\Timestampable\Timestampable;

    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var int
     *
     * @ORM\Column(name="status", type="smallint")
     * @Assert\NotBlank
     */
    private $status;

    ...
}
// AppBundle/Entity/NewsTranslation.php
class NewsTranslation
{
    use ORMBehaviors\Translatable\Translation;

    /**
     * @var string
     *
     * @ORM\Column(name="title", type="string", length=255, nullable=true)
     * @Assert\NotBlank
     * @Assert\Length(max = 255)
     */
    private $title;

    /**
     * @var string
     *
     * @ORM\Column(name="text", type="string", nullable=true)
     * @Assert\NotBlank
     */
    private $text;
}
# AppBundle/Resources/config/validation.yml
AppBundle\Entity\News:
    properties:
        translations:
            - Valid: ~

I tried to use a Closure for the validation_groups form option. But it looks like Symfony do validation on News entity and Valid constraint apply the same groups on NewsTranslation.

I know I could use Callback constraint but that's mean to redo NotBlank, Length and other exiting constraints by myself. And I would like to avoid it if possible.

EDIT:

I'm using Symfony 2.8.*

I try using an en validation group. But looks like the validation is launch on News entity with validation_groups. And with Valid constraint the en validation group is given to validate NewsTranlation. So even it's the en or fr translation the group change nothing in this case.

I also try using the validation medatada through an @Assert\Callback or by using loadValidatorMetadata method into NewsTranslation entity. And the problem stay similar. I can't apply an constraint for a specific entity of collection.

2

2 Answers

2
votes

I finally found a way by creating a custom validator. Like this I could use core constraints easily.

In the translation entity, I could use my validator like this:

/**
 * @var string
 *
 * @ORM\Column(name="title", type="string", length=255, nullable=true)
 * @Assert\Length(max = 255)
 * @AppAssert\ValidTranslation(locales = {"fr"}, constraints = {
 *      @Assert\NotBlank
 * })
 */
private $title;

And the validator:

<?php

namespace AppBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraints\Composite;

/**
 * @Annotation
 * @Target({"PROPERTY", "METHOD", "ANNOTATION"})
 *
 * @author Nicolas Brousse
 */
class ValidTranslation extends Composite
{
    public $locales     = array();
    public $constraints = array();

    public function getCompositeOption()
    {
        return 'constraints';
    }

    public function getRequiredOptions()
    {
        return array('locales', 'constraints');
    }
}
<?php

namespace AppBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;

/**
 * @author Nicolas Brousse
 */
class ValidTranslationValidator extends ConstraintValidator
{
    /**
     * If property constraint
     * {@inheritdoc}
     */
    public function validate($value, Constraint $constraint)
    {
        if (!$constraint instanceof ValidTranslation) {
            throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\ValidTranslation');
        }

        if (false) { // @todo check by interface or trait
            throw new UnexpectedTypeException($value, 'not a translation entity');
        }

        $context = $this->context;
        $entity  = $this->context->getObject();

        if (in_array($entity->getLocale(), $constraint->locales)) {
            $context = $this->context;

            if ($context instanceof ExecutionContextInterface) {
                $validator = $context->getValidator()->inContext($context);
                $validator->validate($value, $constraint->constraints);
            } else {
                // 2.4 API
                $context->validateValue($value, $constraint->constraints);
            }
        }
    }
}
0
votes

you form need to return 2 validations_groups, "Default" and the validation group corresponding to the "en" locale