Skip to content

Instantly share code, notes, and snippets.

@asgrim
Last active January 3, 2016 10:49
Show Gist options
  • Save asgrim/8452272 to your computer and use it in GitHub Desktop.
Save asgrim/8452272 to your computer and use it in GitHub Desktop.
Why doesn't the `ValidatorManager` service get injected into the `ValidatorChain` by default so that validators can easily be configured in module.config.php without having to configure the whole form in there also?
<?php
namespace My\Form;
use Zend\Form\Form as ZendForm;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorAwareTrait;
abstract class AbstractForm
extends ZendForm
implements ServiceLocatorAwareInterface, InputFilterProviderInterface
{
use ServiceLocatorAwareTrait;
/**
* Override the validator plugin manager to be the global 'validatormanager'
* service configured from the service manager, because for some reason it
* isn't used by default :/
*/
public function setValidatorManagerToBeGlobalValidatorManager()
{
$validatorPluginManager = $this->getServiceLocator()->get('validatormanager');
$formFactory = $this->getFormFactory();
$inputFactory = $formFactory->getInputFilterFactory();
$inputFactory->getDefaultValidatorChain()->setPluginManager($validatorPluginManager);
}
/**
* (non-PHPdoc)
* @see \Zend\Form\Form::isValid()
*/
public function isValid()
{
$this->setValidatorManagerToBeGlobalValidatorManager();
return parent::isValid();
}
}
<?php
return array(
'validators' => array(
'invokables' => array(
'My\Validator\Test' => 'My\Validator\Test',
),
'alias' => array(
'Test' => 'My\Validator\Test',
),
),
);
<?php
namespace My\Form\MyForm;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\Form\Element;
class MyForm extends AbstractForm
{
public function __construct($name = null)
{
parent::__construct($name);
$myField = new Element\Text('myField');
$myField->setLabel('My Field');
$this->add($myField);
// Some other fields ...
}
public function getInputFilterSpecification()
{
return array(
'myField' => array(
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'Test',
),
),
),
);
}
}
<?php
namespace My\Validator;
use Zend\Validator\AbstractValidator;
class Test extends AbstractValidator
{
const LOL = 'lol';
/**
* @var array
*/
protected $messageTemplates = array(
self::LOL => "Some lols happened",
);
public function isValid($value)
{
$this->error(self::LOL);
return false;
}
}
@asgrim
Copy link
Author

asgrim commented Jan 16, 2014

Basically, I want to configure validators from module.config.php (so that I can use a factory and inject anything that might be required in the validator).

I reported this as a bug in ZF2 some time ago... zendframework/zendframework#4732 , but there does not seem to be built-in way in the framework to use the ValidatorManager instance that is in the service manager when using validators, and this is what I have found:

On Zend\InputFilter\Factory#L46 a new ValidatorChain instance is created. The Zend\Validator\ValidatorChain class is where the validator plugin manager is used for finding the validators, but if we look at Zend\Validator\ValidatorChain#L54 we can see that it always creates a new ValidatorPluginManager instead of using the one that already exists in the Service Manager, so you can never configure it before using it.

My solution isn't great - basically, I create an abstract form that overrides the behaviour of the isValid function so that it calls my method setValidatorManagerToBeGlobalValidatorManager. This method fetches ValidatorManager from the Service Manager to start with. It then instantiates the $inputFilterFactory in exactly the same way as attachInputFilterDefaults method, but then calls setPluginManager(..) on the default validator chain. This means now that I can configure services in the validators array key in module.config.php (including factories!) and use them in any form (that inherits my My/Form/AbstractForm class).

One other thing that MWOP mentioned in the bug report was that the Zend\Form\FormAbstractServiceFactory does what I'm expecting (i.e. this means just configuring all the forms in module.config.php). Unfortunately, we do some pretty crazy stuff with our forms which means we need the flexibility of writing our own form classes, so this route is a no-go. However, I can see at the bottom of Zend\Form\FormAbstractServiceFactory::marshalInputFilter() method, it is doing:

<?php
$inputFilterFactory->getDefaultValidatorChain()->setPluginManager($services->get('ValidatorManager'));

which is exactly the same way as my solution works... but this seems really long-winded...

TL;DR

So basically... why doesn't the ValidatorManager service get injected into the ValidatorChain by default so that validators can easily be configured in module.config.php without having to configure the whole form in there also?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment