phpvalidationzend-framework2

Injecting a service into a custom validator


Trying to create a custom validator, I need to use a service from its isValid method.

How to inject that service ?

As an alternative, I could try to instantiate the service from within the validator method, but the service constructor requires a controller object, which I don't have at that point.

Here is the validator:

class OrderNumber extends AbstractValidator
{
    const EXIST = 'exist';

    /**
     * @var array
     *
     */
    protected $messageTemplates = array();

    /**
     * @var element name
     *
     */
    private $element = null;


    /**
     * Options for this validator
     *
     * @var array
     */
    protected $options = array(
        'manager' => null,  // set entity manager to find if value already exist
    );

    /**
     * Constructor
     *
     * @param  array|Traversable|int $options OPTIONAL
     */
    public function __construct($options)
    {
        $this->messageTemplates = array(
            self::INVALID => \Application\Util\Translator::translate("This value is required"),
        );

        if (!isset($options['element']) || empty($options['element'])) {
          throw new \Exception('Element name is not defined');
        }

        $this->element = $options['element'];

        parent::__construct();
    }

    /**
     * Returns true if and only if no record where found in Autoself for the value.
     *
     * @param  string $value
     * @return bool
     */
    public function isValid($value, array $context = null) {
      $isAvailable = $vehiculeService->orderNumberIsAvailable($value);

      if ($vehiculeService->orderNumberIsAvailable($value)) {
        return true;
      } else {
        return false;
      }
    }
}

Solution

  • I needed to pass the service manager in a validator for one of my projects. Confronted with the same problem as you, I decided to pass the service manager as a parameter in the options array. Here is may code :

    public function __construct(array $options)
    {
        $this->db_manager = $options['db_manager'];
        parent::__construct($options);
    }
    

    Here, db_manager is a service manager containing only what I need. As a matter of principle, I never get the full service manager.

    This validator is then used in a form. To do this, in the class of the form I define a method getInputFilterSpecification containing the following array :

    'recordSource' => [
                'name' => 'recordSource',
                'required' => true,
                'filters' => [
                    [
                        'name' => 'StripTags'
                    ],
                    [
                        'name' => 'StringTrim'
                    ],
                    [
                        'name' => 'SbmPdf\Model\Filter\NomTable',
                        'options' => [
                            'db_manager' => $this->db_manager
                        ]
                    ]
                ],
                'validators' => [
                    [
                        'name' => 'SbmPdf\Model\Validator\RecordSource',
                        'options' => [
                            'db_manager' => $this->db_manager,
                            'auth_userId' => $this->auth_userId
                        ]
                    ]
                ]
            ],
    

    The form itself being built by a factory :

    class DocumentPdfFactory implements FactoryInterface
    {
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $db_manager = $serviceLocator->get('Sbm\DbManager');
        $pdf = $serviceLocator->get(Tcpdf::class);
        return new DocumentPdf($db_manager, ...);
    }