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

Zend Form without binding object and access to fieldset data

我有 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"。在这种情况下,我如何才能访问字段集数据(在过滤、验证等之后)?

更新:如我所见,当我添加到 POST

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

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

    <input name="input2" />

我做错了什么?

您还没有将字段集添加到表单中。

$form->add($fieldset1);

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

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

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

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

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

namespace Application\Entity;

class MyFieldsetEntity
{
    protected $input2;

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

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

这个简单的实体 class 将处理您发送给控制器的数据。实体 class 的好处之一是,您可以在其中定义默认值。如果 post 数据由于某种原因应该为空,实体可以 return 默认值。让我们为您的字段集将它们全部放在一个工厂中。

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

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

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

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

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

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

很简单,嗯?此输入过滤器 class 只是为您的 input 1 表单元素设置了一些输入过滤器。 fieldset 元素被自身过滤,因为它实现了 InputFilterProviderInterface 接口。您不必在表单的输入过滤器 class 中定义更多内容。

在工厂里组装...

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

这是您的表单的工厂。该工厂包含一个特殊功能。它为您的水化器实例添加了水化器策略。如果您的 post 数组中有 'fieldset1' 键,此策略将使用字段集数据滋润您的实体。

这将是水化器策略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;
    }
}

此策略会将 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'));

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

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

如果您使用 "form" 视图助手,它会为您准备表单。如果不这样做,则必须自己调用 "prepare",例如在视图中,就在输出打开标记之前:

$this->form->prepare();