Using DoctrineModule UniqueObject validator annotation in ZF3 app?

372 Views Asked by At

I am trying to use the DoctrineModule's UniqueObject validator annotation in an entity with Zend\Form (ZF3) but am unable to do so.

Annotation in entity:

 * @Annotation\Validator({"name":"DoctrineModule\Validator\UniqueObject"})

Controller method that uses entity:

public function saveAction()
{   
    $id = (int) $this->params()->fromRoute('id', 0);
    $user = $this->em->getRepository(User::class)->find($id);        
    $builder = new AnnotationBuilder();
    ...

Module.php:

public function getServiceConfig()
{
    return [
        'factories' => [
            'DoctrineModule\Validator\UniqueObject' => function ($serviceManager) {
                $uniqueObject = new DoctrineModule\Validator\UniqueObject(array(
                    'fields' => 'username',
                    'object_repository' => $serviceManager->get('Doctrine\ORM\EntityManager')->getRepository('Application\Entity\User'),
                    'object_manager' => $serviceManager->get('Doctrine\ORM\EntityManager'),
                ));
                return $uniqueObject;
            },
        ],
    ];
}

Error seen:

[Catchable fatal error] Argument 1 passed to DoctrineModule\Validator\UniqueObject::__construct() must be of the type array, none given, called in /var/www/jade/vendor/zendframework/zend-servicemanager/src/Factory/InvokableFactory.php on line 32 and defined
[0] file:///var/www/jade/vendor/doctrine/doctrine-module/src/DoctrineModule/Validator/UniqueObject.php.DoctrineModule\Validator\UniqueObject->__construct:66
[1] file:///var/www/jade/vendor/zendframework/zend-servicemanager/src/Factory/InvokableFactory.php.Zend\ServiceManager\Factory\InvokableFactory->__invoke:32
[2] file:///var/www/jade/vendor/zendframework/zend-servicemanager/src/ServiceManager.php.Zend\ServiceManager\ServiceManager->doCreate:747
[3] file:///var/www/jade/vendor/zendframework/zend-servicemanager/src/ServiceManager.php.Zend\ServiceManager\ServiceManager->get:195
[4] file:///var/www/jade/vendor/zendframework/zend-servicemanager/src/AbstractPluginManager.php.Zend\ServiceManager\AbstractPluginManager->get:143
[5] file:///var/www/jade/vendor/zendframework/zend-validator/src/ValidatorChain.php.Zend\Validator\ValidatorChain->plugin:97
[6] file:///var/www/jade/vendor/zendframework/zend-validator/src/ValidatorChain.php.Zend\Validator\ValidatorChain->attachByName:194
[7] file:///var/www/jade/vendor/zendframework/zend-inputfilter/src/Factory.php.Zend\InputFilter\Factory->populateValidators:428
[8] file:///var/www/jade/vendor/zendframework/zend-inputfilter/src/Factory.php.Zend\InputFilter\Factory->createInput:267
[9] file:///var/www/jade/vendor/zendframework/zend-inputfilter/src/Factory.php.Zend\InputFilter\Factory->createInputFilter:357
[10] file:///var/www/jade/vendor/zendframework/zend-form/src/Factory.php.Zend\Form\Factory->prepareAndInjectInputFilter:545
[11] file:///var/www/jade/vendor/zendframework/zend-form/src/Factory.php.Zend\Form\Factory->configureForm:284
[12] file:///var/www/jade/vendor/zendframework/zend-form/src/Factory.php.Zend\Form\Factory->create:114
[13] file:///var/www/jade/vendor/zendframework/zend-form/src/Factory.php.Zend\Form\Factory->createForm:177
[14] file:///var/www/jade/vendor/zendframework/zend-form/src/Annotation/AnnotationBuilder.php.Zend\Form\Annotation\AnnotationBuilder->createForm:246
[15] file:///var/www/jade/module/Application/src/Controller/UserController.php.Application\Controller\UserController->saveAction:132
[16] file:///var/www/jade/vendor/zendframework/zend-mvc/src/Controller/AbstractActionController.php.Zend\Mvc\Controller\AbstractActionController->onDispatch:78
[17] file:///var/www/jade/vendor/zendframework/zend-eventmanager/src/EventManager.php.Zend\EventManager\EventManager->triggerListeners:271
[18] file:///var/www/jade/vendor/zendframework/zend-eventmanager/src/EventManager.php.Zend\EventManager\EventManager->triggerEventUntil:151
[19] file:///var/www/jade/vendor/zendframework/zend-mvc/src/Controller/AbstractController.php.Zend\Mvc\Controller\AbstractController->dispatch:105
[20] file:///var/www/jade/vendor/zendframework/zend-mvc/src/DispatchListener.php.Zend\Mvc\DispatchListener->onDispatch:119
[21] file:///var/www/jade/vendor/zendframework/zend-eventmanager/src/EventManager.php.Zend\EventManager\EventManager->triggerListeners:271
[22] file:///var/www/jade/vendor/zendframework/zend-eventmanager/src/EventManager.php.Zend\EventManager\EventManager->triggerEventUntil:151
[23] file:///var/www/jade/vendor/zendframework/zend-mvc/src/Application.php.Zend\Mvc\Application->run:332
[24] file:///var/www/jade/public/index.php.include:40
[25] file:///var/www/jade/index.php.{main}:2

Based on discussion in the thread DoctrineModule/issues/585, I injected the FormElementManager into the constructor of the UserController via factory and attached it from there to the AnnotationBuilder:

public function __construct(EntityManager $em, \Zend\Form\FormElementManager $fem)
{
    $this->em = $em;
    $this->fem = $fem;
    ...
}


public function saveAction() {
    ...
    $builder->setFormFactory(new \Zend\Form\Factory($this->fem));
    ...
}

However this simply ended up back at the original error/stack trace. Interestingly, when I dump $this->fem, I do see the UniqueObject validator in the dump output so not clear what's going wrong. I'd appreciate any help or suggestions to get the UniqueObject annotation recognized by the AnnotationBuilder.

Some links I referred to when researching this:

https://github.com/doctrine/DoctrineModule/issues/289 http://permalink.gmane.org/gmane.comp.php.zend.framework.general/41719

1

There are 1 best solutions below

2
novrm On

Can try:

<?php
/**
 * Zend Framework Application.
 */

namespace Fork\DoctrineModule\Validator\Factory;

use DoctrineModule\Validator\UniqueObject;

use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;

/**
 * UniqueObjectFactory factory.
 *
 * Creates an instance of UniqueObject.
 *
 * @package Blog\Repository\Factory
 * @link    https://github.com/doctrine/DoctrineModule/blob/master/docs/validator.md
 * @link    https://github.com/doctrine/DoctrineModule/blob/master/src/DoctrineModule/Validator/UniqueObject.php
 */
class UniqueObjectFactory implements FactoryInterface
{

    /**
     * Factory for UniqueObject.
     *
     * @param  ContainerInterface $container
     * @param  string $requestedName
     * @param  array $options
     * @return UniqueObject
     */
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        // Get services.
        $entityManager = $container->get('Doctrine\ORM\EntityManager');

        // If 'object_repository' string, try find repository in EntityManager.
        // Sets ObjectManager.
        if (is_string($options['object_repository'])) {
            $options['object_manager']    = $entityManager;
            $options['object_repository'] = $entityManager->getRepository($options['object_repository']);
        }

        return new UniqueObject($options);
    }

}

and in module

<?php
/**
 * Zend Framework Application.
 */

namespace Fork\DoctrineModule;

/**
 * Configuration for Fork\DoctrineModule module.
 *
 * Changes and extends configuration of DoctrineModule module.
 *
 * @link https://github.com/doctrine/DoctrineModule/blob/master/config/module.config.php
 * @link https://github.com/doctrine/DoctrineModule/blob/master/src/DoctrineModule/Module.php
 */
return [
    /**
     * Configurations for Zend Framework services and components.
     */
    'validators' => [
        'factories' => [
            Validator\UniqueObject::class => Validator\Factory\UniqueObjectFactory::class,
        ],
        'aliases' => [
            'UniqueObject' => Validator\UniqueObject::class,
        ],
    ],
];

and in annotation

* @Annotation\Validator({
*    'name': 'UniqueObject',
*    'break_chain_on_failure': 'true',
*    'options': {
*        'object_repository': 'Application\Entity\User', // you have User::class
*        'fields': {'bla-bla-bla'},          // ??? - what you have?
*        'messages': {
*            'objectFound' => "bla-bla-bla"
*       },
*    }, 
* })