VitalyP
VitalyP

Reputation: 1867

Symfony2: entity asserts that accept an array of types?

I have an entity with the field $companies. This field has to store an array of Company objects. So I described assert this way:

@Assert\Type("Acme\MyBundle\Entity\Company")

But it's always invalid because from my form i receive array of Companies but this assert wants it be not array but just one Company.

So how to overcome this? I suppose that it has to be something like that:

@Assert\Array(Type("Acme\MyBundle\Entity\Company"))

Upvotes: 6

Views: 6869

Answers (2)

JeanValjean
JeanValjean

Reputation: 17713

Since the question is tagged for Symfony2.x, for the sake of completeness I have to point out that the new validation constraint All introduced since version 2.1 can do the whole job.

For each array or traversable objects (e.g. a Doctrine ArrayCollection), you can do the following:

/**
 * @Assert\All({
 *     @Assert\Type(type="Acme\MyBundle\Entity\EntityType")
 * })
 */    
protected $arrayOfEntities;

So, the Symfony2.1 users that are reading your question should prefer this elegant and clean solution.

Upvotes: 13

Crozin
Crozin

Reputation: 44376

There is no built-in constraint that would meet your requirements, therefore you'll have to define your own.

Constraint definition:

namespace MyProject\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

/**
* @Annotation
*/
class CollectionOf extends Constraint {
    public $message = 'This value should be a collection of type {{ type }}';
    public $type;

    public function getDefaultOption() {
        return 'type';
    }

    public function getRequiredOptions() {
        return array('type');
    }
}

Validator definition:

namespace MyProject\Validator\Constraints;

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

class CollectionOfValidator extends ConstraintValidator {
    public function isValid($value, Constraint $constraint) {
        if ($value === null) {
            return true;
        }
        
        if (!is_array($value) && !$value instanceof \Traversable) {
            throw new UnexpectedTypeException($value, 'collection');
        }
        
        if (count($value) === 0) {
            return true;
        }
        
        $type = $constraint->type == 'boolean' ? 'bool' : $constraint->type;
        $function = 'is_' . $type;
        
        $primitiveTest = function_exists($function);
        
        foreach ($value as $item) {
            if (
                ($primitiveTest && !call_user_func($function, $item)) ||
                (!$primitiveTest && !$item instanceof $type)
            ) {
                $this->setMessage($constraint->message, array(
                    '{{ value }}' => is_object($item) ? get_class($item) : gettype($item),
                    '{{ type }}'  => $constraint->type
                ));
                
                return false;
            }
        }
        
        return true;
    }
}

The above validator works for both, collections and arrays.


Edit (2011-06-29)

Import your own constraints:

// My\TestBundle\Entity\Company

use Doctrine\ORM\Mapping as ORM;
use MyProject\Validator\Constraints as MyAssert;
use Symfony\Component\Validator\Constraints as Assert;

class Company {
    ...

    /**
     * @ORM\ManyToOne(...)
     * @Assert\NotNull
     * @MyAssert\CollectionOf("My\TestBundle\Entity\Employee")
     */
    private $employees;
}

To use annotations constraints you'll have to enable them in confirguartion file:

framework:
    ...
    validation:
        enable-annotations: true

Upvotes: 8

Related Questions