Paramètre et Querybuilder dans un Formtype Symfony

Des fois nous avons déjà rencontré des cas où nous voulons filtrés les valeurs affichés pour un champ ChoiceType d’un formulaire Symfony. Dans ce tutoriel, nous allons voir comment s’y prendre avec un exemple simple.

Nous avons utilisé Symfony 4.2 pour cet exemple. Si votre projet est dans une version antérieure, rien ne change sauf le namespace de chaque classe

Pré-requis

Afin de pouvoir suivre ce tutoriel, vous devez avoir:

  • Notion sur Symfony
  • Notion sur l’utilisation des formulaires Symfony
  • Déjà utilisé le QueryBuilder de doctrine

Objectifs

Préparation de l’environnement

Pour ce tutoriel, nous allons créer deux entités avec une relation OneToMany

Une entité “partie” [père] et un entité “chapitre” [fils], dont une “partie” peut avoir un ou plusieurs “chapitre” liés avec lui.

Un chapitre peut avoir des sous-chapitre ou pas. C’est à dire, l’entité Chapitre a une relation ManyToOne avec lui-même

Voici le contenu du fichier App\Entity\Chapitre.php

<?php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

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

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

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

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Partie", inversedBy="chapitres")
     * @ORM\JoinColumn(nullable=false)
     * @ORM\OrderBy({"position"="ASC"})
     */
    private $partie;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Chapitre", inversedBy="sousChapitres")
     * @ORM\JoinColumn(onDelete="SET NULL")
     */
    private $parent;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Chapitre", mappedBy="parent")
     * @ORM\JoinColumn(onDelete="SET NULL")
     */
    private $sousChapitres;


    public function __construct()
    {
        $this->sousChapitres = new ArrayCollection();
    }

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

    public function getTitre()
    {
        return $this->titre;
    }

    public function setTitre(string $titre)
    {
        $this->titre = $titre;

        return $this;
    }

    public function getIntroduction()
    {
        return $this->introduction;
    }

    public function setIntroduction(string $introduction)
    {
        $this->introduction = $introduction;

        return $this;
    }

    public function getPartie()
    {
        return $this->partie;
    }

    public function setPartie(Partie $partie)
    {
        $this->partie = $partie;

        return $this;
    }

    public function getParent()
    {
        return $this->parent;
    }

    public function setParent($parent)
    {
        $this->parent = $parent;

        return $this;
    }

    /**
     * @return Collection|self[]
     */
    public function getSousChapitres(): Collection
    {
        return $this->sousChapitres;
    }

    public function addSousChapitre(self $sousChapitre): self
    {
        if (!$this->sousChapitres->contains($sousChapitre)) {
            $this->sousChapitres[] = $sousChapitre;
            $sousChapitre->setParent($this);
        }

        return $this;
    }

    public function removeSousChapitre(self $sousChapitre): self
    {
        if ($this->sousChapitres->contains($sousChapitre)) {
            $this->sousChapitres->removeElement($sousChapitre);
            // set the owning side to null (unless already changed)
            if ($sousChapitre->getParent() === $this) {
                $sousChapitre->setParent(null);
            }
        }

        return $this;
    }

 
    /**
     * @return string
     */
    public function __toString()
    {
        return $this->getTitre();
    }
}

Et voici le fichier App\Entity\Partie.php

<?php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

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

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

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Chapitre", mappedBy="partie", orphanRemoval=true)
     */
    private $chapitres;

    public function __construct()
    {
        $this->chapitres = new ArrayCollection();
    }
    

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

    public function getTitre()
    {
        return $this->titre;
    }

    public function setTitre(string $titre)
    {
        $this->titre = $titre;

        return $this;
    }

    /**
     * @return Collection|Chapitre[]
     */
    public function getChapitres(): Collection
    {
        return $this->chapitres;
    }

    public function addChapitre(Chapitre $chapitre): self
    {
        if (!$this->chapitres->contains($chapitre)) {
            $this->chapitres[] = $chapitre;
            $chapitre->setPartie($this);
        }

        return $this;
    }

    public function removeChapitre(Chapitre $chapitre): self
    {
        if ($this->chapitres->contains($chapitre)) {
            $this->chapitres->removeElement($chapitre);
            // set the owning side to null (unless already changed)
            if ($chapitre->getPartie() === $this) {
                $chapitre->setPartie(null);
            }
        }

        return $this;
    }
}

Créons maintenant nos formulaire en sachant que le parent d’un chapitre doit être un chapitre dans la même partie que lui. C’est à dire, nous devons filtrer les valeurs affichés dans le champ ‘parent’ de notre formulaire pour n’afficher que les chapitres dans le même partie que le chapitre courant.

Pour faire cela, voici le contenu de notre formulaire pour l’entité Chapitre:

<?php

namespace App\Form;

use App\Entity\Chapitre;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ChapitreType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $partie = $options['partie_id'];
        $builder
            ->add('titre')
            ->add('introduction')
            ->add('parent', EntityType::class, [
                'class' => Chapitre::class,
                //Ici nous définissons le query_builder pour filtrer les chapitres
                'query_builder' => function (EntityRepository $entityRepository) use ($partie){
                    return $entityRepository->createQueryBuilder('c')
                        ->where('c.partie = :partie')
                        ->setParameter('partie', $partie);
                }
            ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Chapitre::class,
        ]);
        // partie_id est requis
        $resolver->setRequired('partie_id'); 
        // Nous allons passer en paramètre l'ID qui est un entier
        $resolver->setAllowedTypes('partie_id', 'integer');
    }
}

Qu’avons-nous fait?

Nous avons passer en paramètre du formulaire l’ID de la partie [Les paramètres pour la création d’un formulaire sont ajoutés dans le tableau $options  de la méthode buildForm()

 Et maintenant, utilisons ce formulaire dans notre controlleur

Prenons un exemple pour créer un nouveau chapitre.

/**
 * @Route("/new/{id}", name="chapitre_new", methods={"GET","POST"})
 */
public function new(Request $request, Partie $partie): Response
{
    $chapitre = new Chapitre();
    $chapitre->setPartie($partie);
    //Nous passons en paramètre de la création de notre formulaire l'ID de la partie
    $form = $this->createForm(ChapitreType::class, $chapitre, ['partie_id' => $partie->getId()]);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $entityManager = $this->getDoctrine()->getManager();
        $entityManager->persist($chapitre);
        $entityManager->flush();
       
        return [notre Response];
    }

    return $this->render([Le view pour afficher le formulaire], [
        'chapitre' => $chapitre,
        'form' => $form->createView(),
    ]);
}

Mot de la fin

Ainsi, nous avons vu ensemble comment on passe une paramètre à un FormType de Symfony et d’utiliser un QueryBuilder dans un Field.

Si vous avez des questions, n’hésitez pas à écrire sur le commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *