zendframework / zend-validator

Validator component from Zend Framework
BSD 3-Clause "New" or "Revised" License
181 stars 136 forks source link

Validation Manager doesnt recognize validator factory #40

Closed cbichis closed 6 years ago

cbichis commented 9 years ago

Hi,

It seems using annotations is not possible to use custom validators defined through a factory.

/**
 * User
 * 
 * @ORM\Entity
 * @ORM\Table(name="user", indexes={
 *      @ORM\Index(name="group_id", columns={"group_id"}),
 *      @ORM\Index(name="full_name", columns={"first_name", "last_name"}),
 *      @ORM\Index(name="location_id", columns={"location_id"}),
 *      @ORM\Index(name="created_id", columns={"created_id"}),
 *      @ORM\Index(name="updated_id", columns={"updated_id"}),
 *      @ORM\Index(name="group_id", columns={"group_id"}),
 *      @ORM\Index(name="status", columns={"status"})
 *      },
 *      uniqueConstraints={@ORM\UniqueConstraint(name="email", columns={"email"})}
 * )
 * @ORM\Entity(repositoryClass="Crm\Repository\UserRepository")
 * @ORM\EntityListeners({"Crm\Entity\Listener\CreatedUpdatedListener"})
 */
class User
{

    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false, options={"unsigned"=true})
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * 
     * @Annotation\Exclude
     */
    protected $id;

.....

    /**
     * @var string
     *
     * //@Annotation\Type("email")
     * @Annotation\Type("Crm\Form\Element\Email")
     * @Annotation\Required(true)
     * @Annotation\Filter({"name":"Zend\Filter\StringTrim"})
     * @Annotation\Filter({"name":"Zend\Filter\StripTags"})
     * @Annotation\Validator({"name":"uniqueobject"})
     * @Annotation\Options({"label":"Email", "column-size":"sm-10", "label_attributes":{"class":"col-sm-2"}})
     * @Annotation\Flags({"priority": 93})
     * 
     * @ORM\Column(name="email", type="string", length=128, nullable=false)
     */
    protected $email;

}

And no matter where I place the factory into module_config it doesnt get called...

    'validators' => array(
        'factories' => array(
            'uniqueobject' => function ($sm){
                die('request validator');
                $uniqueObject = new Crm\Validator\UniqueObject(array(
                    'fields' => 'email',
                    'object_repository' => 'Crm\Entity\User',
                    'object_manager' => 'Doctrine\ORM\EntityManager',
                    'service_locator' => $sm
                ));
                return $uniqueObject;
            },
        ),
    ),
    'form_element_manager' => array(
        'factories' => array(
            'uniqueobject' => function ($sm){
                die('request validator');
                $uniqueObject = new Crm\Validator\UniqueObject(array(
                    'fields' => 'email',
                    'object_repository' => 'Crm\Entity\User',
                    'object_manager' => 'Doctrine\ORM\EntityManager',
                    'service_locator' => $sm
                ));
                return $uniqueObject;
            },
        ),
    ),

The error is:

Zend\Validator\ValidatorPluginManager::get was unable to fetch or create an instance for uniqueobject

cbichis commented 9 years ago

Btw, this is NOT a Doctrine related issue, if I create a factory for RecordExists validator I am getting through same issue.

koseduhemak commented 8 years ago

Having same issue... In my form:

public function getInputFilterSpecification()
{
    return array(
        'search' => array(
            'validators' => array(
                array(
                    'name' => 'My\Validator\CustomValidator'
                )
            )
        )
    );
}

In my module.config.php:

'validators' => array(
        'factories' => array(
            'My\Validator\CustomValidator' => 'My\Factory\Validator\CustomValidatorFactory'
        ),
    ),

Factory is never used. The validator is still invoked... Maybe a bug?

func0der commented 8 years ago

I guess that this might be related to not initializing the ValidatorChain and/or its PluginManager.

Zend\Validator\ValidatorChain::getPluginManager() initializes Zend\Validator\ValidatorPluginManager on the fly if no plugins were set. This one does not go through the ServiceLocator and is created without a factory. Ergo it can not have any configurations given in its constructor.

Maybe I am wrong here, but this is the workflow it seems to follow, though I did not have the validators defined in the entity but in a dedicated InputFilter class.

bupy7 commented 7 years ago

I have same problem.

bupy7 commented 7 years ago

Resolve:

Zend\InputFilter\InputFilter every time is creating a new instance of Zend\Validator\ValidatorPluginManager because of this config from validators don't loaded. For fix this need to inject one class manualy to Zend\InputFilter\InputFilter.

Example:

// in a factory `InputFilterFactory`
$vpm = $container->get('PluginManager');
$if = new \Zend\InputFilter\InputFilter;
$if->getFactory()->getDefaultValidatorChain()->setPluginManager($vpm);
return $if;
provodok commented 6 years ago

Solution

Add Zend\InputFilter to config/module.config.php Configure validator and form element in module.config.php

'validators' => [
    'factories' => [
        CustomValidator::class => CustomValidatorFactory::class,
    ],
],
'form_elements' => [
    'factories' => [
        CustomElement::class => CustomElementFactory::class,
    ],
],

Set InputFilterPluginManager into your CustomForm form

class CustomFormFactory implements FactoryInterface
{
    public function __invoke(
        ContainerInterface $container,
        $requestedName,
        array $options = null
    ): CustomForm {
        $form = new CustomForm($em);
        $form->getFormFactory()
            ->getInputFilterFactory()
            ->setInputFilterManager(
                $container->get(InputFilterPluginManager::class)
            );

        return $form;
    }
}

Form

class CustomForm extends Form implements InputFilterProviderInterface
{
    public function __construct()
    {
        parent::__construct('custom-form');

        $this->add(
            [
                'name' => 'submit',
                'type' => Submit::class,
            ]
        );
    }

    public function init()
    {
        $this->add([
            'name' => 'name',
            'type' => CustomElement::class,
        ]);
    }

    public function getInputFilterSpecification(): array
    {
        return [
            'name' => [
                'required' => true,
                'validators' => [
                    ['name' => CustomValidator::class],
                ],
            ],
        ];
    }
}

Getting form

class CustomService
{
   /**
     * Injection
     * @var ServiceManager
     */
    $serviceManager;

    public function getCustomForm(): CustomForm
    {
        if ($this->serviceManager->has(CustomForm::class)) {
            return $this->serviceManager->get(CustomForm::class);
        }

        $form = $this->serviceManager->get('FormElementManager')->get(CustomForm::class);

        $this->serviceManager->setService(CustomForm::class, $form);

        return $form;
    }
}
weierophinney commented 6 years ago

In the future, our forums are a better place to post problems such as this. That way, other developers can help you find a solution, or determine that a bug exists, and help you provide a reproduce case for the issue.