Symfony表单集合-维护与主键的关联



我有一个Symfony表单,其中包含一个集合,定义如下:

<?php declare(strict_types=1);
namespace AppForm;
use AppEntityDocuments;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormExtensionCoreTypeCollectionType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolver;
class DocumentsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(
'documents',
CollectionType::class,
[
'entry_type' => DocumentType::class,
'by_reference' => false,
'entry_options' => [
'label' => false,
],
'allow_add' => true,
'allow_delete' => true,
'delete_empty' => true,
'attr' => [
'class' => 'documents-collection',
'data-min-items' => 1,
],
'required' => true,
]
);
parent::buildForm($builder, $options);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'data_class' => Documents::class,
]
);
}
}

文档类型如下:

<?php declare(strict_types=1);
namespace AppForm;
use AppEntityDocument;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormExtensionCoreTypeFileType;
use SymfonyComponentFormExtensionCoreTypeHiddenType;
use SymfonyComponentFormExtensionCoreTypeTextType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolver;
class DocumentType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'description',
TextType::class,
[
'required' => true,
'attr' => [
'placeholder' => 'Document description, eg: Ticket, receipt, itinerary, map, etc…',
],
]
)
->add(
'document',
FileType::class,
[
'mapped' => false,
'required' => true,
]
);
parent::buildForm($builder, $options);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'data_class' => Document::class,
]
);
}
}

文件实体为:

<?php declare(strict_types=1);
namespace AppEntity;
use AppServiceUuid;
use DoctrineCommonCollectionsArrayCollection;
use DoctrineCommonCollectionsCollection;
use DoctrineORMMapping as ORM;
/**
* @ORMEntity
*/
class Documents
{
/**
* @ORMColumn(type="uuid")
* @ORMGeneratedValue(strategy="UUID")
* @ORMId
*/
private $id;
/**
* @ORMManyToMany(
*     targetEntity="Document",
*     cascade={"persist", "remove"},
*     orphanRemoval=true
* )
* @ORMJoinTable(
*     name="documents_document",
*     joinColumns={
*         @ORMJoinColumn(name="documents_id", referencedColumnName="id"),
*     },
*     inverseJoinColumns={
*         @ORMJoinColumn(name="document_id", referencedColumnName="id", unique=true),
*     }
* )
* @var Document[]
*/
private $documents;

public function __construct()
{
$this->id = Uuid::uuid4();
$this->documents = new ArrayCollection();
}

/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @return Collection
*/
public function getDocuments(): Collection
{
return $this->documents;
}
/**
* @param Document $document
*
* @return $this
*/
public function addDocument(Document $document): Documents
{
if (!$this->documents->contains($document)) {
$this->documents->add($document);
$document->setDocuments($this);
}
return $this;
}
/**
* @param Document $document
*
* @return bool
*/
public function hasDocument(Document $document): bool
{
return $this->documents->contains($document);
}
/**
* @param Document $document
*
* @return $this
*/
public function removeDocument(Document $document): Documents
{
if ($this->documents->contains($document)) {
$this->documents->removeElement($document);
}
return $this;
}
/**
* @param Collection $documents
*
* @return $this
*/
public function setDocuments(Collection $documents): Documents
{
$this->documents = $documents;
return $this;
}
/**
* @return $this
*/
public function clearDocuments(): Documents
{
$this->documents = new ArrayCollection();
return $this;
}
}

文件实体为:

<?php declare(strict_types=1);
namespace AppEntity;
use AppServiceUuid;
use DoctrineCommonCollectionsArrayCollection;
use DoctrineCommonCollectionsCollection;
use DoctrineORMMapping as ORM;
/**
* @ORMEntity
*/
class Document
{
/**
* @var Uuid|string
* @ORMColumn(type="uuid")
* @ORMGeneratedValue(strategy="UUID")
* @ORMId
*/
private $id;
/**
* @var Documents
* @ORMManyToOne(targetEntity="Documents")
*/
private $documents;
/**
* @var string
* @ORMColumn(type="string", length=1024, nullable=false)
*/
private $description;

public function __construct()
{
$this->id = Uuid::uuid4();
}

/**
* @return Uuid|string
*/
public function getId()
{
return $this->id;
}
/**
* @return Documents
*/
public function getDocuments(): Documents
{
return $this->documents;
}
/**
* @param Documents $documents
*
* @return $this
*/
public function setDocuments(Documents $documents): Document
{
$this->documents = $documents;
return $this;
}
/**
* @return string
*/
public function getDescription(): ?string
{
return $this->description;
}
/**
* @param string $description
*
* @return $this
*/
public function setDescription(string $description): Document
{
$this->description = $description;
return $this;
}
}

我在我的控制器中创建这样的表单:

$repo = $entityManager->getRepository(Documents::class);
$documents = $repo->findOneBy(['id' => $id]);
$form = $this->formFactory->create(
DocumentsType::class,
$documents
);

当我在呈现的表单中将新的Document条目添加到集合中,然后保存表单时,它们将正确地持久化到数据库中并链接到Documents实体。

如果我删除集合中的最后一个条目,它将正确地从$documents集合中删除,然后从documents表中删除,因为不再有任何对它的引用。

然而,如果我删除集合中间的一个条目,Doctrine会将剩余条目中的数据保存在已删除条目及其追随者上,然后删除列表中的最后一个实体,从而更改所有实体的id。

我使用UUID作为新文件名保存上传到DocumentTypedocument字段中的文件,因此在从集合中删除条目时,ID需要保持不变。我已经尝试将映射和未映射的id字段添加到集合中,但是未映射的字段被完全忽略,并且映射的字段允许用户修改id列中的数据,因此不适合在这里使用。

我需要做些什么来修改此表单,以使条令保持集合中的数据与其在数据库中表示的实体之间的连接?

因此,在具有类似行为的回购的错误跟踪器中发现此问题后,最后一个链接的问题将我指向read-me:的这一部分

不要更改字段名

Symfony使用字段名来排序集合,而不是dom上的每个元素。因此,默认情况下,如果在在中间,以下所有元素的索引都将减少1(field[3]将变为field[2],依此类推(,如果您添加一些元素在中间,所有后续元素都将看到它们的索引增加以留出空间给新的。

通过此实施,您一定会在以下情况下保持正确的位置单击"向上移动"one_answers"向下移动"以获得exmaple。但在某些方面在某些情况下,您可能不想覆盖索引,最有可能的是维护条令关系。

preserve_names选项设置为true以从不接触字段名称。但是请注意,此选项将禁用allow_upallow_downdrag_drop选项,并将强制CCD_ 10为true。

默认值:

$('.collection').collection({
preserve_names: false
});

来源:https://github.com/ninsuo/symfony-collection/blob/d5e6cbc7c7dc1f0509631c9bb6094fead0f6c8f0/README.md#options

因此,解决方案应该是初始化集合,将选项preserve_names设置为true,而不是默认值false

$('.collection').collection({
preserve_names: true // this is our fix
});

最新更新