我想为一个实体创建一个表单,该表单包含同一实体的两个集合,但每个集合都包含一个属性中具有特定值的实体。
我的数据库表如下所示:
Table 1: base
| id | foo | bar |
|----|-----|-----|
Table 2: extended
| id | table_1_id | type | baz |
|----|------------|------|-----|
表 1 和表 2 处于 1:n 关系中,因此"基本"实体可以容纳任意数量的"扩展"实体。
我已经定义了实体:
class Base {
private $id;
private $foo;
private $baz;
private $extendedCollection;
/*...*/
public function getTypeA() {
$result = new ArrayCollection();
foreach ($extendedCollection->toArray() as $item) {
if ($item->getType() == "A") {
$result->add($item);
}
}
return $result;
}
/* respective for type "B" */
public function addExtendedA(Extended $a) {
if (!$this->extendedCollection->contains($a)) {
$a
->setBase($this)
->setType("A");
$this->extendedCollection->add($a);
}
return $this;
}
/* respective for type "B" */
}
class Extended {
private $id;
private $base;
private $type;
private $baz;
/*...*/
}
最后,我还创建了两个表单类:
class BaseType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('foo')
->add('bar')
->add('typeA', CollectionType::class, array(
'entry_type' => ExtendedType::class,
'entry_options' => array(
'type' => "A"
),
'allow_add' => true,
'allow_delete' => true,
'required' => false,
'by_reference' => false
)
->add('typeB', CollectionType::class, array(
'entry_type' => ExtendedType::class,
'entry_options' => array(
'type' => "B"
),
'allow_add' => true,
'allow_delete' => true,
'required' => false,
'by_reference' => false
);
/*...*/
}
}
class ExtendedType extends AbstractType {
private $type;
public function buildForm(FormBuilderInterface $builder, array $options) {
$this->type = $options['type'];`enter code here`
$builder
->add('type', HiddenType::class, array(
'data' => $this->type
)
->add('baz');
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => Extended::class,
'type' => ""
));
}
}
预期结果如下: 当请求"Base"实体时,数据库内容将解析为实体,并且"扩展"实体的 ArrayCollection 将按集合项的类型分类为两个集合"A"和"B",并且这两个集合都呈现为单独的表单列表。这行得通。
更新"基"实体时,应将两个"扩展"集合融合到新的扩展集合,并且应保留该实体。这行不通。
如果我手动向数据库添加一些扩展行,我可以呈现模板并显示所有内容 - 但是,当我尝试通过 HTML 表单应用一些更改时,会抛出一个NoSuchPropertyException
,其中消息显示Could not determine access type for "typeA"
。我到底错过了什么?我可以做些什么来优化这个模型吗?
更新14.09.2017 我添加了一个 setter 函数来应用新的"扩展"集合:
class Base {
/* see above for reference code */
public function setTypeACollection(ArrayCollection $typeAExtended)
{
$this->setExtendedCollection($typeAExtended, "A");
return $this;
}
/* Respective for type "B" */
private function setExtendedCollection(ArrayCollection $newExtended, $type)
{
$newExtendedCollection = new ArrayCollection();
$extendedArray = $this->extendedCollection->toArray();
foreach ($extendedArray as $k => $v) {
if ($v->getType() == $type) {
unset($extendedArray[$k]);
} else {
$newExtendedCollection->add($v);
}
}
foreach ($newExtended->toArray() as $newExt) {
$newExt->setType($type);
$newExtendedCollection->add($item);
}
$this->extendedCollection = $newExtendedCollection;
}
}
现在,NoSuchPropertyException
消失了,但是一个新问题仍然存在: 当我从数据库加载 Base 实体时,扩展集合应用干净,但通过 HTML 表单更新实体会导致以下错误,具体取决于是否有更改:
- 无更改:实体以错误的方式更新,因为所有类型"B"都转换为类型"A",因此都显示在类型"A"集合中。 删除类型"B">
- :删除的类型"B"将变为类型"A",而不是被删除。 删除类型"A">
- :删除的延长住宿以及所有类型"B"将变为类型"A"。 添加类型"A">
- :添加的扩展不会持久化,此外,所有类型"B"都转换为类型"A"。
- 添加类型"B":抛出
ORMInvalidArgumentException
,消息A new entity was found through the relationship 'AcmeBundleEntityBase#extendedCollection' that was not configured to cascade persist operations for entity: AcmeBundleEntityExtended@0000000011153bff000000006bb39f24. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example @ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'AcmeBundleEntityExtended#__toString()' to get a clue.
我的Extended
类包含引用$base
属性处的注释@ORMManyToOne(targetEntity="AcmeBundleEntityBase", inversedBy="extendedCollection", cascade={"persist"})
。
表单字段是从表单的基础对象/类映射的,因此当表单组件尝试将提交的值映射到此属性时,Could not determine access type for "typeA"
引用Base
类中丢失的属性typeA
。
尝试在Base
类中添加setTypeA(Collection $items)
方法来解决它,并根据type
手动更新extendedCollection
属性。