Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Symfony 4] Requête DQL

Sujet résolu
    19 novembre 2019 à 11:18:01

    Bonjour à tous/toutes.

    Je suis actuellement en train de faire un site de prise de rendez-vous, qui s'apparentera pour le client, osthéopathe, à un mini-ERP.

    Je n'ai pas un niveau de dingue en Symfony, mais j'adore ce framework, et bosser sur ce projet me plaît énormément, bref.

    Pour le moment j'en suis au stade où l'utilisateur prend rendez-vous. Je souhaite  que le patient (déjà loggé) puisse prendre rendez-vous avec un type de séance et un praticien. Pour le moment je part du principe que tous les médecins savent faire toutes types de séance.

    Lors de la conception, je suis parti sur plusieurs tables pour régir mes droits et mes accès:

    - J'en ai 3 pour les "métiers" : Practitioner/Praticien, Secretary/Secrétaire, Patient/Patient (oui bon... :D).
    - Deux pour les "droits" : Role, et Rights
    - Une pour les utilisateurs : Users

    Dans ma table Role se présente comme ceci :



    Et ma table Users se présente ainsi :
     + d'autres champs, mais cela ne m'intéresse par pour le moment.

    Dans mon formulaire de prise de rendez-vous, je souhaite que le patient puisse choisir avec quel médecin il souhaite prendre rendez-vous, et que sur le formulaire, au lieu de choisir l'ID du praticien, il choisisse un nom et un prénom (c'est plus intelligible tout de suite!).

    Pour ça j'utilise le "choice_label", mais pour être franc, je ne sais pas si c'est le meilleur choix, c'est ce que j'ai vu qui se rapprochait du plus de ce que souhaitait mettre en place.

    Je passe par une fonction privée dans la classe de mon formulaire pour appeller le Repo de ma class User.

    Ensuite dans mon Repo, je souhaiterai executer la fonction suivante :

    SELECT u.last_name, u.first_name FROM user as u, role as r WHERE u.role_id = r.id AND "$role"_id IS NOT NULL

    Où $role est un string : ou 'practitioner', ou 'secretary', ou 'patient', comme ça je pourrai le réutiliser à ma guise (et une fois que j'aurai compris le fonctionnement d'une requête, ça ira déjà mieux je pense!).

    Et ma query :

        /*
         * Param : a string : 'practitioner', 'secretary', or 'patient'
         * Return : An array of strings
         * Uses : AppointmentType
         * SQL : SELECT u.last_name, u.first_name FROM user as u, role as r WHERE u.role_id = r.id AND "$role"_id IS NOT NULL
         *
         * */
        public function findNamesByRole($role) {
            
            return $this->createQueryBuilder('u' )
                        ->from('r', 'role')
                        ->select(['u.lastName', 'u.firstName'])
                        ->where('u.roleId = r.id')
                        ->andWhere($role.' IS NOT NULL')
                        ->getQuery()
                        ->getArrayResult()
                        ;
        }

    Et mon erreur :

    Ce que je comprend c'est que r est pas défini, la QueryBuilder n'arrive pas à comprendre qu'il faut travailler sur 2 tables. Or toute les recherches que j'ai effectuées sur le sujet porte sur les jointures, et ce n'est pas ce que je recherche.

    Je n'ai pas besoin d'avoir des élements d'une autre tables, je souhaite juste avoir toutes les lignes de role où practioner_id n'est pas null. Ensuite je souhaite selectionner les User où ces ID sont égaux à ceux de role_id.

    Y a t-il une âme charitable qui pourrait me débloquer, s'il vous plait ? :ange:


    Mon AppointmentControler :

    <?php
    
    namespace App\Controller\EntityController;
    
    use App\Entity\Appointment;
    use App\Form\AppointmentType;
    use App\Repository\AppointmentRepository;
    use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\HttpFoundation\Response;
    use Symfony\Component\Routing\Annotation\Route;
    use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
    use App\EventSubscriber;
    
    class AppointmentController extends AbstractController
    {
        public function __construct(TokenStorageInterface $tokenStorage){
            $this->user = $tokenStorage->getToken()->getUser();
    
        }
    
        /**
         * @Route("/appointment/new", name="appointment_new", methods={"GET","POST"})
         */
        public function new(Request $request): Response
        {
            $appointment = new Appointment();
            $form = $this->createForm(AppointmentType::class, $appointment);
            $form->handleRequest($request);
    
            if ($form->isSubmitted() && $form->isValid()) {
                $entityManager = $this->getDoctrine()->getManager();
                $entityManager->persist($appointment);
                $entityManager->flush();
    
                return $this->redirectToRoute('appointment_index');
            }
    
            return $this->render('appointment/new.html.twig', [
                'appointment' => $appointment,
                'form' => $form->createView(),
                'user' => $this->user
            ]);
        }
    }

    Mon AppointmentType :

    <?php
    
    
    namespace App\Form;
    
    use App\Entity\Appointment;
    use App\Entity\Practitioner;
    use App\Entity\Session;
    use App\Entity\User;
    use App\Repository\UserRepository;
    use Symfony\Bridge\Doctrine\Form\Type\EntityType;
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
    use Symfony\Component\Form\Extension\Core\Type\TextareaType;
    use Symfony\Component\Form\FormBuilderInterface;
    use Symfony\Component\OptionsResolver\OptionsResolver;
    
    class AppointmentType extends AbstractType{
    
        private $userRepository;
    
        public function __construct(UserRepository $userRepository){
            $this->userRepository = $userRepository;
        }
    
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder
                ->add('session', EntityType::class,[
                    'class' => Session::class,
                    'choice_label' => 'type',
                    'multiple' => false,
                    'required' => true
                ])
                ->add('practitioner', EntityType::class,[
                    'class' => Practitioner::class,
                    'choice_label' => $this->getPractitionersNames(),
                    'multiple' => false,
                    'required' => true
                ])
                ->add('date', DateTimeType::class, [
                    'placeholder' => [
                        'year' => 'Année', 'month' => 'Mois', 'day' => 'Jour'
                    ],
                ])
                ->add('reason', TextareaType::class,[
                    'required' => false
                ])
            ;
        }
    
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults([
                'data_class' => Appointment::class,
            ]);
        }
    
        private function getPractitionersNames(){
    
            return $this->userRepository->findNamesByRole('practitioner');
        }
    
    }


    Mon UserRepository :

    <?php
    
    namespace App\Repository;
    
    use App\Entity\User;
    use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
    use Doctrine\Common\Persistence\ManagerRegistry;
    
    /**
     * @method User|null find($id, $lockMode = null, $lockVersion = null)
     * @method User|null findOneBy(array $criteria, array $orderBy = null)
     * @method User[]    findAll()
     * @method User[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
     */
    class UserRepository extends ServiceEntityRepository
    {
        public function __construct(ManagerRegistry $registry)
        {
            parent::__construct($registry, User::class);
        }
    
        /*
         * Param : a string : 'practitioner', 'secretary', or 'patient'
         * Return : An array of strings
         * Uses : AppointmentType
         * SQL : SELECT u.last_name, u.first_name FROM user as u, role as r WHERE u.role_id = r.id AND "$role"_id IS NOT NULL
         *
         * */
        public function findNamesByRole($role) {
    
            return $this->createQueryBuilder('u' )
                        ->from('r', 'role')
                        ->select(['u.lastName', 'u.firstName'])
                        ->where('u.roleId = r.id')
                        ->andWhere($role.' IS NOT NULL')
                        ->getQuery()
                        ->getArrayResult()
                        ;
        }
    }
    

    Mon Entity\Practitioner :

    <?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\PractitionerRepository")
     */
    class Practitioner
    {
        /**
         * @ORM\Id()
         * @ORM\GeneratedValue()
         * @ORM\Column(type="integer")
         */
        private $id;
    
        /**
         * @ORM\Column(type="boolean")
         */
        private $isLeader;
    
        /**
         * @ORM\OneToMany(targetEntity="App\Entity\Appointment", mappedBy="practitioner")
         */
        private $appointments;
    
        public function __construct()
        {
            $this->appointments = new ArrayCollection();
        }
    
        public function getId(): ?int
        {
            return $this->id;
        }
    
        public function getIsLeader(): ?bool
        {
            return $this->isLeader;
        }
    
        public function setIsLeader(bool $isLeader): self
        {
            $this->isLeader = $isLeader;
    
            return $this;
        }
    
        /**
         * @return Collection|Appointment[]
         */
        public function getAppointments(): Collection
        {
            return $this->appointments;
        }
    
        public function addAppointment(Appointment $appointment): self
        {
            if (!$this->appointments->contains($appointment)) {
                $this->appointments[] = $appointment;
                $appointment->setPractitioner($this);
            }
    
            return $this;
        }
    
        public function removeAppointment(Appointment $appointment): self
        {
            if ($this->appointments->contains($appointment)) {
                $this->appointments->removeElement($appointment);
                // set the owning side to null (unless already changed)
                if ($appointment->getPractitioner() === $this) {
                    $appointment->setPractitioner(null);
                }
            }
    
            return $this;
        }
    }
    

    Mon Entity\User :

    <?php
    
    namespace App\Entity;
    
    use Doctrine\Common\Collections\ArrayCollection;
    use Doctrine\Common\Collections\Collection;
    use Doctrine\ORM\Mapping as ORM;
    use Symfony\Component\Security\Core\User\UserInterface;
    
    /**
     * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
     */
    class User implements UserInterface,\Serializable
    {
    
        /**
         * @ORM\Id()
         * @ORM\GeneratedValue()
         * @ORM\Column(type="integer")
         */
        private $id;
    
        /**
         * @ORM\Column(type="string", length=255)
         */
        private $lastName;
    
        /**
         * @ORM\Column(type="string", length=255)
         */
        private $firstName;
    
        /**
         * @ORM\Column(type="string", length=255)
         */
        private $phone;
    
        /**
         * @ORM\Column(type="string", length=255)
         */
        private $mail;
    
        /**
         * @ORM\OneToOne(targetEntity="App\Entity\Role", cascade={"persist", "remove"})
         * @ORM\JoinColumn(nullable=false)
         */
        private $role;
    
        /**
         * @ORM\Column(type="boolean")
         */
        private $isActive;
    
        /**
         * @ORM\Column(type="string", length=255)
         */
        private $username;
    
        /**
         * @ORM\Column(type="string", length=255)
         */
        private $password;
    
        /**
         * @ORM\ManyToMany(targetEntity="App\Entity\Appointment", mappedBy="users")
         */
        private $appointments;
    
        /**
         * @ORM\OneToOne(targetEntity="App\Entity\Prescription", mappedBy="patient", cascade={"persist", "remove"})
         */
        private $prescriptions;
    
        public function __construct()
        {
            $this->appointments = new ArrayCollection();
        }
    
        public function getId(): ?int
        {
            return $this->id;
        }
    
        public function getLastName(): ?string
        {
            return $this->lastName;
        }
    
        public function setLastName(string $lastName): self
        {
            $this->lastName = $lastName;
    
            return $this;
        }
    
        public function getFirstName(): ?string
        {
            return $this->firstName;
        }
    
        public function setFirstName(string $firstName): self
        {
            $this->firstName = $firstName;
    
            return $this;
        }
    
        public function getPhone(): ?string
        {
            return $this->phone;
        }
    
        public function setPhone(string $phone): self
        {
            $this->phone = $phone;
    
            return $this;
        }
    
        public function getMail(): ?string
        {
            return $this->mail;
        }
    
        public function setMail(string $mail): self
        {
            $this->mail = $mail;
    
            return $this;
        }
    
        public function getRole(): ?Role
        {
            return $this->role;
        }
    
        public function setRole(Role $role): self
        {
            $this->role = $role;
    
            return $this;
        }
    
        public function getIsActive(): ?bool
        {
            return $this->isActive;
        }
    
        public function setIsActive(bool $isActive): self
        {
            $this->isActive = $isActive;
    
            return $this;
        }
    
        public function getUsername(): ?string
        {
            return $this->username;
        }
    
        public function setUsername(string $username): self
        {
            $this->username = $username;
    
            return $this;
        }
    
        public function getPassword(): ?string
        {
            return $this->password;
        }
    
        public function setPassword(string $password): self
        {
            $this->password = $password;
    
            return $this;
        }
    
        /**
         * @return Collection|Appointment[]
         */
        public function getAppointments(): Collection
        {
            return $this->appointments;
        }
    
        public function addAppointment(Appointment $appointment): self
        {
            if (!$this->appointments->contains($appointment)) {
                $this->appointments[] = $appointment;
                $appointment->addUser($this);
            }
    
            return $this;
        }
    
        public function removeAppointment(Appointment $appointment): self
        {
            if ($this->appointments->contains($appointment)) {
                $this->appointments->removeElement($appointment);
                $appointment->removeUser($this);
            }
    
            return $this;
        }
    
        public function getPrescriptions(): ?Prescription
        {
            return $this->prescriptions;
        }
    
        public function setPrescriptions(Prescription $prescriptions): self
        {
            $this->prescriptions = $prescriptions;
    
            // set the owning side of the relation if necessary
            if ($prescriptions->getPatient() !== $this) {
                $prescriptions->setPatient($this);
            }
    
            return $this;
        }
    
        /*################################## USER INTERFACE ###############################*/
    
         /**
         * Returns the roles granted to the user.
         *
         *     public function getRoles()
         *     {
         *         return ['ROLE_USER'];
         *     }
         *
         * Alternatively, the roles might be stored on a ``roles`` property,
         * and populated in any number of different ways when the user object
         * is created.
         *
         * @return (Role|string)[] The user roles
         */
         /*
          * Uses : Login
          * */
        public function getRoles(){
    
            if($this->getRole()){
                if($this->getRole()->getPractitioner()){
                    if($this->getRole()->getPractitioner()->getIsLeader()){
                        return ['ROLE_PRACTITIONER_ADMIN'];
                    }
                    else{
                        return ['ROLE_PRACTITIONER'];
                    }
                }
    
                elseif ($this->getRole()->getSecretary()){
                    if ($this->getRole()->getSecretary()->getIsLeader()){
                        return ['ROLE_SECRETARY_ADMIN'];
                    }
                    else{
                        return ['ROLE_SECRETARY'];
                    }
                }
                else{
                    return ['ROLE_PATIENT'];
                }
            }
        }
        /*
        /**
         * Returns the password used to authenticate the user.
         *
         * This should be the encoded password. On authentication, a plain-text
         * password will be salted, encoded, and then compared to this value.
         *
         * @return string|null The encoded password if any
    
        public function getPassword(){
    
        }*/
    
        /**
         * Returns the salt that was originally used to encode the password.
         *
         * This can return null if the password was not encoded using a salt.
         *
         * @return string|null The salt
         */
        public function getSalt(){
            return null;
        }
        /*
        /**
         * Returns the username used to authenticate the user.
         *
         * @return string The username
         *
        public function getUsername(){
    
        }*/
    
        /**
         * Removes sensitive data from the user.
         *
         * This is important if, at any given point, sensitive information like
         * the plain-text password is stored on this object.
         */
        public function eraseCredentials(){
    
        }
    
        /*################################## SERIALIZE INTERFACE ###############################*/
    
        /**
         * String representation of object
         * @link https://php.net/manual/en/serializable.serialize.php
         * @return string the string representation of the object or null
         * @since 5.1.0
         */
        public function serialize(){
    
            return serialize([
                    $this->id,
                    $this->username,
                    $this->password
            ]);
    
        }
    
        /**
         * Constructs the object
         * @link https://php.net/manual/en/serializable.unserialize.php
         * @param string $serialized <p>
         * The string representation of the object.
         * </p>
         * @return void
         * @since 5.1.0
         */
        public function unserialize($serialized){
    
            list(
                $this->id,
                $this->username,
                $this->password
                ) = unserialize($serialized, ['allowed_class' => false]);
    
        }
    }
    

    -
    Edité par NicolasVigouroux2 19 novembre 2019 à 14:51:59

    • Partager sur Facebook
    • Partager sur Twitter
      19 novembre 2019 à 11:28:10

      Salut,

      tu ne t'es pas inversé dans le from('r', 'role') => from('role','r') ???

      • Partager sur Facebook
      • Partager sur Twitter
        19 novembre 2019 à 11:48:18

        Effectivement, en plus mon IDE l'avait suggéré :honte:

        Bon après niveau erreur c'est à peut près la même chose :

        Mais par contre ça me fait penser, ai-je besoin de déclarer mon Entity\Role dans le constructeur ? Peut être que comme je passe par le Repo de User celui ci ne détecte que l'entity User et sa table. Je regarde.

        • Partager sur Facebook
        • Partager sur Twitter
          19 novembre 2019 à 11:55:04

          Je n'ai pas commencé à bosser sur les repository, je les utilise basiquement...
          • Partager sur Facebook
          • Partager sur Twitter
            19 novembre 2019 à 13:44:11

            Salut !

            Il y a quelque chose qui me surprend : tu es dans UserRepository, mais tu spécifies dans ta requête que tu veux requêter l'entité Role… Es-tu sûr que c'est bien ce que tu veux ? Si oui, pourquoi ne pas mettre ce qui retourne des objets Role dans RoleRepository ?

            La syntaxe pour une sélection partielle, c'est select('PARTIAL alias.{id, nomDeChamp1, nomDeChamp2}'). Lister les champs dans un select(…) va les dédoubler. Il faut savoir que createQueryBuilder('u') implique plus ou moins SELECT * FROM laTableDeLentitéDuRepository AS u, donc l'entité est automatiquement spécifiée et tous ses champs scalaires sont automatiquement récupérés.

            Ensuite, les jointures devraient se faire avec la syntaxe adéquate, soit l'utilisation de join('u.role', 'r'). Cela utilise des optimisations et de Doctrine, et du SGBDR qu'il y a derrière le cas échéant.

            • Partager sur Facebook
            • Partager sur Twitter
              22 novembre 2019 à 12:09:28

              Bonjour Ymox,

              Effectivement, je ne peux pas requêter sur deux tables à la fois, sinon faire du raw SQL, mais même ça j'ai galérer.

              Finalement j'ai créer un champ "lastname" dans la table qui m'intéresse, c'est peut être moins clean au niveau DB mais au moins ça fonctionne.

              • Partager sur Facebook
              • Partager sur Twitter

              [Symfony 4] Requête DQL

              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
              • Editeur
              • Markdown