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

时间:2018-01-30 10:40:36

标签: php zend-framework zend-framework2 zend-form

我有Zend Framework 2表格:

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

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

和它的控制器:

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

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

问题是,当我转储价值时,我得到了这个:

  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

那我做错了什么?因为现在在“field2”键中我得到“nulll”。在这种情况下,我如何能够访问fieldset(s)数据(在过滤器,验证等之后)?

更新:正如我所看到的,当我添加到POST

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

我得到预期的结果。但为什么zendform不会像那样生成html,而是(错误地)生成:

    <input name="input2" />

我做错了什么?

3 个答案:

答案 0 :(得分:1)

这里有一个完整的或多或少的简单示例,其中包含实体,输入过滤器,水合器和验证器,用于与字段集一起使用。

首先设置您要使用的字段集类。

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,
                    ],
                ],
            ],
        ];
    }
}

您的fieldset类定义了fieldset中的所有输入元素。我鼓励你使用实体类和工厂。这也是此示例使用init方法的原因。在类的构造函数之后调用init方法。使用工厂时,您可以使用构造函数来定义字段集或表单类所需的内容。例如依赖输入字段等。

接下来,您应该为您的字段集编写一个实体。

namespace Application\Entity;

class MyFieldsetEntity
{
    protected $input2;

    public function getInput2()
    {
        return $this->input2;
    }

    public function setInput2($input2)
    {
        $this->input2 = $input2;
        return $this;
    }
}

这个简单的实体类将处理您发送给控制器的数据。实体类的一个好处是,您可以在其中定义默认值。如果由于某种原因后期数据应为空,则实体可以返回默认值。我们把它们放在一个工厂中,用于你的场地设置。

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);
    }
}

为什么使用智能工厂?因为您可以使用面向对象环境的所有好处。您可以在工厂中定义所需的所有东西。为此,我们创建了一个带有实体和水化器的fieldset实例。这将使用经过筛选和验证的数据来水化字段集。

我们现在需要的只是表单的表单和实体。

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,
        ]);
    }
}

这就是你的表格。此表单正在实现您的字段集。就这样。现在我们需要一个验证器和这个表单的实体。

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;
    }
}

...最后是表单的输入过滤器类。输入过滤器可过滤和验证表单数据。出于安全原因,您应该始终使用输入过滤器等等。

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,
                ],
            ],
        ]);
    }
}

简单,嗯?此输入过滤器类仅为输入1表单元素设置一些输入过滤器。 fieldset元素由其自身过滤,因为它实现了InputFilterProviderInterface接口。您无需在表单的输入过滤器类中定义更多内容。

将它放在工厂......

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;
    }
}

这是您表单的工厂。这家工厂包含一个特殊功能。它为您的保湿剂实例添加了保湿剂策略。如果你的帖子数组中有'fieldset1'键,这个策略会使用fieldset数据来保存你的实体。

这将是保湿策略类...

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;
    }
}

此策略会将MyFieldsetEntity添加到表单实体。 最后一步是在配置文件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);

使用示例

这是如何在控制器中使用它的一个小例子。

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,
        ];
    }
}

在您的视图/模板中,您可以使用zf2提供的不同表单视图帮助程序显示表单。

$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'));

当然,这个答案有点复杂。但我鼓励你试一试。一旦在您的应用程序中实现,这种表单管理是您可以使用的最简单方法。请记住,只是处理原始的帖子数据可能是不安全的。如果你希望过滤后的数据具有对象的好处,建议使用实体,输入过滤器以及zend框架附带的所有其他很酷的东西。

答案 1 :(得分:0)

您尚未将字段集添加到表单中。

$form->add($fieldset1);

答案 2 :(得分:0)

您忘记准备表格了。在$form->prepare()中,元素的名称已更改为包含字段集的前缀。

如果您使用“表单”视图帮助器,它将为您准备表单。如果不这样做,则必须自己输出“准备”它,例如在视图中,即在输出open标签之前

$this->form->prepare();