Access result of other field validation in a Zend Framework 2 validator - zend-framework2

I have a form with two dates, start and stop. I have a validator for start and I want to validate stop and also that stop is after start. But the after validation only makes sense if start is valid.
isValid($value, $context = null) could be passed the other values in the context variable, but then I have to do the start check again.
So is there a possibility to check the result of the start validation in the stop validator's isValid() function?

You can use Callback
Or just write your own validator
------ Edit - my proposed answer - Input filter with callback or validator ------
I do that like this.
First create a filter with all params:
namespace MyGreatNameSpace\Filter;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\Factory as InputFactory;
class MyDateFilter extends InputFilter
{
public function __construct($myGreatClass)
{
$factory = new InputFactory();
$this->add($factory->createInput(array(
'name' => 'start_date',
'required' => true,
'validators' => array(
array(
'name' => 'Date',
'options' => array(
'format' => '2000-10-10',
),
)
),
)));
$this->add($factory->createInput(array(
'name' => 'end_date',
'required' => true,
'validators' => array(
array(
'name' => 'Date',
'options' => array(
'format' => '2000-10-10',
),
),
array(
'name' => 'Callback',
'options' => array(
'callback' => array($myGreatClass, 'isDateNewer'),
'messages' => array(
'callbackValue' => "The end date is Older then the start date",
),
),
),
),
)));
} // End of __construct
}
Create the callback function
public function isDateNewer($date, $params)
{
$date2 = $params['start_date'];
if ($date > $date2) { // Over simplistic
return TRUE;
}
}
Implant in the controller (I used services to pull the form/filter class)
// Get the form / validator objects from the SM
$form = $this->getServiceLocator()->get('date_form');
$filter = $this->getServiceLocator()->get('date_filter');
// Inject the input filter object to the form object, load the form with data and bind the result to the model
$form->setInputFilter($filter);
$form->setData($post);
$form->bind($myModel); // (if you wish to bind the data to whatever)
if (!$form->isValid()) {
return $this->forward()->dispatch.... (or whatever)
}
Another slightly diffrent way (though cleaner) is to write a validator. Check the Zend\Validator\Identical (note the token)
array(
'name' => '\Application\Validator\myNewNamedValidator',
'options' => array(
'token' => 'start_date',
'messages' => array(
'older' => "The end date is Older then the start date",
),
),
),

Related

Zend 2: Unit tests for form class

I'm just starting using PHPUnit with Zend and need little help to figure out how these tests should work.
I want to test if form return any error message if I do not pass any POST parameters.
The problem is that one field from my form is using Doctrine's DoctrineModule\Form\Element\ObjectSelect
...
$this->add(array(
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'name' => 'user',
'attributes' => array(
'id' => 'user-label',
),
'options' => array(
'object_manager' => $em,
'target_class' => 'Application\Entity\User',
'property' => 'username',
'label' => 'User:',
'display_empty_item' => true,
'empty_item_label' => '---',
'label_generator' => function($entity) {
return $entity->getUsername();
},
),
));
...
I get following error:
Fatal error: Call to a member function getIdentifierFieldNames() on null
I tried override this field with mocked object, however Zend doesn't allow objects in type, just class name (string), so this code doesn't work:
public function testIfFormIsValid()
{
$objectSelect = $this->getMockBuilder('DoctrineModule\Form\Element\ObjectSelect')
->disableOriginalConstructor()
->getMock();
$objectSelect->expects($this->any())
->method('getValueOptions')
->will($this->returnValue(array()));
$form = new \AppModuleComment\Form\Comment('form', array(
'em' => $this->em // Mocked object
));
$form->add(array(
'type' => $objectSelect,
'name' => 'user',
'attributes' => array(
'id' => 'user-label',
),
'options' => array(
'object_manager' => $this->em,
'target_class' => 'Application\Entity\User',
'property' => 'username',
'label' => 'User:',
'display_empty_item' => true,
'empty_item_label' => '---',
'label_generator' => function($entity) {
return $entity->getUsername();
},
),
));
$data = array(
'id' => null,
'user' => null
);
$form->setData($data);
$this->assertTrue($form->isValid(), 'Form is not valid');
}
What am I doing wrong? How should I test such code?
It seems you are testing functionality of Zend or Doctrine (or both) and not your own code. When you use libraries you should trust these libraries.
What happens is: Form\Form::add() uses Form\Factory::create() to create from the array an element. Form\Factory::create() uses Form\FormElementManager::get() to get an element from the given type.
Your type is an object and because Form\FormElementManager::get() can not handle objects your script will fail.
It seems you want to test that if post is empty Form::valid() calls ObjectSelect::valid() but this does not verify if the value is null. That's code from Doctrine / Zend not yours. Don't test it.
More interesting it gets when you want to mock the result of an select from within Doctrines ObjectSelect. But that's another question.

ZF2 Adding filters with element definition

I am trying to add filters and validators with element definition. But so far it is not working. Here is my controller class code.
public function validateAction() {
$testVals = Array ('question1' => 'value1' );
$formMaker = $this->getServiceLocator ()->get ( 'ttForm\Maker' );
$form = $formMaker->generateForm();
$form->setData($testVals);
if ($form->isValid()){
echo "Valid form";
}
else{
print_r($form->getMessages());
echo "Invalid form";
}
die;
}
This is form class code
public function generateForm (){
$element = array (
'options' => array(
'label' => "First Name :",
),
'attributes' => array(
'required' => 'required',
'type' => 'text'
),
'name' => 'firstName',
'filters' => array(
array('name' => 'Zend\Filter\StringTrim'),
),
'validators' => array(
array(
'name' => 'not_empty',
)),
);
$form = new Form();
$form->add($element);
return $form;
}
As you can see in form class, I have attached filters and validators with element definition, it does not work. Looking at docs, it seems like we can do so. Can anybody point out missing link? Thanks.

Zend Framework 2 Setting Zend\InputFilter setRequired()

I have a Form\Element with an InputFilter whose 'required' value is set to "false". Under certain circumstances that Element will need the 'required' value set to "true" from within the Controller.
When I set Zend\InputFilter setRequired(true) in my Controller, it doesn't seem to be respected when the $form->isValid() method is called. However, if the filter is set where 'required' is "true" in the Zend\InputFilter (and not dynamically set inside the Controller) then it works as expected - but that's not my desired solution as I use this form and filter in several locations and sometimes the field is required and other times it is not.
In my Controller, I have the following:
$form = new UserDataForm();
$request = $this->getRequest();
if ($request->isPost()) {
$update = new UserFilter();
// The following doesn't seem to be respected
$update->getInputFilter()->get('userName')->setRequired(true);
$form->setInputFilter($update->getInputFilter());
$form->setData($request->getPost());
if($form->isValid()) {
//The result is true even when the 'userName' var is not set in the POST data.
echo("Is Valid");
} else {
echo($form->getMessages());
}
I have set my UserDataForm() class like so:
class UserDataForm extends Form
{
public function __construct($name = null, array $userTypes) {
parent::__construct('user');
$this->setAttribute('method','post');
$this->add(array(
'name' => 'userName',
'attributes' => array(
'type' => 'text',
'class'=> 'small'
),
'options' => array(
'label' => 'Username:'
)
));
//... and so on...
I have set my UserFilter class like so:
public function getInputFilter() {
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$factory = new InputFactory();
$inputFilter->add($factory->createInput(array(
'name' => 'userName',
'required' => false,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
)
),
)
)));
// ...
Can someone explain why the $update->getInputFilter()->get('userName')->setRequired(true) as called from my Controller doesn't seem to be respected when the form is validated?
I found the problem after resting on it for a while.
Apparently, ->setRequired(true) is mutually exclusive when assigned dynamically. If you are looking for the same behavior as setting 'required' => true from the \FilterInput, then you also need to add ->setAllowEmpty(false) in addition.
So my revised code now looks like this:
$update->getInputFilter()->get('userName')->setRequired(true);
$update->getInputFilter()->get('userName')->setAllowEmpty(false);
Derrick

ZFCUser extend form

I am using Zendframework 2 with ZfcUser and ZfcUserDoctrineORM.
I extended the normal user with some additional information.
Now i want to adapt the registerForm. Therefor i created this form in the ZfcUser\Form folder:
class UserRegister extends ZfcUser\Form\Register {
public function init(){
$this->add(array(
'name' => 'firstName',
'options' => array(
'label' => 'First Name',
),
'attributes' => array(
'type' => 'text'
),
));
$this->add(array(
'name' => 'name',
'options' => array(
'label' => 'Last Name',
),
'attributes' => array(
'type' => 'text'
),
));
}
}
In the Next step I changed adapted the getServiceConfig() function in the Module.php in the ZfcUser folder:
'zfcuser_register_form' => function ($sm) {
$options = $sm->get('zfcuser_module_options');
$form = new Form\UserRegister(null, $options);
//$form->setCaptchaElement($sm->get('zfcuser_captcha_element'));
$form->setInputFilter(new Form\RegisterFilter(
new Validator\NoRecordExists(array(
'mapper' => $sm->get('zfcuser_user_mapper'),
'key' => 'email'
)),
new Validator\NoRecordExists(array(
'mapper' => $sm->get('zfcuser_user_mapper'),
'key' => 'username'
)),
$options
));
return $form;
},
When calling the register url this error message is shown:
Fatal error: Cannot redeclare class UserRegister in C:\xampp\htdocs\THWDiver\vendor\zf-commons\zfc-user\src\ZfcUser\Form\UserRegister.php on line 24
What am I making wrong?
Realize this is an old question, but just stumbled onto it. You need to edit your Entity module's bootstrap and attach to 'ZfcUser\Form\Register' at 'init'.
I've got a blog article here that details the solution in depth:
http://circlical.com/blog/2013/4/1/l5wftnf3p7oks5561bohmb9vkpasp6
Hope it helps you!
I think that the answer is to override the service factory "zfcuser_register_form" and inside of it declare your own RegisterForm.

Zend Framework 2 Di creates class without parameters

I have the following code in config:
<?php
return array(
'di' => array(
'instance' => array(
'alias' => array(
'sms_message' => 'Sms\Message',
),
'sms_message' => array(
'parameters' => array(
'from' => 'SENDER',
),
),
),
),
);
And in Message.php class I have a setter (I dont want to use contructor):
/**
* From
* #var string
*/
protected $from;
/**
* #param string $from
*/
public function setFrom($from)
{
$this->from = $from;
}
But when I try to load it I get unconfigured object:
var_dump($this->getLocator()->get('Sms\Message'));exit;
object(Sms\Message)[596]
protected 'to' => null
protected 'from' => null
protected 'body' => null
How do I can make it work?
For setter-injection you have to use the injections keyword:
array(
'di' => array(
'instance' => array(
'alias' => array(
'sms_message' => 'Sms\Message'
),
'Sms\Message' => array(
'injections' => array(
'setFrom' => array(
'from' => 'SENDER'
),
),
),
),
),
);
I'm not sure if the instance configuration is supposed to work with aliases. Better use the FQCN instead.
I also discovered that currently injections are not executed when requesting an alias while reproducing your example:
// The will call setFrom(...)
$di->get('Sms\Message);
// This will not call setFrom(...)
$di->get('sms_message');
I don't know if this behavior is intended or not. (I'll report this test which is currently failing)

Resources