Permalink
174 lines (132 sloc) 7.62 KB

Validator

DoctrineModule provides three validators that work out the box : DoctrineModule\Validator\ObjectExists and DoctrineModule\Validator\NoObjectExists that allow to check if an entity exists or does not exists in database, respectively; DoctrineModule\Validator\UniqueObject that allows to check if a value is only used in one object. They work like any other standard Zend validators.

All three validators accept the following options :

  • object_repository : an instance of an object repository.
  • fields : an array that contains all the fields that are used to check if the entity exists (or does not).

The DoctrineModule\Validator\UniqueObject also needs the following option:

  • object_manager : an instance of an object manager.

For the use_context option and other specifics to DoctrineModule\Validator\UniqueObject see below.

Tip : to get an object repository (in Doctrine ORM this is called an entity repository) from an object manager (in Doctrine ORM this is called an entity manager), you need to call the getRepository function of any valid object manager instance, passing it the FQCN of the class. For instance, in the context of Doctrine 2 ORM, here is how you get the object_repository of the 'Application\Entity\User' entity :

$repository = $entityManager->getRepository('Application\Entity\User');

Simple usage

You can directly instantiate a validator the following way:

$validator = new \DoctrineModule\Validator\ObjectExists([
    'object_repository' => $objectManager->getRepository('Application\Entity\User'),
    'fields' => ['email'],
]);

var_dump($validator->isValid('test@example.com')); // dumps 'true' if an entity matches
var_dump($validator->isValid(['email' => 'test@example.com'])); // dumps 'true' if an entity matches

Use together with Zend Framework 2 forms

Of course, validators are especially useful together with forms. And this is deadly simple. Here is how you would add a NoObjectExists validator to a form element (for more details about Form, please refer to the official Zend Framework 2 documentation) :

namespace Application\Form;

use DoctrineModule\Validator\NoObjectExists as NoObjectExistsValidator;
use Zend\Form\Form;
use Zend\ServiceManager\ServiceManager;

class User extends Form
{
    public function __construct(ServiceManager $serviceManager)
    {
        parent::__construct('my-form');

        // Add an element
        $this->add([
            'type'    => 'Zend\Form\Element\Email',
            'name'    => 'email',
            'options' => [
                'label' => 'Email',
            ],
            'attributes' => [
                'required' => 'required',
            ],
        ]);

        // add other elements (submit, CSRF…)

        // Fetch any valid object manager from the Service manager (here, an entity manager)
        $entityManager = $serviceManager->get('Doctrine\ORM\EntityManager');

        // Now get the input filter of the form, and add the validator to the email input
        $emailInput = $this->getInputFilter()->get('email');

        $noObjectExistsValidator = new NoObjectExistsValidator([
            'object_repository' => $entityManager->getRepository('Application\Entity\User'),
            'fields'            => 'email',
        ]);

        $emailInput->getValidatorChain()
                   ->attach($noObjectExistsValidator);
    }
}

Of course, if you are using fieldsets, you can directly add the validator using the array notation, for instance in the getInputFilterSpecification function, as shown here :

namespace Application\Form;

use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\ServiceManager\ServiceManager;

class UserFieldset extends Fieldset implements InputFilterProviderInterface
{
    protected $serviceManager;

    public function __construct(ServiceManager $serviceManager)
    {
        $this->serviceManager = $serviceManager;

        parent::__construct('my-fieldset');

        // Add an element
        $this->add([
            'type'    => 'Zend\Form\Element\Email',
            'name'    => 'email',
            'options' => [
                'label' => 'Email',
            ],
            'attributes' => [
                'required' => 'required',
            ],
        ]);
    }

    public function getInputFilterSpecification()
    {
        $entityManager = $this->serviceManager->get('Doctrine\ORM\EntityManager');

        return [
            'email' => [
                'validators' => [
                    [
                        'name' => 'DoctrineModule\Validator\NoObjectExists',
                        'options' => [
                            'object_repository' => $entityManager->getRepository('Application\Entity\User'),
                            'fields' => 'email',
                        ],
                    ],
                ],
            ],
        ];
    }
}

You can change the default message of the validators the following way :

// For NoObjectExists validator (using array notation) :
'validators' => [
    [
        'name' => 'DoctrineModule\Validator\NoObjectExists',
        'options' => [
            'object_repository' => $this->getEntityManager()->getRepository('Application\Entity\User'),
            'fields' => 'email',
            'messages' => [
                'objectFound' => 'Sorry guy, a user with this email already exists !',
            ],
        ],
    ],
],

// For ObjectExists validator (using object notation) :
$objectExistsValidator = new \DoctrineModule\Validator\ObjectExists([
    'object_repository' => $entityManager->getRepository('Application\Entity\User'),
    'fields'            => 'email',
]);

**$objectExistsValidator->setMessage('noObjectFound', 'Sorry, we expect that this email exists !');**

Note : as you can see, in order to create a validator in your form objects, you need an object repository, and hence you need to have access to the service manager in order to fetch it (this is also the case for other features from DoctrineModule like custom Form elements). However, when dealing with complex forms, you can have a very deep hierarchy of fieldsets, and "transferring" the service manager from one fieldset to another can be a tedious task, and bring useless complexity to your code, especially if only the deepest fieldset effectively needs the service manager. When dealing with such cases, I have found that the simplest case is to use a Registry. I perfectly know that registry was removed from Zend Framework 2, and it is considered bad practice as it makes testing harder. However, for this very specific use case, I found that this is a nice way to solve the problem. But remember, don't tend to take the easy way out, and don't use this Registry trick every where in your program.

UniqueObject

There are two things you have to think about when using DoctrineModule\Validator\UniqueObject. As mentioned above you have to pass an ObjectManager as object_manager option. Second, you have to pass a value for every identifier your entity has got.

  • If you leave out the use_context option or set it to false you have to pass an array containing the fields- and identifier-values into isValid(). When using Zend\Form this behaviour is needed if you're using fieldsets.
  • If you set the use_context option to true you have to pass the fields-values as first argument and an array containing the identifier-values as second argument into isValid(). When using Zend\Form without fieldsets, this behaviour would be needed.

Important: Whatever you choose, please ensure that the identifier-values are named by the field-names, not by the database-column.