使用来自控制器的参数来操作 zend 表单是个坏主意吗?
Is it a bad idea to manipulate zend forms with parameters from the controller?
我目前正在为我的项目创建表单,并且已经在字段集上浪费了时间。首先我不得不发现你不能在字段集中定义过滤器和验证器,然后在无数次尝试在表单本身中修复它之后我放弃了它。在混合了 fieldset 表单元素和表单元素之后,我认为它对于我拥有的简单表单来说太复杂了。
所以现在我想创建一个通用表单 class 并将参数从控制器传递给它,以便只创建我需要的元素。
这是一个坏主意吗?如果是,为什么?
像这样:
控制器:
$include_title = true
$screening_form->setData($include_title);
表格
public function __construct($include_title)
{
if ($include_title)
{
$this->add([
'type' => 'text',
'name' => 'title',
'options' => [
'label' => 'text',
'placeholder' => 'Test',
],
]);
}
}
我建议为不同的上下文保留不同的表单,而不是为您的整个应用程序准备一个大表单 class。根据通过 setData() 传入的数据更改表单外观非常困难,可能是导致您的字段集和验证出现问题的原因
如果你想要 "generic" 可以适应不同上下文的表单 - 这是可能的,我会像这样实现它:
<?php
namespace ConferenceTools\Attendance\Form;
use Zend\Form\Element\DateTime;
use Zend\Form\Element\Submit;
use Zend\Form\Form;
class DateTimeForm extends Form
{
public function init()
{
$this->add([
'type' => DateTime::class,
'name' => 'datetime',
'options' => [
'label' => $this->getOption('fieldLabel'),
],
'attributes' => [
'class'=> 'datetimepicker-input',
'id' => "dtpicker",
'data-toggle' => "datetimepicker",
'data-target' => "#dtpicker",
'autocomplete' => 'off',
],
]);
}
}
这是此项目中使用的通用日期时间格式:https://github.com/conferencetools/attendance-module/blob/master/src/Form/DateTimeForm.php
与您的表单的不同之处在于 $this->getOption('fieldLabel')
调用,这是将选项传递到表单中比使用构造函数更好的方法。它还在 init 方法而不是构造函数中构建表单,这对于使用选项字段和任何自定义表单元素很重要,这些元素在 class 完全构建之前不可用。
为了使其正常工作,您需要从标准服务定位器 FormPluginManager(服务名称:'Zend\Form\FormElementManager')中检索它。您可以将它注入到您的工厂控制器中,然后按如下方式使用它:
// in controller method
$form = $this->formElementManager->get(FormName::class, ['fieldLabel' => 'Arrival time']);
这不是我在上面链接的项目中的做法;我没有将 formElementManager 注入每个需要它的控制器,而是创建了一个控制器插件,使其在每个控制器中都可用。 (代码是一样的,只是用一个插件代替)
虽然 user999305 是正确的(并且应该被标记为解决方案),但看到您的评论和理解中的混淆,我想为您提供他所说内容的扩展示例。
假设你有以下情况:
- 模块:
FormModule
- 控制器:
FormController
- 表格:
ComposedForm
在控制器中,您有两个操作,withTitleAction
和 withoutTitleAction
。两者都使用 ComposedForm
。由于您的 FormController
中需要 ComposedForm
,因此您需要使用 FormPluginManager
实例化它(您可以阅读更多相关信息 in the docs)
关于如何在控制器中获取表单,现在有两种选择:
- 通过controller plugin获取(正如user999305最后提出的,其实是个聪明的主意)
- 你在控制器中注入
FormPluginManager
在控制器
中注入FormPluginManager
首先,您必须为控制器创建一个工厂:
Controller/FormControllerFactory.php
namespace FormModule\Controller;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
class FormControllerFactory implements FactoryInterface {
public function __invoke(ContainerInterface $container, $requestedName, array $options = null) {
$formPluginManager = $container->get('FormElementManager');
return new FormController($formPluginManager);
}
}
必须更新模块的配置,以便应用程序知道如何创建控制器
module.config.php
'controllers' => [
'factories' => [
Controller\FormController::class => Controller\FormControllerFactory::class
]
],
现在,只需更新控制器即可接收新参数:
Controller/FormController.php
namespace FormModule\Controller;
use Zend\Form\FormElementManager;
use Zend\Mvc\Controller\AbstractActionController;
class FormController extends AbstractActionController {
private $formManager;
public function __construct(FormElementManager $formManager) {
$this->formManager = $formManager;
}
}
现在,执行两个操作:
Controller/FormController.php
public function withTitleAction(){
$options = ['hasTitle' => true];
$form = $this->formManager->get(\FormModule\Form\ComposedForm::class, $options);
}
public function withoutTitleAction(){
$options = ['hasTitle' => false];
$form = $this->formManager->get(\FormModule\Form\ComposedForm::class, $options);
}
最后,你的表格:
Form/ComposedForm.php
namespace FormModule\Form;
use Zend\Form\Form;
use Zend\InputFilter\InputFilterProviderInterface;
class ComposedForm extends Form {
public function __construct($name = null, $options = []) {
// DO NOT FORGET THIS
parent::__construct($name, $options);
// The constructor is not really needed, unless you have some
// dependencies or you have to do something particular, like
// setting the hydrator, form attributes, form object, ...
}
public function init() {
parent::init();
if ($this->getOption('hasTitle')) {
$this->add([
'type' => 'text',
'name' => 'title',
'options' => [
'label' => 'text',
'placeholder' => 'Test',
],
]);
}
}
}
希望对您有所帮助
我目前正在为我的项目创建表单,并且已经在字段集上浪费了时间。首先我不得不发现你不能在字段集中定义过滤器和验证器,然后在无数次尝试在表单本身中修复它之后我放弃了它。在混合了 fieldset 表单元素和表单元素之后,我认为它对于我拥有的简单表单来说太复杂了。
所以现在我想创建一个通用表单 class 并将参数从控制器传递给它,以便只创建我需要的元素。 这是一个坏主意吗?如果是,为什么?
像这样:
控制器:
$include_title = true
$screening_form->setData($include_title);
表格
public function __construct($include_title)
{
if ($include_title)
{
$this->add([
'type' => 'text',
'name' => 'title',
'options' => [
'label' => 'text',
'placeholder' => 'Test',
],
]);
}
}
我建议为不同的上下文保留不同的表单,而不是为您的整个应用程序准备一个大表单 class。根据通过 setData() 传入的数据更改表单外观非常困难,可能是导致您的字段集和验证出现问题的原因
如果你想要 "generic" 可以适应不同上下文的表单 - 这是可能的,我会像这样实现它:
<?php
namespace ConferenceTools\Attendance\Form;
use Zend\Form\Element\DateTime;
use Zend\Form\Element\Submit;
use Zend\Form\Form;
class DateTimeForm extends Form
{
public function init()
{
$this->add([
'type' => DateTime::class,
'name' => 'datetime',
'options' => [
'label' => $this->getOption('fieldLabel'),
],
'attributes' => [
'class'=> 'datetimepicker-input',
'id' => "dtpicker",
'data-toggle' => "datetimepicker",
'data-target' => "#dtpicker",
'autocomplete' => 'off',
],
]);
}
}
这是此项目中使用的通用日期时间格式:https://github.com/conferencetools/attendance-module/blob/master/src/Form/DateTimeForm.php
与您的表单的不同之处在于 $this->getOption('fieldLabel')
调用,这是将选项传递到表单中比使用构造函数更好的方法。它还在 init 方法而不是构造函数中构建表单,这对于使用选项字段和任何自定义表单元素很重要,这些元素在 class 完全构建之前不可用。
为了使其正常工作,您需要从标准服务定位器 FormPluginManager(服务名称:'Zend\Form\FormElementManager')中检索它。您可以将它注入到您的工厂控制器中,然后按如下方式使用它:
// in controller method
$form = $this->formElementManager->get(FormName::class, ['fieldLabel' => 'Arrival time']);
这不是我在上面链接的项目中的做法;我没有将 formElementManager 注入每个需要它的控制器,而是创建了一个控制器插件,使其在每个控制器中都可用。 (代码是一样的,只是用一个插件代替)
虽然 user999305 是正确的(并且应该被标记为解决方案),但看到您的评论和理解中的混淆,我想为您提供他所说内容的扩展示例。
假设你有以下情况:
- 模块:
FormModule
- 控制器:
FormController
- 表格:
ComposedForm
在控制器中,您有两个操作,withTitleAction
和 withoutTitleAction
。两者都使用 ComposedForm
。由于您的 FormController
中需要 ComposedForm
,因此您需要使用 FormPluginManager
实例化它(您可以阅读更多相关信息 in the docs)
关于如何在控制器中获取表单,现在有两种选择:
- 通过controller plugin获取(正如user999305最后提出的,其实是个聪明的主意)
- 你在控制器中注入
FormPluginManager
在控制器
中注入FormPluginManager
首先,您必须为控制器创建一个工厂:
Controller/FormControllerFactory.php
namespace FormModule\Controller;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
class FormControllerFactory implements FactoryInterface {
public function __invoke(ContainerInterface $container, $requestedName, array $options = null) {
$formPluginManager = $container->get('FormElementManager');
return new FormController($formPluginManager);
}
}
必须更新模块的配置,以便应用程序知道如何创建控制器
module.config.php
'controllers' => [
'factories' => [
Controller\FormController::class => Controller\FormControllerFactory::class
]
],
现在,只需更新控制器即可接收新参数:
Controller/FormController.php
namespace FormModule\Controller;
use Zend\Form\FormElementManager;
use Zend\Mvc\Controller\AbstractActionController;
class FormController extends AbstractActionController {
private $formManager;
public function __construct(FormElementManager $formManager) {
$this->formManager = $formManager;
}
}
现在,执行两个操作:
Controller/FormController.php
public function withTitleAction(){
$options = ['hasTitle' => true];
$form = $this->formManager->get(\FormModule\Form\ComposedForm::class, $options);
}
public function withoutTitleAction(){
$options = ['hasTitle' => false];
$form = $this->formManager->get(\FormModule\Form\ComposedForm::class, $options);
}
最后,你的表格:
Form/ComposedForm.php
namespace FormModule\Form;
use Zend\Form\Form;
use Zend\InputFilter\InputFilterProviderInterface;
class ComposedForm extends Form {
public function __construct($name = null, $options = []) {
// DO NOT FORGET THIS
parent::__construct($name, $options);
// The constructor is not really needed, unless you have some
// dependencies or you have to do something particular, like
// setting the hydrator, form attributes, form object, ...
}
public function init() {
parent::init();
if ($this->getOption('hasTitle')) {
$this->add([
'type' => 'text',
'name' => 'title',
'options' => [
'label' => 'text',
'placeholder' => 'Test',
],
]);
}
}
}
希望对您有所帮助