我已经在ZF2中阅读了一些关于表单集合/字段集的内容,因为我想在表单中添加一些数组字段。 但是据我所知 - fieldsets / collections是在考虑域实体的情况下构建的。在我的情况下,这个解决方案似乎并没有接近好主意。
我的表单与任何实体无关,它只是将params传递给pdf生成器。这些参数被序列化并保存在db。中的一列中。
表单本身很大,有很多字段,如:
Question:
a) smth
b) smth
c) smth
d) other
a) other 1
b) other 2
...
x) other 22
因此,我有多个元素可供用户额外输入,必须动态添加。
为包含... 1个额外字段的字段集构建一个单独的类,每个字段将导致至少20个额外的类。
我以为我可以简单地通过:
$element = new Element\Text('citiesOther[]');
$element->setAttributes(array(
'id' => 'citiesOther',
'placeholder' => 'Other city',
));
$this->add($element);
//view:
$this->formElement( $form->get( 'citiesOther[]' )
然后在前端有一个按钮"添加另一个"这只是克隆整个输入。它工作得很好,我可以从帖子中收到参数。
事情是......我无法过滤/验证这些字段。如果我传了一个名字" citiesOther []"对于inputFilter,它甚至没有验证其中一个字段,甚至没有提到整个数组。
答案 0 :(得分:2)
Zend Framework中的集合有点难以理解。基本上,您需要一个集合的基本字段集。此字段集实现InputFilterProviderInterface以进行过滤和验证。考虑到这一点,您必须定义所有可重复输入字段以及集合中此字段的验证。不验证此数据不是一个好主意。在pdf中注入非常糟糕的数据有几种情况。因此,请验证来自表单的所有数据。总是!
以下是Zend Framework集合的快速示例。
namespace Application\Form;
use Zend\Form\Fieldset;
use Zend\Form\Element\Text;
use Zend\InputFilter\InputFilterProviderInterface;
class YourCollection extends Fieldset implements InputFilterProviderInterface
{
public function init()
{
$this->add([
'name' => 'simple',
'type' => Text::class,
'attributes' => [
...
],
'options' => [
...
]
]);
}
public function getInputFilterSpecification()
{
return [
'simple' => [
'required' => true,
'filters' => [
...
],
'validators' => [
...
],
],
];
}
}
这是基本的fieldset类,它充当您希望在表单中重复的所有输入字段的集合。此基本字段集通过实现InputFilterProviderInterface来过滤和验证自身。通过使用实现的方法,您可以为输入放置过滤器和验证器 如果您有依赖输入字段,则必须在此字段集中定义它们。
要正确使用它,您需要一个实体,该实体将在本文后面的基本字段集中绑定。接下来,我们为此字段集创建实体。
namespace Application\Entity;
class YourCollectionEntity
{
protected $simple;
public function getSimple()
{
return $this->simple;
}
public function setSimple($simple)
{
$this->simple = $simple;
return $this;
}
}
这是一个非常简单的实体。这将作为您的数据持有者,并将在以后绑定到该集合。接下来我们需要一个fieldset,它包含集合和集合计数的隐藏字段。使用另一个字段集听起来有点复杂。但在我看来,这是最好的方式。
namespace Application\Form;
class YourCollectionFieldset extends Fieldset implements InputFilterProviderInterface
{
public function init()
{
$this->add([
'name' => 'collectionCounter',
'type' => Hidden::class,
'attributes' => [
...
],
]);
// add the collection fieldset here
$this->add([
'name' => 'yourCollection',
'type' => Collection::class,
'options' => [
'count' => 1, // initial count
'should_create_template' => true,
'template_placeholder' => '__index__',
'allow_add' => true,
'allow_remove' => true,
'target_element' => [
'type' => YourCollection::class,
],
],
]);
}
public function getInputFilterSpecification()
{
return [
'collectionCounter' => [
'required' => true,
'filters' => [
[
'name' => ToInt::class,
],
],
],
];
}
}
此字段集实现您的集合和隐藏字段,该字段充当您的集合的计数器。当您想要处理集合时,您需要两者。案例可能是编辑收集内容。然后你必须设置集合的数量。
您可以想象,您也需要此字段集的实体类。我们来写一个。
namespace YourCollectionFieldsetEntity
{
protected $collectionCounter;
protected $yourCollection;
public function getCollectionCounter()
{
return $this->collectionCounter;
}
public function setCollectionCounter($collectionCounter)
{
$this->collectionCounter = $collectionCounter;
return $this;
}
public function getYourCollection()
{
return $this->yourCollection;
}
public function setYourCollection(YourCollectionEntity $yourCollection)
{
$this->yourCollection = $yourCollection;
return $this;
}
}
此实体包含setYourCollection方法,该方法将YourCollectionEntity实例作为参数。我们稍后会看到,我们将如何做那个小怪物。
我们把它包装在工厂里。
namespace Application\Form\Service;
YourCollectionFactory
{
public function __invoke(ContainerInterface $container)
{
$entity = new YourCollectionFieldsetEntity();
$hydrator = (new ClassMethods(false))
->addStrategy('yourCollection', new YourCollectionStrategy());
$fieldset = (new YourCollectionFieldset())
->setHydrator($hydrator)
->setObject($entity);
return $fieldset;
}
}
该工厂为保湿剂添加了保湿策略。这将水合绑定到可重复字段集的实体。
如何在表单中使用它?
以上显示的类仅适用于所需的集合。该集合本身尚未形成。我们假设我们有一个简单的形式,它实现了这个集合。
namespace Application\Form;
class YourForm extends Form
{
public function __construct($name = null, array $options = [])
{
parent::__construct($name, $options);
// add a few more input fields here
// add your collection fieldset
$this->add([
'name' => 'fancy_collection',
'type' => YourCollectionFieldset::class,
]);
// add other simple input fields here
}
}
嗯,这是一个简单的表单类,它使用上面显示的集合。正如您所写,您需要一个验证器来验证此表单包含的所有数据。没有实体的形式验证器。首先,我们需要一个这种形式的实体类。
namespace Application\Entity;
class YourFormEntity
{
// possible other form members
...
protected $fancyCollection;
public function getFancyCollection()
{
return $this->fancyCollection;
}
public function setFancyCollection(YourCollectionFieldsetEntity $fancyCollection)
{
$this->fancyCollection = $fancyCollection;
return $this;
}
// other getters and setters for possible other form fields
}
...最后是表单的验证器类。
namespace Application\InputFilter;
class YourFormInputFilter extends InputFilter
{
// add other definitions for other form fields here
...
}
}
我们无需在此处为您的收藏重新定义过滤器和验证器。请记住,当执行包含集合的表单的验证器时,集合本身实现了自动执行的InputFilterProviderInterface。
让我们把它包装在工厂里
namespace Application\Form\Service;
class YourFormFactory
{
public function __invoke(ContainerInterface $container)
{
$container = $container->getServiceLocator();
$entity = new YourFormEntity();
$filter = new YourFormInputFilter();
$hydrator = (new ClassMethods(false))
->addStrategy('fancy_collection', new FancyCollectionStrategy());
$form = (new YourForm())
->setHydrator($hydrator)
->setObject($entity)
->setInputFilter($filter);
return $form;
}
}
就是这样。这是包含您的收藏的表单。
保湿策略是你的朋友
以上两种保湿剂策略被添加到您的表格所需的保湿剂中。如果你有复杂的后期数据,那么在水化器中添加水化器策略的工作就像魅力一样,这是一种很好的结果。
namespace Application\Hydrator\Strategy;
class YourCollectionStrategy extends DefaultStrategy
{
public function hydrate($value)
{
$entities = [];
if (is_array($value)) {
foreach ($value as $key => $data) {
$entities[] = (new ClassMethods())->hydrate($data, new YourCollectionEntity());
}
}
return $entities;
}
}
这种保湿策略将为重复收集字段集四个实体提供水合作用。下一个策略将整个集合数据融合到您的表单实体中。
namespace Application\Hydrator\Strategy;
class FancyCollectionStrategy extends DefaultStrategy
{
public function hydrate($value)
{
return (new ClassMethods())
->addStrategy('yourCollection', new YourCollectionFieldsetEntity())
->hydrate($value);
}
}
这个将水合收集计数和重复的字段集。这就是数据水化的全部内容。
在控制器中看起来如何?
嗯,这很简单。现在,因为我们拥有了一个带有集合的复杂形式所需的所有类,所以我们可以继续使用控制器。
namespace Application\Controller;
class YourController extends AbstractActionController
{
protected $form;
public function __consturct(Form $form)
{
$this->form = $form;
}
public function indexAction()
{
$request = $this->getRequest();
if ($request->isPost()) {
$this->form->setData($oRequest->getPost());
if ($this->form->isValid()) {
// get all data as entity
$data = $this->form->getData();
// iterate through all collection data
foreach ($data->getFancyCollection()->getYourCollection() as $collection) {
// get every simple field from every collection
echo $collection->getSimple();
}
}
}
}
}
当然,这比复制原始帖子数据更复杂。但如前所述,由于安全原因,您不应使用原始数据。始终验证和过滤用户在表单上提供的数据。或者只是为了保持简单:不要相信用户!