前一段时间我问了一个similar question,这归结为Forms,Fieldsets和InputFilters的结构。
我一直在彻底应用关注点分离的原则来分割来自InputFilters的Fieldsets,因为它们创建的模块也将用于API(基于Apigility),因此我只需要Entities和InputFilters。
但是,我现在有一个问题,当我有一个Fieldset使用的Fieldset,在Fieldset的Collection中使用时,最内层的Fieldset不会验证。
让我详细说明示例和代码!
情况是我希望能够创建Location
。 Location
由属性name
和OneToMany ArrayCollection|Address[]
关联组成。这是因为Location
可能有多个地址(例如访问者地址和递送地址)。
Address
包含一些属性(街道,数字,城市,Country
等)和OneToOne Coordinates
关联。
现在,Address
具有以下字段集:
class AddressFieldset extends AbstractFieldset
{
public function init()
{
parent::init();
// More properties, but you get the idea
$this->add([
'name' => 'street',
'required' => false,
'type' => Text::class,
'options' => [
'label' => _('Street'),
],
]);
$this->add([
'name' => 'country',
'required' => false,
'type' => ObjectSelect::class,
'options' => [
'object_manager' => $this->getEntityManager(),
'target_class' => Country::class,
'property' => 'id',
'is_method' => true,
'find_method' => [
'name' => 'getEnabledCountries',
],
'display_empty_item' => true,
'empty_item_label' => '---',
'label' => _('Country'),
'label_generator' => function ($targetEntity) {
return $targetEntity->getName();
},
],
]);
$this->add([
'type' => CoordinatesFieldset::class,
'required' => false,
'name' => 'coordinates',
'options' => [
'use_as_base_fieldset' => false,
],
]);
}
}
如您所见,必须输入Address
实体的详细信息,必须选择Country
并且可以{不需要Coordinates
。
使用下面的InputFilter验证以上内容。
class AddressFieldsetInputFilter extends AbstractFieldsetInputFilter
{
/** @var CoordinatesFieldsetInputFilter $coordinatesFieldsetInputFilter */
protected $coordinatesFieldsetInputFilter;
public function __construct(
CoordinatesFieldsetInputFilter $filter,
EntityManager $objectManager,
Translator $translator
) {
$this->coordinatesFieldsetInputFilter = $filter;
parent::__construct([
'object_manager' => $objectManager,
'object_repository' => $objectManager->getRepository(Address::class),
'translator' => $translator,
]);
}
/**
* Sets AddressFieldset Element validation
*/
public function init()
{
parent::init();
$this->add($this->coordinatesFieldsetInputFilter, 'coordinates');
$this->add([
'name' => 'street',
'required' => false,
'filters' => [
['name' => StringTrim::class],
['name' => StripTags::class],
],
'validators' => [
[
'name' => StringLength::class,
'options' => [
'min' => 3,
'max' => 255,
],
],
],
]);
$this->add([
'name' => 'country',
'required' => false,
]);
}
}
正如您所看到的,AddressFieldsetInputFilter
需要一些内容,其中一个是CoordinatesFieldsetInputFilter
。在init()
函数中,然后添加名称对应于Fieldset中给出的名称。
现在,以上所有工作,没问题。到处都有坐标的地址。太棒了。
当我们进一步进入另一个级别并且LocationFieldset
,如下所示,LocationFieldsetInputFilter
时出现问题。
class LocationFieldset extends AbstractFieldset
{
public function init()
{
parent::init();
$this->add([
'name' => 'name',
'required' => true,
'type' => Text::class,
'options' => [
'label' => _('Name'),
],
]);
$this->add([
'type' => Collection::class,
'name' => 'addresses',
'options' => [
'label' => _('Addresses'),
'count' => 1,
'allow_add' => true,
'allow_remove' => true,
'should_create_template' => true,
'target_element' => $this->getFormFactory()->getFormElementManager()->get(AddressFieldset::class),
],
]);
}
}
在下面的课程中,您可能会注意到一堆已注释掉的行,这些行是修改DI和/或设置InputFilter的不同尝试,以便它可以正常工作。
class LocationFieldsetInputFilter extends AbstractFieldsetInputFilter
{
/** @var AddressFieldsetInputFilter $addressFieldsetInputFilter */
protected $addressFieldsetInputFilter;
// /** @var CoordinatesFieldsetInputFilter $coordinatesFieldsetInputFilter */
// protected $coordinatesFieldsetInputFilter;
public function __construct(
AddressFieldsetInputFilter $filter,
// CoordinatesFieldsetInputFilter $coordinatesFieldsetInputFilter,
EntityManager $objectManager,
Translator $translator
) {
$this->addressFieldsetInputFilter = $filter;
// $this->coordinatesFieldsetInputFilter = $coordinatesFieldsetInputFilter;
parent::__construct([
'object_manager' => $objectManager,
'object_repository' => $objectManager->getRepository(Location::class),
'translator' => $translator,
]);
}
/**
* Sets LocationFieldset Element validation
*/
public function init()
{
parent::init();
$this->add($this->addressFieldsetInputFilter, 'addresses');
// $this->get('addresses')->add($this->coordinatesFieldsetInputFilter, 'coordinates');
$this->add([
'name' => 'name',
'required' => true,
'filters' => [
['name' => StringTrim::class],
['name' => StripTags::class],
],
'validators' => [
[
'name' => StringLength::class,
'options' => [
'min' => 3,
'max' => 255,
],
],
],
]);
}
}
您可能已经注意到LocationFieldset
和LocationFieldsetInputFilter
使用了现有的AddressFieldset
和`AddressFieldsetInputFilter。
看到它们是如何工作的,我无法弄清楚它为什么会出错。
但出了什么问题?
好吧,要创建Location
,似乎始终需要输入Coordinates
。如果您查看AddressFieldset
(位于顶部),您会发现'required' => false,
,所以这没有任何意义。
但是,当我在输入中输入值时,它们不会被验证。调试时,我进入\Zend\InputFilter\BaseInputFilter
第262行专门验证输入的地方,我注意到它在验证过程中丢失了数据。
我已经在开始时和验证期间确认了数据的存在,直到它尝试验证Coordinates
实体,它似乎失去了它(没有找到原因)。
如果有人能指出我正确的方向来清理它,那将非常感谢你的帮助。现在已经花了太多时间打击这个问题。
修改
添加视图部分代码以显示打印方法,以防应该/可以提供帮助:
地址form.phtml
<?php
/** @var \Address\Form\AddressForm $form */
$form->prepare();
echo $this->form()->openTag($form);
echo $this->formRow($form->get('csrf'));
echo $this->formRow($form->get('address')->get('id'));
echo $this->formRow($form->get('address')->get('street'));
echo $this->formRow($form->get('address')->get('city'));
echo $this->formRow($form->get('address')->get('country'));
echo $this->formCollection($form->get('address')->get('coordinates'));
echo $this->formRow($form->get('submit'));
echo $this->form()->closeTag($form);
位置-form.phtml
<?php
/** @var \Location\Form\LocationForm $form */
$form->prepare();
echo $this->form()->openTag($form);
echo $this->formRow($form->get('csrf'));
echo $this->formRow($form->get('location')->get('id'));
echo $this->formRow($form->get('location')->get('name'));
//echo $this->formCollection($form->get('location')->get('addresses'));
$addresses = $form->get('location')->get('addresses');
foreach ($addresses as $address) {
echo $this->formCollection($address);
}
echo $this->formRow($form->get('submit'));
echo $this->form()->closeTag($form);
答案 0 :(得分:0)
经过另一天的调试(和咒骂),我找到了答案!
This SO question通过指向Zend CollectionInputFilter
帮助我。
由于AddressFieldset
已添加到LocationFieldset
中的Collection
,因此必须使用CollectionInputFilter
对InputFilter
进行验证,该Fieldset
具有LocationFieldsetInputFilter
的{{1}} {1}}已指定。
要修复我的应用,我必须同时修改LocationFieldsetInputFilterFactory
和LocationFieldsetInputFilterFactory.php
。在更新的代码下方,注释中包含旧代码。
<强> class LocationFieldsetInputFilterFactory extends AbstractFieldsetInputFilterFactory
{
/**
* @param ServiceLocatorInterface|ControllerManager $serviceLocator
* @return InputFilter
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
parent::setupRequirements($serviceLocator, Location::class);
/** @var AddressFieldsetInputFilter $addressFieldsetInputFilter */
$addressFieldsetInputFilter = $this->getServiceManager()->get('InputFilterManager')
->get(AddressFieldsetInputFilter::class);
$collectionInputFilter = new CollectionInputFilter();
$collectionInputFilter->setInputFilter($addressFieldsetInputFilter); // Make sure to add the FieldsetInputFilter that is to be used for the Entities!
return new LocationFieldsetInputFilter(
$collectionInputFilter, // New
// $addressFieldsetInputFilter, // Removed
$this->getEntityManager(),
$this->getTranslator()
);
}
}
强>
LocationFieldsetInputFilter.php
<强> class LocationFieldsetInputFilter extends AbstractFieldsetInputFilter
{
// Removed
// /** @var AddressFieldsetInputFilter $addressFieldsetInputFilter */
// protected $addressFieldsetInputFilter ;
// New
/** @var CollectionInputFilter $addressCollectionInputFilter */
protected $addressCollectionInputFilter;
public function __construct(
CollectionInputFilter $addressCollectionInputFilter, // New
// AddressFieldsetInputFilter $filter, // Removed
EntityManager $objectManager,
Translator $translator
) {
// $this->addressFieldsetInputFilter = $filter; // Removed
$this->addressCollectionInputFilter = $addressCollectionInputFilter; // New
parent::__construct([
'object_manager' => $objectManager,
'object_repository' => $objectManager->getRepository(Location::class),
'translator' => $translator,
]);
}
/**
* Sets LocationFieldset Element validation
*/
public function init()
{
parent::init();
// $this->add($this->addressFieldsetInputFilter, 'addresses'); // Removed
$this->add($this->addressCollectionInputFilter, 'addresses'); // New
$this->add([
'name' => 'name',
'required' => true,
'filters' => [
['name' => StringTrim::class],
['name' => StripTags::class],
],
'validators' => [
[
'name' => StringLength::class,
'options' => [
'min' => 3,
'max' => 255,
],
],
],
]);
}
}
强>
AddressFieldsetInputFilter
这种方法的工作方式是,在验证数据期间,它会将单数element
应用于从客户端收到的每个“Collection
”。因为来自客户端的R
可能是这些元素中的0个或更多(因为添加/删除它们是使用JavaScript完成的)。
现在我已经明白了,它确实非常有意义。