I have a case, in which I want to reuse a set of rules for InputFilters: filtering and validation.
{ "name": "Hassle free development", "address": { "line": "3/1 online road", "suburb": "Point Cook", "state": "VIC", "country": "Australia" }, "phone": "1300652000", "email": "foalford@gmail.com", }
What I want is to validate and filter the address by configuration. This is natural1) and should be met easily however I found it very hard using the existing zf-content-validation module.
Here is the dilemma:
In my case, the address array isn't a collection. I cannot define a set of rules to measure agains address.line and address.state at the same time. Then I come up with a solution with my customized InputFilter.
class ComposedInputFilter extends CollectionInputFilter { public function setData($data) { $this->data = [$data]; } public function setCount($count) { if ($count > 1) { throw new \RuntimeException("Count always equal or less than 1, $count"); } return parent::setCount($count); } public function setInputFilter($inputFilter) { if (is_string($inputFilter)) { //To reuse the InputFilter definition for other API request. $inputFilter = $this->getFactory()->getInputFilterManager()->get($inputFilter); } return parent::setInputFilter($inputFilter); } public function getValues() { return reset($this->collectionValues); } public function getRawValues() { return reset($this->collectionRawValues); } public function getMessages() { return reset($this->collectionMessages); } }
And with this code, configuration file goes like this
.... 'zf-content-validation' => array( 'GeoSearch\\V1\\Rest\\Supplier\\Controller' => array( 'input_filter' => 'GeoSearch\\V1\\Rest\\Hasslefree\\Validator', ), ), 'input_filter_specs' => array( 'GeoSearch\\V1\\Rest\\Hasslefree\\Validator' => array( 0 => array( 'name' => 'name', 'required' => true, ), 'address' => array( 'name' => 'address', 'type' => 'Geosearch\\InputFilter\\ComposedInputFilter', 'input_filter' => 'GeoSearch\\V1\\Rest\\Supplier\\Address\\Validator' //'input_filter' => array( // Can be the same definition as normal inputfilters //) ... ), // GeoSearch\\V1\\Rest\\Hasslefree\\Validator GeoSearch\\V1\\Rest\\Hasslefree\\Address\\Validator' => array( 0 => array( 'name' => 'line', 'required' => true, 'filters' => array( 0 => array( 'name' => 'Zend\\Filter\\StringTrim', 'options' => array(), ), ), 'validators' => array(), ), ... ) //GeoSearch\\V1\\Rest\\Hasslefree\\Address\\Validator ) //input_filter_specs
However the code above doesn't work and complains about something like Expecting the InputFilter as class instead of a string. The reason is that the InputFilterPluginManager that respects the 'type' parameter within InputFilterSpec isn't the one registerred as ServiceManager. The culprit is because it wasn't injected into the factory. Changing it to construction injection would resolve it.
protected function getInputFilterFactory(ServiceLocatorInterface $services) { if ($this->factory instanceof Factory) { return $this->factory; } $this->factory = new Factory($services->get('InputFilterManager')); ...
Not sure if this is by design or not. An issue has been raised.