doukang1962 2018-01-30 10:40
浏览 17
已采纳

Zend Form没有绑定对象和访问字段集数据

I have Zend Framework 2 Form:

    $form = new Form();
    $form->add(
        [
            'name' => 'input1',
            'type' => 'Text',
        ]
    );

    $fieldset1 = new Fieldset();
    $fieldset1->setName('field1');
    $fieldset1->add(
        [
            'name' => 'input2',
            'type' => 'Text',
        ]
    );

and controller for it:

    $request = $this->getRequest();
    if ($request->isPost()) {
        $form->setData($request->getPost());
        if ($form->isValid()) {
            $data = $form->getData();

            var_dump($this->params()->fromPost(),$data);
            exit;
        }
    }

and problem is that when i dump values i get this:

  array (size=3)
  'input1' => string 'a' (length=1)
  'input2' => string 'b' (length=1)

  array (size=3)
  'input1' => string 'a' (length=1)
  'field1' => 
    array (size=1)
      'input2' => null

So what i do wrong? Because now in "field2" key i get "nulll". how i can get access to fieldset(s) data (after filters, validation etc) in that case?

Update: as i see, when i add to POST

    <input name="field1[input2]" value="test" />

i get expected result. but why zendform not generate html like that, but (wrongly) generate:

    <input name="input2" />

what i do wrong?

  • 写回答

2条回答 默认 最新

  • doutan5724 2018-01-31 09:35
    关注

    here is a complete more or less simple example with entities, input filters, hydrators and validators für zf2 form use with fieldsets.

    First set up the fieldset class you want to use.

    namespace Application\Form;
    
    use Zend\Filter\StripTags;
    use Zend\Form\Fieldset;
    use Zend\Form\Element\Text;
    use Zend\InputFilter\InputFilterProviderInterface;
    
    class MyFieldset extends Fieldset implements InputFilterProviderInterface
    {
        /**
         * @see \Zend\Form\Element::init()
         */
        public function init()
        {
            $this->add([
                'name' => 'input2',
                'type' => Text::class,
                'required' => true,
                'attributes' => [
                    'id' => 'input2',
                    'required' => true,
                ],
                'options' => [
                    'label' => 'The label for input2 of this fieldset',
                ],
            ]);
        }
    
        /**
         * @see \Zend\InputFilter\InputFilterProviderInterface::getInputFilterSpecification()
         */
        public function getInputFilterSpecification()
        {
            return [
                'input2' => [
                    'required' => true,
                    'filters' => [
                        [
                            'name' => StripTags::class,
                        ],
                    ],
                ],
            ];
        }
    }
    

    Your fieldset class defines all input elements within the fieldset. I encurage you to work with entity classes and factories. this is also the reason this example works with the init method. The init method is called after the constructor of the class. While using factories you can use the constructor for defining stuff you need for your fieldset or form class. For example depending input fields and so on.

    Next you should write an entity for your fieldset.

    namespace Application\Entity;
    
    class MyFieldsetEntity
    {
        protected $input2;
    
        public function getInput2()
        {
            return $this->input2;
        }
    
        public function setInput2($input2)
        {
            $this->input2 = $input2;
            return $this;
        }
    }
    

    This simple entity class will handle the data you have sent to your controller. One of the benefits of a entity class is, that you can define default values in it. If the post data should be empty for some reason, the entity can return default values. Let 's put it all together in a factory for your fieldset.

    namespace Application\Form\Service;
    
    class MyFieldsetFactory
    {
        public function __invoke(ContainerInterface $container)
        {
            $hydrator = new ClassMethods(false);
            $entity = new MyFieldsetEntity();
    
            return (new MyFieldset())
                ->setObject($entity)
                ->setHydrator($hydrator);
        }
    }
    

    Why is using a factory smart? Because you can use all the favors of an object orientated environment. You can define all the stuff you need in a factory. for this purpose we create a fieldset instance with an entity and a hydrator. This will hydrate the fieldset with the filtered and validated data.

    All that we need now is the form and an entity for the form.

    namespace ApplicationForm;
    
    use Zend\Form\Element\Text;
    use Zend\Form\Form;
    
    class MyForm extends Form
    {
        public function __construct($name = null, array $options = [])
        {
            parent::__construct($name, $options);
    
            $this->setAttribute('method', 'post');
    
            $this->add([
                'name' => 'input1',
                'type' => Text::class,
                'required' => true,
                'attributes' => [
                    'id' => 'input2',
                    'required' => true,
                ],
                'options' => [
                    'label' => 'The label for input2 of this fieldset',
                ],
            ]);
    
            // here goes your fieldset (provided, that your fieldset class is defined in the form elements config in your module.config.php file)
            $this->add([
                'name' => 'fieldset1',
                'type' => MyFieldset::class,
            ]);
        }
    }
    

    That 's all for your form. This form is implementing your fieldset. That 's all. Now we need a validator and an entity for this form.

    namespace Application\Entity;
    
    class MyFormEntity
    {
        protected $input1;
    
        // we will hydrate this property with the MyFieldsetEntity
        protected $fieldset1;
    
        public function getInput1()
        {
            return $this->input1;
        }
    
        public function setInput1($input1)
        {
            $this->input1 = $input1;
            return $this;
        }
    
        public function getFieldset1()
        {
            return $fieldset1;
        }
    
        public function setFieldset1($fieldset1)
        {
            $this->fieldset1 = $fieldset1;
            return $this;
        }
    }
    

    ... and finally the input filter class for your form. An input filter filters and validates your form data. You should use always an input filter for security reasons and many more.

    namespace Application\InputFilter;
    
    use Zend\InputFilter\InputFilter;
    use Zend\Filter\StripTags;
    use Zend\Filter\StringTrim;
    
    class MyFormInputFilter extends InputFilter
    {
        public function __construct()
        {
            $this->add([
                'name' => 'input1',
                'required' => true,
                'filters' => [
                    [
                        'name' => StripTags::class,
                    ],
                    [
                        'name' => StringTrim::class,
                    ],
                ],
            ]);
        }
    }
    

    Simple, hm? This input filter class just sets some input filters for your input 1 form element. The fieldset element is filtered by itself because it implements the InputFilterProviderInterface interface. You don 't hav to define more in the input filter class for your form.

    Put it together in a factory ...

    namespace Application\Form\Service;
    
    class MyFormFactory
    {
        public function __invoke(ContainerInterface $container)
        {
            $entity = new MyFormEntity();
            $inputFilter = new MyFormInputFilter();
            $hydrator = (new ClassMethods(false))
                ->addStrategy('fieldset1', new Fieldset1Strategy());
    
            $form = (new MyForm())
                ->setHydrator($hydrator)
                ->setObject($entity)
                ->setInputFilter($inputFilter);
    
            return $form;
        }
    }
    

    This is the factory for your form. This factory contains a special feature. It adds a hydrator strategy to your hydrator instance. this strategy will hydrate your entity with the fieldset data, if there is a 'fieldset1' key in your post array.

    This will be the hydrator strategy class ...

    namespace Application\Hydrator\Strategy;
    
    use Zend\Hydrator\Strategy\DefaultStrategy;
    use Zend\Hydrator\ClassMethods;
    
    use Application\Entity\MyFieldsetEntity;
    
    class Fieldset1Strategy extends DefaultStrategy
    {
        public function hydrate($value)
        {
            if (!$value instanceof MyFieldsetEntity) {
                return (new ClassMethods(false))->hydrate($value, new MyFieldsetEntity());
            }
    
            return $value;
        }
    }
    

    This strategy will add the MyFieldsetEntity to your form entity. The last step is defining all that stuff in the config file module.config.php

    // for the forms config provides the form elements key
    'form_elements' => [
        'factories' => [
            YourForm::class => YourFormFactory::class, 
            YourFormFieldset::class => YourFormFactory::class,
        ]
    ],
    
    // can be accessed with $container->get('FormElementsManager')->get(YourFormFieldset::class);
    

    Usage Example

    This is a small example how to use it in a controller.

    class ExampleController extends AbstractActionController
    {
        protected $form;
    
        public function __construct(Form $form)
        {
            $this->form = $form;
        }
    
        public function indexAction()
        {
            if ($this->getRequest()->isPost()) {
                $this->form->setData($this->getRequest()->getPost());
    
                if ($this->form->isValid()) {
                    $data = $this->form->getData();
    
                    \Zend\Debug\Debug::dump($data);
                    die();
    
                    // output will be
                    // MyFormEntity object
                    //     string input1
                    //     MyFieldsetEntity fieldset1
                    //         string input2
    
                    // for fetching the filtered data
                    // $data->getInput1();
                    // $data->getFieldset1()->getInput2();
                }
            }
    
            return [
                'form' => $this->form,
            ];
        }
    }
    

    In your view / template you can display the form with the different form view helpers zf2 provides.

    $form = $this->form;
    $form->setAttribute('action', $this->url('application/example'));
    $form->prepare();
    
    echo $this->form()->openTag($form);
    
    // outputs the single text input element
    echo $this->formRow($form->get('input1'));
    
    // outputs the complete fieldset
    echo $this->formCollection($form->get('fieldset1'));
    

    Sure, this answer is a bit complex. But I encurage you to have a try. Once implemented in your application, this kind of form management is the easiest way you can use. Keep in mind, that just handling the raw post data can be insecure as hell. If you want the filtered data with the benefit of objects it is recommended using entities, input filters and all the other cool stuff zend framework comes with.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 表达式必须是可修改的左值
  • ¥15 如何绘制动力学系统的相图
  • ¥15 对接wps接口实现获取元数据
  • ¥20 给自己本科IT专业毕业的妹m找个实习工作
  • ¥15 用友U8:向一个无法连接的网络尝试了一个套接字操作,如何解决?
  • ¥30 我的代码按理说完成了模型的搭建、训练、验证测试等工作(标签-网络|关键词-变化检测)
  • ¥50 mac mini外接显示器 画质字体模糊
  • ¥15 TLS1.2协议通信解密
  • ¥40 图书信息管理系统程序编写
  • ¥20 Qcustomplot缩小曲线形状问题