phpsymfonydoctrine-ormsymfony4

Automatically set database ID in CollectionType


I have two entities, ParkingType and Exception, there's a OneToMany relation between them, since each ParkingType can have multiple exceptions.

I made it so whenever I create a new ParkingType I can also create the exceptions related to it at the same time. I did that by using a CollectionType containing the exception form inside the parkingtype form. the collectiontype is dynamic so I can add as many exceptions as I would like.

Problem: the exception table has a column called type_id, which is used to relate that exception to the ParkingType and I have to populate that field myself everytime by choosing from a drop down. I don't want to do that, I want that field to reference the object that I just created by default. My code: Exception entity:


<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\ExceptionRepository")
 */
class Exception
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=200, nullable=true)
     */
    private $nom;

    /**
     * @ORM\Column(type="date", nullable=true)
     */
    private $datedebut;

    /**
     * @ORM\Column(type="date", nullable=true)
     */
    private $datefin;

    /**
     * @ORM\Column(type="time", nullable=true)
     */
    private $tempsdebut;

    /**
     * @ORM\Column(type="time", nullable=true)
     */
    private $tempsfin;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\TypeParking", inversedBy="exceptions")
     * @ORM\JoinColumn(nullable=false)
     */
    private $type;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getNom(): ?string
    {
        return $this->nom;
    }

    public function setNom(?string $nom): self
    {
        $this->nom = $nom;

        return $this;
    }

    public function getDatedebut(): ?\DateTimeInterface
    {
        return $this->datedebut;
    }

    public function setDatedebut(?\DateTimeInterface $datedebut): self
    {
        $this->datedebut = $datedebut;

        return $this;
    }

    public function getDatefin(): ?\DateTimeInterface
    {
        return $this->datefin;
    }

    public function setDatefin(?\DateTimeInterface $datefin): self
    {
        $this->datefin = $datefin;

        return $this;
    }

    public function getTempsdebut(): ?\DateTimeInterface
    {
        return $this->tempsdebut;
    }

    public function setTempsdebut(?\DateTimeInterface $tempsdebut): self
    {
        $this->tempsdebut = $tempsdebut;

        return $this;
    }

    public function getTempsfin(): ?\DateTimeInterface
    {
        return $this->tempsfin;
    }

    public function setTempsfin(?\DateTimeInterface $tempsfin): self
    {
        $this->tempsfin = $tempsfin;

        return $this;
    }

    public function getType(): ?TypeParking
    {
        return $this->type;
    }

    public function setType(?TypeParking $type): self
    {
        $this->type = $type;

        return $this;
    }
}

ParkingType Entity:

<?php

namespace App\Entity;

/**
 * @ORM\Entity(repositoryClass="App\Repository\TypeParkingRepository")
 */
class TypeParking
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=55)
     */
    private $libelle;

    /**
     * @ORM\Column(type="time", nullable=true)
     */
    private $tempsmax;

    /**
     * @ORM\Column(type="date", nullable=true)
     */
    private $jourdebut;

    /**
     * @ORM\Column(type="date", nullable=true)
     */
    private $jourfin;



    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Exception", mappedBy="type", cascade={"persist"})
     */
    private $exceptions;

    public function __construct()
    {
        $this->exceptions = new ArrayCollection();
    }
    public function getId(): ?int
    {
        return $this->id;
    }

    public function getTempsmax(): ?\DateTimeInterface
    {
        return $this->tempsmax;
    }

    public function setTempsmax(\DateTimeInterface $tempsmax): self
    {
        $this->tempsmax = $tempsmax;

        return $this;
    }

    public function getJourdebut(): ?\DateTimeInterface
    {
        return $this->jourdebut;
    }

    public function setJourdebut(\DateTimeInterface $jourdebut): self
    {
        $this->jourdebut = $jourdebut;

        return $this;
    }

    public function getJourfin(): ?\DateTimeInterface
    {
        return $this->jourfin;
    }

    public function setJourfin(\DateTimeInterface $jourfin): self
    {
        $this->jourfin = $jourfin;

        return $this;
    }



    public function getLibelle(): ?string
    {
        return $this->libelle;
    }

    public function setLibelle(string $libelle): self
    {
        $this->libelle = $libelle;

        return $this;
    }

    /**
     * @return Collection|Exception[]
     */
    public function getExceptions(): Collection
    {
        return $this->exceptions;
    }

    public function removeException(Exception $exception): self
    {
        if ($this->exceptions->contains($exception)) {
            $this->exceptions->removeElement($exception);
            // set the owning side to null (unless already changed)
            if ($exception->getType() === $this) {
                $exception->setType(null);
            }
        }

        return $this;
    }
    public function addException(Exception $exception)
    {
        $this->exceptions->add($exception);
    }

}

ParkingType Form:

<?php


class TypeParkingType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('libelle')
            ->add('tempsmax')
            ->add('jourdebut')
            ->add('jourfin')





           ->add('exceptions', CollectionType::class, [
            'label'        => 'Exception',
            'entry_type'   => Exception1Type::class,
            'allow_add'    => true,
            'allow_delete' => true,
            'prototype'    => true,
            'required'     => false,
            'by_reference' => true,
            'delete_empty' => true,
            'attr'         => [
                'class' => 'collection',
            ],
        ])


        ;
                $builder->add('save', SubmitType::class, [
                'label' => 'See my addresses',
        ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => TypeParking::class,
        ]);
    }
}

Exception Form:

class ExceptionType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder            

        ->add('nom')
        ->add('datedebut')
        ->add('datefin')
        ->add('tempsdebut')
        ->add('tempsfin')
        ->add('type',EntityType::class, [
            'class' => TypeParking::class,
            'choice_label' => 'libelle',
        ])        

 ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Exception::class,
        ]);
    }
}


Solution

  • just remove the field from the ExceptionType* and in your ParkingType, change the addException:

    public function addException(Exception $exception)
    {
        $this->exceptions->add($exception);
        $exception->setType($this); // <-- this is new.
    }
    

    update: you also have to set the exceptions CollectionType option by_reference to false, such that the adder is actually called by the form component.

    this is one way to do it. the other option is to do this in your controller and call setType for every Exception you find in the ParkingType ...

    *) this assumes, you don't edit the Exception on its own, ever. otherwise, either conditionally add the parking-type form field on certain options, or use a different form for Exceptions (for example named ParkingTypeExceptionType) that doesn't adds a form field for parking type.