没有绑定对象和访问字段集数据的 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();
我有 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();