Validate Single Form Elements in ZF2

Published on February 6, 2013 by

Sometimes it is useful to only validate single form elements. In Zend Framework 1, this was possible by either using the isValidPartial method on Zend_Form or the isValid method directly on the form elements. In Zend Framework 2, the approach is less obvious, but relatively straightforward nevertheless.

We will be making use of a Zend\InputFilter\InputFilter in this approach. If you need more validation than the form elements’ default validators provide, you will usually create a custom input filter class. This can be added to the form like below.

$form = new \User\Form\MyForm();
$form->setInputFilter(new \User\Form\Filter\MyFilter());

This is, however, optional because if we do not specify an input filter, one is created for us by default such that the default validators can be added. I will explain this in just a moment.

What we will do is to access the Zend\InputFilter\Input objects on the form’s input filter. If we did this directly on the instance we added above, the default validators that most form elements add will not be used for the validation. That is, only the validators that are added within the filter class will be used. Therefore, we will use a small trick to ensure that these default validators are added to the input filter instance. Below is the rest of the code that is needed, which will be discussed afterwards.

$form = new \User\Form\MyForm();
$request = $this->getRequest();

if ($request->isPost()) {
	// Default validators are added to the input filter
	$input_filter = $form->getInputFilter();

	// 'my_form_element' is the name of an element in the form
	$input_element = $input_filter->get('my_form_element');

	// Set the value to validate against
	$input_element->setValue($request->getPost('my_form_element'));

	// Check if the provided value is valid
	if ($input_element->isValid()) {
		// Element value is valid
	}
}

There are a few things to discuss about the above code. Firstly, note that the call to the getInputFilter method on the form adds any default validators to the input filter. If no input filter was added to the form, a new one is created such that the default validators can be added to it. For example, a Zend\Form\Element\Select element adds a Zend\Validator\InArray validator by default. The purpose of this particular default validator is to ensure that submitted values exist in the select element’s haystack. That is, the element is not valid if a value that is not part of the select element is submitted. The validator is added to the input element’s validator chain along with any validators that might have been added to it within the filter class. Thus, if we have a form element with the name age, then the validator will be added to the input element with the same name.

Afterwards, we get the particular Zend\InputFilter\Input instance that we want to validate from the input filter. The name to use is the name of the form element; if you have a form element with the name username then a Zend\InputFilter\Input instance will be added to the input filter with this name containing the default validators. This happens when calling the getInputFilter method. The default validators do of course not overwrite the validators added within the filter class. Any input elements within the filter class should match the form elements’ names as well.

Next, the POST value of the form element is added to the input element. This is the value that the validators will validate against. Lastly, we can call the isValid method on the input element, which will run the attached validators and return a boolean value indicating whether the element was validated successfully or not.

Now we have successfully validated a single form element. Although it may be difficult to grasp what is happening at first, it is still a fairly trivial task. It is, however, definitely not as convenient as in Zend Framework 1.

Featured

Learn Zend Framework today!

Take an online course and become a ZF2 ninja!

Here is what you will learn:

  • Understand the theory of Zend Framework in details
  • How to implement an enterprise-ready architecture
  • Develop professional applications in Zend Framework
  • Proficiently work with databases in Zend Framework
  • ... and much more!
Zend Framework logo
Author avatar
Bo Andersen

About the Author

I am a back-end web developer with a passion for open source technologies. I have been a PHP developer for many years, and also have experience with Java and Spring Framework. I currently work full time as a lead developer. Apart from that, I also spend time on making online courses, so be sure to check those out!

9 comments on »Validate Single Form Elements in ZF2«

  1. Waqas

    Thx, for the nice tutorial. I have a question i am getting
    [notInArray] => The input was not found in the haystack
    against my select element. What i am doing at the moment is am trying to repopulate the Select element some what like this

    
        foreach ($Accounts as $Account)
        {
            $accountsArr[$Account->getId()] = $Account->getName();
        }
    
        $Form->get('account')->setValueOptions($arrData);
    
    

    Finally i am dont know how to get rid of this issue, Kindly tell me the work around for this.

    • Hello Waqas.

      Thank you for your comment. Could you please provide more code for me to inspect and explain your use case? I assume that the $arrData variable above is in fact $accountsArr in your actual application code. Did you verify that the Select element’s data is successfully populated prior to validating the element? Also, did you remember to set the POST value on the input element prior to validating the element as well? I hope to be able to assist you further if you provide a little more information. :-)

      Best regards,
      Bo Andersen

  2. Waqas

    Thank you for your Comment and Taking Info to the consideration. In my case i am using Ajax to create multiple select Boxes. With Same NAME. Now when i post Data i get IDs of the Selected Elements.
    Lets say, I have Select element with name=”account”. In my Scenario i am creating (assuming) 10 Drop Downs, all with Name=”account”. After Post I re-populate the data as mentioned above.
    When i var_dump Haystack, it shows me something as e.g { [0]=>11″ [1]=>”119″ [2]=>”220″}, Logically speaking it is holding IDs of select as (’11’,’119′,’220′). Where as When i re-populate form it holds something like {[11]=>”Hello” [119]=>”World” [220]=>”Nice Day”}. From what i get this is causing haystack issue. For further Code snippet i will try to post my Code Hoping it may help. Sorry if you find my English hard to understand.

    
     public function addAction()
        {
            $ViewModel = new ViewModel();
            $arraySelectData = $this->fetchCurrencies();
            $Form = new VoucherForm($arraySelectData);
            $voucherTypes = $this->getEntityManager()->getRepository('Account\Entity\VoucherType')->findAll();
            $Accounts = $this->PopulateAccounts();
            $voucher_array = array();
            if (count($voucherTypes) > 0)
            {
                foreach ($voucherTypes as $type)
                {
                    $voucher_array[0] = 'Select';
                    $voucher_array[$type->getId()] = $type->getName();
                }
            }
            $Form->get('submit')->setValue('Add');
            $Form->get('voucher_type')->setAttribute('options', $voucher_array);
            $Request = $this->getRequest();
            if ($Request->isPost())
            {
                $VoucherFilter = new VoucherFilter();
                $Form->setInputFilter($VoucherFilter->getInputFilter());
                $Voucher = new \Account\Entity\Voucher();
                $Form->setData($Request->getPost());
    
                $voucherDate = $Request->getPost('voucher_date');
                $idVoucherType = $Request->getPost('voucher_type');
                $idAccounts = $Request->getPost('account');
                $names = $Request->getPost('name');
                $debits = $Request->getPost('debit');
                $credits = $Request->getPost('credit');
                $narrations = $Request->getPost('narration');
                $chequeDate = $Request->getPost('cheque_date');
                $idCurrency = $Request->getPost('currency');
                foreach ($idAccounts as $index => $idAccount)
                {
    
                    $arrData = array();
                    $narration = $narrations[$index];
                    $debit = $debits[$index];
                    $credit = $credits[$index];
                    //================ For populate State during save 
                    $arrData = $this->fetchCodeForVocuher($idVoucherType);
                    $Form->get('account')->setValueOptions($arrData);
                    ///////////////////////////////Set Data Again in form after Posting it
                    $VoucherDetail = new \Account\Entity\VoucherDetail();
                    //////////////////////Set Name For Account START////////////////////////////////////////////
                    $accountsNameArrForSelect = $this->fetchNameForVocuher($idVoucherType);
                    print_r($accountsNameArrForSelect);
                    exit;
                    $Form->get('name')->setValueOptions($accountsNameArrForSelect);
    //                print_r($Form->get('name')->getValueOptions());
    //                exit;
                    
                   
                    if ($Form->isValid())
                    {
                        //******Insert Main-Voucher Data For 
                        if ($index exchangeArray($Request->getPost());
                            if ($idCurrency)
                                $Currency = $this->getServiceLocator()->get('EntityManager')->getRepository('Administration\Entity\Currency')->find($idCurrency);
                            $Voucher->setCurrency($Currency);
                            $Voucher->setVoucherDate($voucherDate);
                            if ($chequeDate)
                                $Voucher->setVoucherDate($chequeDate);
                            $this->getServiceLocator()->get('EntityManager')->persist($Voucher);
                        }
                        //***End
    
                        $VoucherDetail->setVoucher($Voucher);
                        $data = array('debit' => $debit,
                            'credit' => $credit,
                            'narration' => $narration,
                        );
                        $VoucherDetail->exchangeArray($data);
                        $VocuherType = $this->getServiceLocator()->get('EntityManager')->getRepository('Account\Entity\VoucherType')->find($idVoucherType);
                        if ($VocuherType)
                            $Voucher->setVoucherType($VocuherType);
                        $Account = $this->getServiceLocator()->get('EntityManager')->getRepository('Account\Entity\Account')->find($idAccount);
                        if ($Account)
                            $VoucherDetail->setAccount($Account);
                        $this->getServiceLocator()->get('EntityManager')->persist($VoucherDetail);
                    }
                }
                $this->getServiceLocator()->get('EntityManager')->flush();
            }
            $ViewModel->setVariable('form', $Form);
            $ViewModel->setVariable('Accounts', $Accounts);
            return $ViewModel;
        }
    
     private function fetchCodeForVocuher($idVoucherType)
        {
            $accountsArr = array();
            $VoucherType = $this->getServiceLocator()->get('EntityManager')->getRepository('Account\Entity\VoucherType')->find($idVoucherType);
            $AccountType = $VoucherType->getAccountType();
            $idAccountType = $AccountType->getId();
    
            $Accounts = $this->getServiceLocator()->get('EntityManager')->getRepository('Account\Entity\Account')->findBy(array('AccountType' => $idAccountType));
            if (count($Accounts) > 0)
            {
                foreach ($Accounts as $Account)
                {
                    $accountsArr[$Account->getId()] = $Account->getCode();
                }
            }
            return $accountsArr;
    
        }
    
        private function fetchNameForVocuher($idVoucherType)
        {
            $accountsArr = array();
            $VoucherType = $this->getServiceLocator()->get('EntityManager')->getRepository('Account\Entity\VoucherType')->find($idVoucherType);
            $AccountType = $VoucherType->getAccountType();
            $idAccountType = $AccountType->getId();
    
            $Accounts = $this->getServiceLocator()->get('EntityManager')->getRepository('Account\Entity\Account')->findBy(array('AccountType' => $idAccountType));
            if (count($Accounts) > 0)
            {
                foreach ($Accounts as $Account)
                {
                    $accountsArr[$Account->getId()] = $Account->getName();
                }
            }
            return $accountsArr;
        }
    
    
    • Hello Waqas.

      Thank you for providing more information. Since you stated that you have multiple Select elements with the same name, I would expect your $idAccounts to contain an array instead of a single value. The Select element uses the InArray validator to validate that the provided value is in the haystack of permitted values. Perhaps your problem is that this evaluates to false because the provided value is an array, while you do not have any nested arrays within your Select element’s data.

      I took a quick peak at the source code of the Select element and noticed that there is a “multiple” option. As far as I could see, this option allows for multiple values to be submitted for the same Select element. I would try this. Depending on how you construct your form, you can use either of the approaches below.

      
      $form = new Form('my-form');
      $form->add(array(
      	'type' => 'Zend\Form\Element\Select',
      	'name' => 'language',
      	'options' => array(
      		'multiple' => true, //  'Which is your mother tongue?',
      		'value_options' => array(
      			 '0' => 'French',
      			 '1' => 'English',
      			 '2' => 'Japanese',
      			 '3' => 'Chinese',
      		),
      	)
      ));
      
      
      
      $select = new Element\Select('language');
      $select->setLabel('Which is your mother tongue?');
      $select->setAttribute('multiple', true); // setValueOptions(array(
      	 '0' => 'French',
      	 '1' => 'English',
      	 '2' => 'Japanese',
      	 '3' => 'Chinese',
      ));
      
      $form = new Form('language');
      $form->add($select);
      
      

      Also, is there any specific reason why you are using $VoucherFilter->getInputFilter() when you are attaching your input filter to the form? When I do this, I just add the input filter object directly.

      I hope this helps.

      Best regards,
      Bo Andersen

  3. Saulo Toledo

    Just use $form->setValidationGroup(array(‘my_form_element’)) before $form->setData() and all.

    • Hello Saulo,

      Thank you for your comment. I was not aware of this method. I think my IDE did not successfully find this method, and the documentation seemingly lacks any documentation of it. Either way, I will keep this in mind the next time I have to do this and update the article accordingly if it works. It would indeed be a much more convenient solution. Thank you!

      Best regards,
      Bo Andersen

  4. Ragunathan

    I Want to give the required field when i click the check box…

  5. James Brown

    Can I still implement ZF1 style validation to ZF2, because it intiutively made more sense as you read the code!

    ZF2 adds a layer of abstraction, that can lead to confusion!!! Was the abstraction really necessary???

    You see, you can define the validation criteria at the time of FORM creation, so its all in one package. In ZF2, it is not so clear, when you define filters, how they are applied to individual elements within the FORM, makes it trickier to apply complex validation filters to the entire form.

    Any documentation you can point out, including worked examples would be useful, especially when a form is dealing with a multi-varied data set in the fields…. :-).

    Kind regards,

    • Hello James,

      Thank you for your comment. There are several ways in which you can define validation rules for your forms in ZF2. If you really want to keep everything together, then you can implement the getInputSpecification method in your form’s class. There is an example of this in the documentation.

      ZF2 is more complicated in some ways, but also quite flexible. Because of this, you have the choice of keeping things together or separate. I personally prefer to separate the validation rules from the form class, because it is then much easier to define different validation rules in different contexts. But that all depends on your personal preference and the complexity of your project.

      I hope this helped. :-)

Leave a Reply

Your e-mail address will not be published.