I have a table which contains two many-to-one associations and two date fields. This table is used to form a connection between those two mane-to-one associations for a specific date range.
I use a symfony form collection to show all the existing entries in this table and I’m trying to use it for adding / deletion, too.
I have this entity:
class ProductGroupConnection
{
private ?int $id = null;
private BaseProduct $product;
private Group $group;
private DateTime $activeFrom;
private DateTime $activeUntil;
public function getId(): ?int
{
return $this->id;
}
public function getProduct(): BaseProduct
{
return $this->product;
}
public function setProduct(BaseProduct $product): static
{
$this->product = $product;
return $this;
}
public function getGroup(): Group
{
return $this->group;
}
public function setGroup(Group $group): static
{
$this->group = $group;
return $this;
}
}
I also created a simple Model as source for the form:
class ConnectionCollection
{
private ArrayCollection $productGroups;
public function __construct()
{
$this->productGroups = new ArrayCollection();
}
public function hasProductGroups(): bool
{
return !empty($this->productGroups);
}
public function containsProductGroup(ProductGroupConnection $productGroup): bool
{
return $this->productGroups->contains($productGroup);
}
public function getProductGroups(): ArrayCollection
{
return $this->productGroups;
}
public function setProductGroups(array $productGroups): static
{
foreach ($productGroups as $productGroup) {
$this->productGroups->add($productGroup);
}
return $this;
}
public function addProductGroup(ProductGroupConnection $productGroup): static
{
$this->productGroups->add($productGroup);
return $this;
}
public function removeProductGroup(ProductGroupConnection $productGroup): static
{
if ($this->productGroups->contains($productGroup)) {
$this->productGroups->removeElement($productGroup);
}
return $this;
}
}
Then I created this form type:
class ConnectionPageType extends AbstractType
{
use FormButtonTrait;
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('productGroups', CollectionType::class, [
'entry_type' => ConnectionType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'by_reference' => false,
]);
// submit button is added via FormButtonTrait
}
}
This is a simplified version of the ConnectionType:
class ConnectionType extends AbstractType
{
$form
->add('product', EntityType::class, [
'class' => BaseProduct::class,
'multiple' => false,
])
->add('group', EntityType::class, [
'class' => Group::class,
'multiple' => false,
])
->add('activeFrom', DateType::class, [
'label' => 'Active from',
])
->add('activeUntil', DateType::class, [
'label' => 'Active until',
]);
}
And finally, this is the relevant part of my controller:
class ConnectionPageController extends AbstractController
{
public function editAction(Request $request, string $pageType): Response
{
$model = new ConnectionCollection();
$model->setProductGroups($this->productGroupRepo->findAll())
$form = $this->createForm(ConnectionPageType::class, $model);
if ($request) {
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
foreach ($data->getProductGroups() as $group) {
if (null === $group->getId()) {
$this->entityManager->persist($group);
}
}
$this->entityManager->flush();
}
}
}
}
Displaying and adding works fine, but I don’t really know how to build the deletion.
I could do something like this:
class ConnectionPageController extends AbstractController
{
public function editAction(Request $request, string $pageType): Response
{
$model = new ConnectionCollection();
$model->setProductGroups($this->productGroupRepo->findAll())
$originalData = new ConnectionCollection();
$originalData->setProductGroups($this->productGroupRepo->findAll())
$form = $this->createForm(ConnectionPageType::class, $model);
if ($request) {
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** @var ConnectionCollection $data */
$data = $form->getData();
foreach ($originalData->getProductGroups as $group) {
if (!$data->containsProductGroup($group)) {
$this->entityManager->remove($group);
}
}
foreach ($data->getProductGroups() as $group) {
if (null === $group->getId()) {
$this->entityManager->persist($group);
}
}
$this->entityManager->flush();
}
}
}
}
But that doesn’t seem right to me. This way, every entry which is not submitted, will be deleted. If someone just removes the entry via Dev tools, it is gone. If the entry is removed from the DOM because of other circumstances, it is gone, too. So the entry might be deleted without explicitly clicking on the delete-button.
As I said, this seems odd to me.
Is there a better way to do it?