Search code examples
formszend-framework2

Radio button: input was not found in the haystack?


Whenever I submit the form I get this message:

The input was not found in the haystack.

This is for the shipping-method element (radio button). Can't figure out what it means, the POST data for that element is not null.

public function getInputFilter()
{
    if (!$this->inputFilter) {
        $inputFilter = new InputFilter();

        // Some other basic filters

        $inputFilter->add(array(
                'name' => 'shipping-method',
                'required' => true,
                'filters' => array(
                    array('name' => 'StripTags'),
                    array('name' => 'StringTrim')
                ),
                'validators' => array(
                    array(
                        'name' => 'StringLength',
                        'options' => array(
                            'encoding' => 'UTF-8',
                            'max' => 20,
                        ),
                    ),
                    array(
                        'name' => 'Db\RecordExists',
                        'options' => array(
                            'table' => 'shipping',
                            'field' => 'shipping_method',
                            'adapter' => $this->dbAdapter
                        )
                    ),
                ),
        ));

        $inputFilter->get('shipping-address-2')->setRequired(false);
        $inputFilter->get('shipping-address-3')->setRequired(false);

        $this->inputFilter = $inputFilter;
    }

    return $this->inputFilter;
}

I only keep finding solutions for <select>.

Here's the sample POST data:

object(Zend\Stdlib\Parameters)#143 (1) {
  ["storage":"ArrayObject":private] => array(9) {
    ["shipping-name"] => string(4) "TEST"
    ["shipping-address-1"] => string(4) "test"
    ["shipping-address-2"] => string(0) ""
    ["shipping-address-3"] => string(0) ""
    ["shipping-city"] => string(4) "TEST"
    ["shipping-state"] => string(4) "TEST"
    ["shipping-country"] => string(4) "TEST"
    ["shipping-method"] => string(6) "Ground"
    ["submit-cart-shipping"] => string(0) ""
  }
}

UPDATE: form.phtml

<div class="form-group">
    <?= $this->formRow($form->get('shipping-method')); ?>
    <?= $this->formRadio($form->get('shipping-method')
            ->setValueOptions(array(
                'Ground' => 'Ground',
                'Expedited' => 'Expedited'))
            ->setDisableInArrayValidator(true)); ?>
</div>

ShippingForm.php

    $this->add(array(
        'name' => 'shipping-method',
        'type' => 'Zend\Form\Element\Radio',
        'options' => array(
            'label' => 'Shipping Method',
            'label_attributes' => array(
                'class' => 'lbl-shipping-method'
            ),
        )
    ));

Solution

  • The problem lies with when you use the setValueOptions() and the setDisableInArrayValidator(). You should do this earlier within your code as it is never set before validating your form and so the inputfilter still contain the defaults as the InArray validator. As after validation, which checks the inputfilter, you set different options for the shipping_methods.

    You should move the setValueOptions() and the setDisableInArrayValidator() before the $form->isValid(). Either by setting the right options within the form itsself or doing this in the controller. Best way is to keep all of the options in one place and doing it inside the form class.

    $this->add([
        'name' => 'shipping-method',
        'type' => 'Zend\Form\Element\Radio',
        'options' => [
            'value_options' => [
                'Ground'    => 'Ground',
                'Expedited' => 'Expedited'
            ],
            'disable_inarray_validator' => true,
            'label' => 'Shipping Method',
            'label_attributes' => [
                'class' => 'lbl-shipping-method',
            ],
        ],
    ]);
    

    Another small detail you might want to change is setting the value options. They are now hardcoded but your inputfilter is checking against database records whether they exist or not. Populate the value options with the database records. If the code still contains old methods but the database has a few new ones, they are not in sync.

    class ShippingForm extends Form
    {
        private $dbAdapter;
    
        public function __construct(AdapterInterface $dbAdapter, $name = 'shipping-form', $options = [])
        {
            parent::__construct($name, $options)
            // inject the databaseAdapter into your form
            $this->dbAdapter = $dbAdapter;
        }
    
        public function init()
        {
            // adding form elements to the form
            // we use the init method to add form elements as from this point
            // we also have access to custom form elements which the constructor doesn't
    
            $this->add([
                'name' => 'shipping-method',
                'type' => 'Zend\Form\Element\Radio',
                'options' => [
                    'value_options' => $this->getDbValueOptions(),
                    'disable_inarray_validator' => true,
                    'label' => 'Shipping Method',
                    'label_attributes' => [
                        'class' => 'lbl-shipping-method',
                    ],
                ],
            ]);
        }
    
        private function getDbValueOptions()
        {
            $statement = $this->dbAdapter->query('SELECT shipping_method FROM shipping');
            $rows = $statement->execute();
    
            $valueOptions = [];
    
            foreach ($rows as $row) {
                $valueOptions[$row['shipping_method']] = $row['shipping_method'];
            }
    
            return $valueOptions;
        }
    }