Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Symfony2] Problème de requête avec QueryBuilder

Sujet résolu
    21 juin 2013 à 19:32:26

    Bonsoir à tous !! :)

    Je viens vers vous car j'ai encore du mal à me servir correctement du QueryBuilder dans des situations un peu plus complexes et je suis bloqué. Malgrè mes recherches je n'ai pas trouvé de solution à mon blocage. 

    Concrètement, j'ai une entité Actus qui contient les actualités de mon site, et qui a un lien ManytoMany bidirectionnel avec mon entité Quartier qui contient les quartiers de ma ville. Je voudrais afficher sur la page de chaque quartier les actualités correspondantes à celui-ci en y mettant une pagination avec 5 actualités par page. Pour l'instant je n'ai pas mis en place le paginator je pense pouvoir le faire mais il faut déjà que mon appelle au repository fonctionne :) .

    Première question: est-ce qu'il vaut mieux installer un bundle complémentaire pour faire des paginations ou l'exemple du tutoriel est-il le plus efficace ?

    Je vous mets le code de mon Controller, de mon entité Actus et de son repository où je déclare la fonction de recherche. Je ne pense pas que le template soit nécessaire puisque j'arrive parfaitement à les affichés en utilisant une fonction de type findAll() dans le Controller.

    QuartierController : 

    <?php
    
    namespace Tefec\QuartierBundle\Controller;
    
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Symfony\Component\HttpFoundation\Response;
    use Tefec\QuartierBundle\Entity\Actus;
    use Tefec\AnnuaireBundle\Entity\Offres;
    
    	class QuartierController extends Controller
    	{
    	   public function capitoleAction() // quartier capitole esquirol saintpierre voire carmes
    	    {
    			$liste = $this->getDoctrine()
                      ->getManager()
                      ->getRepository('TefecQuartierBundle:Actus')
                      ->Quartieractu('Capitole');
         
    			return $this->render('TefecQuartierBundle:Quartier:capitole.html.twig', array(
    			'liste_actus' => $liste));
    		}

    ActusRepository

    <?php
    
    namespace Tefec\QuartierBundle\Entity;
    
    use Doctrine\ORM\EntityRepository;
    
    /**
     * ActusRepository
     *
     */
    class ActusRepository extends EntityRepository
    {
       public function Quartieractu($quartier)
        {
    		$qb = $this->createQueryBuilder("SELECT * FROM Actus a LEFT JOIN Quartier q ON a.quartier_id=q.id WHERE q.nom= :motcle")
    				  ->setParameter('motcle', $quartier);
    
            return $qb->getQuery()              
                      ->getResult();
    	}
    }

    Et la définition de la relation dans mon entité Actus

    <?php
    
    namespace Tefec\QuartierBundle\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    
    /**
     * Actus
     *
     * @ORM\Table()
     * @ORM\Entity(repositoryClass="Tefec\QuartierBundle\Entity\ActusRepository")
     */
    class Actus
    {
    	/**
       * @ORM\ManyToMany(targetEntity="Tefec\AnnuaireBundle\Entity\Quartier", inversedBy="actus")
       */
      private $quartier;


    Voici l'erreur que j'obtiens, je précise que j'ai déjà essayé de changer le '*' par 'a.*' ou encore 'a' dans ma requête SQL .

    "

    [Syntax Error] line 0, col 7: Error: Expected IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression, got 'SELECT' "

    J'espère que vous pourrez m'aider, merci d'avance à tous, help SVP !

    PS : Désolé pour l'autre sujet avec le même nom je ne sais pas trop comment j'ai fait mais c'est une fausse manipulation.

    -
    Edité par gaetanclemenceau 21 juin 2013 à 20:58:39

    • Partager sur Facebook
    • Partager sur Twitter
      21 juin 2013 à 22:41:13

      Salut !

      Si tu souhaites utiliser une requête comme tu le fais, en mettant du DQL, il faudrait utiliser createQuery() plutôt que createQueryBuilder()

      -
      Edité par Ymox 28 mars 2017 à 16:31:54

      • Partager sur Facebook
      • Partager sur Twitter
        21 juin 2013 à 23:58:34

        Salut Ymox ! Toujours toi à la rescousse haha .

        Alors merci pour la précision, j'ai effectivement vu cla sur des forums mais lorsque je le fais la methode n'est pas reconnue, voici l'erreur :

        "

        Undefined method 'createQuery'. The method name must start with either findBy or findOneBy! "

        Je ne suis pas sûre mais j'ai lu beaucoup de sujets concernant les annotations (JOIN Column) qui pourrait générer des problèmes et aussi par rapport à l'entity class qui serait à la source du problème, mais sans trop comprendre le pourquoi du comment ...

        • Partager sur Facebook
        • Partager sur Twitter
          22 juin 2013 à 10:21:25

          En fait, je te conseillerais d'utiliser le QueryBuilder plutôt que le DQL. La seule chose qui change est qu'avec le QueryBuilder, je sais comment faire fonctionner :p

          Avec la fonction createQueryBuilder(), tu peux directement donner un alias à l'objet pour lequel est le repository est prévu, donc $this->createQueryBuilder('a') sait déjà que tu travailles pour récupérer les objets Actus avec l'alias a.
          Ensuite, il te faut utiliser la méthode join() pour dire que tu veux joindre la propriété quartier, donc join('a.quartier', 'q'). Là aussi, le second paramètre est un alias de l'objet Quartier. Pour des raisons de performances, on va aussi ajouter addSelect('q').
          Il ne te reste plus qu'à mettre le filtre. Tu l'as peut-être senti, c'est la fonction where(), mais ce qu'on va y mettre est un peu différent ce que qu'on pourrait penser : on va utiliser le constructeur d'expression. C'est un outil qui permet de construire des égalités ou toute autre comparaison avec des fonctions. Là, pour ne pas trop perdre de temps, on va mettre $qb->expr()->eq('q.id', ':id'), donc au final, on aura where($qb->expr()->eq('q.nom', ':motcle')). Ne pas oublier de lier le paramètre motcle, et on est bon.

          gaetanclemenceau a écrit:

          […] Je ne suis pas sûre […]

          Oui, j'en doute aussi :lol:

          -
          Edité par Ymox 28 mars 2017 à 16:32:20

          • Partager sur Facebook
          • Partager sur Twitter
            22 juin 2013 à 19:07:09

            Oui effectivement je trouve le QueryBuilder plus pratique également mais cela m'empêchera de faire un LIMIT ou un setMaxResults par la suite non ?

            J'ai essayé tout de même tes propositions car avant d'utiliser un LIMIT je voudrais pouvoir faire fonctionner mon repository mais avec le code suivant j'obtiens.... UNE PAGE BLANCHE ! Donc rien est marqué dans la console et j'ai pu lire que c'était souvient lié à un problème d'accessiblité via .htaccess sans trouver comment règler cela.

            Mon code dans le repository : 

            class ActusRepository extends EntityRepository
            {
               public function Quartieractu($quartier)
                {
            		$qb = $this->createQueryBuilder('a')
            				   ->join('a.quartier', 'q')
            				   ->addSelect('q')
            				   ->where($qb->expr()->eq('q.nom', ':nom'))
            				   ->setParameter('nom', $quartier);
            
                    return $qb->getQuery()
            				  ->getResult();
            	}
            }

            DOis-je effectuer certains tests pour rechercher une erreur précise ? 

            J'ai aussi essayé un code relativement similaire sains le constructeur d'expression sait-on jamais :) :

            class ActusRepository extends EntityRepository
            {
               public function Quartieractu($quartier)
                {
            		$qb = $this->createQueryBuilder('a')
            				   ->join('a.quartier', 'q')
            				   ->addSelect('q')
            				   ->where('q.nom', ':nom')
            				   ->setParameter('nom', $quartier);
            
                    return $qb->getQuery()
            				  ->getResult();
            	}
            }

            Avec ce code j'obtiens l'erreur suivante :

            "

            [Semantical Error] line 0, col 75 near 'q WHERE q.nom': Error: Class Tefec\QuartierBundle\Entity\Actus has no association named quartier "

            Peut-être que cela donne t'aidera pour la source de l'erreur... J'espère du moins ;)

            -
            Edité par gaetanclemenceau 22 juin 2013 à 19:15:28

            • Partager sur Facebook
            • Partager sur Twitter
              22 juin 2013 à 19:15:05

              gaetanclemenceau a écrit:

              Oui effectivement je trouve le QueryBuilder plus pratique également mais cela m'empêchera de faire un LIMIT ou un setMaxResults par la suite non ?

              Pas le moins du monde ! o_O

              gaetanclemenceau a écrit:

              J'ai essayé tout de même tes propositions car avant d'utiliser un LIMIT je voudrais pouvoir faire fonctionner mon repository mais avec le code suivant j'obtiens.... UNE PAGE BLANCHE ! Donc rien est marqué dans la console […] DOis-je effectuer certains tests pour rechercher une erreur précise ?

              Non, au temps pour moi, c'est leftJoin au lieu de join().

              -
              Edité par Ymox 28 mars 2017 à 16:33:06

              • Partager sur Facebook
              • Partager sur Twitter
                22 juin 2013 à 20:50:39

                "J'aimerais que tu réfléchisses à la dernière question de ma signature, s'il te plaît ^^

                si c'est bien la question sur l'AS3 Je veux bien le faire, mais je ne pense pas utiliser de l' ActionScript 3 dans mon code si ? Ou j'ai mal compris...

                J'avais pensé à essayer le leftJoin mais j'obtiens tout de même une Page Blanche....

                Je vais peut-être préciser un peu où j'en suis. En fait, j'arrive à récupérer mes actualités via la méthode findAll() ou encore quelque chose du type findBy( array(), array('date' =>'desc'), 5, 0); mais dès que j'essaye de les récupérer en fonction du quartier j'ai des erreurs, que ce soit avec findByQuartier($valeur) ou en utilisant mon repository.

                Avec le Repository et la fonction que tu m'as soufflée j'obtiens donc une Page Blanche. 

                Et lorsque j'essaye pour changer par exemple d'utiliser un findByQuartier('capitole') j'obtiens cette erreur dans symfony :

                "Notice: Undefined index: joinColumns in C:\wamp\www\Symfony\vendor\doctrine\orm\lib\Doctrine\ORM\Persisters\BasicEntityPersister.php line 1651"

                Donc bien évidemment le principal serait de pouvoir le faire via le Repository ce qui ne change pas mon problème mais j'ai comme l'impression que toutes ces erreurs viennent d'un problème de définition de ma relation entre Actus et Quartier; y aurait-il des annotations manquantes ? 

                J'espère que c'est un peu plus clair en tous les cas :)

                -
                Edité par gaetanclemenceau 22 juin 2013 à 20:51:59

                • Partager sur Facebook
                • Partager sur Twitter
                  22 juin 2013 à 20:54:52

                  Cette histoire de joinColumns manquant me fait me demander ce qu'il y a dans tes mappings. Tu pourrais les mettre ici, s'il te plaît ? Pas besoin de ce qu'il y a après le constructeur – et pas besoin du constructeur non-plus ;)

                  • Partager sur Facebook
                  • Partager sur Twitter
                    22 juin 2013 à 21:27:34

                    Oui c'est aussi ce que je pense. POur le mapping je te mets la relation mes entités je pense que c'est tout ce qu'il faut, sinon tu me diras s'il te faut également les getter et setter :) !

                    Actus.php

                    <?php
                    
                    namespace Tefec\QuartierBundle\Entity;
                    
                    use Doctrine\ORM\Mapping as ORM;
                    
                    /**
                     * Actus
                     *
                     * @ORM\Table()
                     * @ORM\Entity(repositoryClass="Tefec\QuartierBundle\Entity\ActusRepository")
                     */
                    class Actus
                    {
                        /**
                         * @var integer
                         *
                         * @ORM\Column(name="id", type="integer")
                         * @ORM\Id
                         * @ORM\GeneratedValue(strategy="AUTO")
                         */
                        private $id;
                    	
                    	/**
                       * @ORM\ManyToMany(targetEntity="Tefec\AnnuaireBundle\Entity\Quartier", inversedBy="actus")
                       * 
                       * @ORM\JoinColumn(nullable=false)
                       */
                      private $quartier;

                    et dans Quartier.php 

                    namespace Tefec\AnnuaireBundle\Entity;
                    
                    use Doctrine\ORM\Mapping as ORM;
                    
                    /**
                     * Quartier
                     *
                     * @ORM\Table(name="Quartier")
                     * @ORM\Entity(repositoryClass="Tefec\AnnuaireBundle\Entity\QuartierRepository")
                     */
                    class Quartier
                    {
                        /**
                         * @var integer
                         *
                         * @ORM\Column(name="id", type="integer")
                         * @ORM\Id
                         * @ORM\GeneratedValue(strategy="AUTO")
                         */
                        private $id;
                    
                        /**
                         * @var string
                         *
                         * @ORM\Column(name="nom", type="string", length=255)
                         */
                        private $nom;
                    
                    	/**
                        * @ORM\OneToMany(targetEntity="Tefec\AnnuaireBundle\Entity\Commerce1", mappedBy="quartier")
                        */
                        private $commerces;
                    	
                    	/**
                        * @ORM\OneToMany(targetEntity="Tefec\AnnuaireBundle\Entity\Offres", mappedBy="quartier")
                        */
                        private $offres;
                    	
                    	/**
                        * @ORM\ManyToMany(targetEntity="Tefec\QuartierBundle\Entity\Actus", mappedBy="quartier")
                        */
                        private $actus;



                    • Partager sur Facebook
                    • Partager sur Twitter
                      22 juin 2013 à 21:40:22

                      Tu as une manyToMany entre Actus et Quartier, l'annotation correcte est @ORM\JoinColumns, avec un s

                      • Partager sur Facebook
                      • Partager sur Twitter
                        22 juin 2013 à 21:57:18

                        Ha d'accord, j'ai effectué la modification maintenant j'ai un problème avec ce que je mets dans la parenthèse car l'erreur qu'il y a est :

                        "[Creation Error] The annotation @ORM\JoinColumns declared on property Tefec\QuartierBundle\Entity\Actus::$quartier does not have a property named "nullable". Available properties: value".

                        Je comprends ce que cela signifie mais je ne comprends pourquoi je ne peux pas utiliser la propriété nullable ... Et le "available properties: value" cela signifie que dans les parenthèses je peux uniquement définir une valeur pour ce champs ?

                        Je ne vois pas très bien en quoi le JoinColumns perturbe la relation entre mes entités à vrai dire ... :/  

                        • Partager sur Facebook
                        • Partager sur Twitter
                          22 juin 2013 à 22:07:21

                          Il faut aussi l'annotation @ORM\JoinTable. Reprend les mappings depuis le tutoriel ou la documentation officielle, je pense que ce sera mieux. L'attribut nullable est à mettre dans une annotation @ORM\JoinColumn, qui doit être ici dans une @ORM\JoinColumns, elle-même dans @ORM\JoinTable. La seule chose est que je ne pense pas qu'une colonne nullable pour une table de jointure ait du sens, donc tu pourras probablement enlever cette instruction.

                          • Partager sur Facebook
                          • Partager sur Twitter
                            23 juin 2013 à 1:31:35

                            Effectivement, le mapping a encore beaucoup de mystère pour moi ;) .

                            Je me suis renseigné notamment avec la documentation et voici comment j'ai modifié mon entité Actus :

                            <?php
                            
                            namespace Tefec\QuartierBundle\Entity;
                            
                            use Doctrine\ORM\Mapping as ORM;
                            
                            /**
                             * Actus
                             *
                             * @ORM\Table()
                             * @ORM\Entity(repositoryClass="Tefec\QuartierBundle\Entity\ActusRepository")
                             */
                            class Actus
                            {
                                /**
                                 * @var integer
                                 *
                                 * @ORM\Column(name="id", type="integer")
                                 * @ORM\Id
                                 * @ORM\GeneratedValue(strategy="AUTO")
                                 */
                                private $id;
                            	
                            	/**
                               * @var ArrayCollection $quartier
                               * @ORM\ManyToMany(targetEntity="Tefec\AnnuaireBundle\Entity\Quartier", inversedBy="actus")
                               * @ORM\JoinTable(name="Actus_Quartier",
                               *				joinColumns={@ORM\JoinColumn(name="actus_id", referencedColumnName="id")},
                               *      inverseJoinColumns={@ORM\JoinColumn(name="quartier_id", referencedColumnName="id")}
                               * )
                               */
                              private $quartier;


                            Il me semble que les annotations sont justes, je les ai matérialisé en base de données et j'ai vidé le cahce, mais j'ai toujours les même erreurs... à savoir un epage blanche lorsque j'essaye d'utiliser mon repository ( ->Quartieractu('capitole') ) et l'erreur déjà citée avec ( ->findByQuartier('capitole') ) : 

                            "Notice: Undefined index: joinColumns in C:\wamp\www\Symfony\vendor\doctrine\orm\lib\Doctrine\ORM\Persisters\BasicEntityPersister.php line 1651"

                            Pourtant il me semble que l'utilisation de joinColumns est correct.... 

                            J'ai quand même une moitié bonne nouvelle haha, j'arrive à charger ma page sans avoir d'erreur lorsque j'utilise la syntaxe suivante dans le repository :

                            public function Quartieractu($quartier)
                                {
                            		$qb = $this->createQueryBuilder('a')
                            				   ->leftJoin('a.quartier', 'q')
                            				   ->addSelect('q')
                            				   ->where('q.nom = :motcle')
                            				   ->setParameter('motcle', $quartier);
                            		throw new \Exception($qb->getQuery()->getSql());
                            
                                    return $qb->getQuery()
                            				  ->getResult();


                            Et je dis que c'est à moitié une bonne nouvelle car je n'ai aucune actualité que s'affiche, et en testant la requête SQL renvoyé par ce bout de code je m'aperçois qu'elle est bonne mais que Mysql récupère un résultat vide ( "MySQL a retourné un résultat vide (aucune ligne). ( Traitement en 0.0012 sec )"), mais alors pourquoi ... ? voici la requête: 

                            "SELECT a0_.id AS id0, a0_.titre AS titre1, a0_.motcle AS motcle2, a0_.contenu AS contenu3, a0_.date AS date4, q1_.id AS id5, q1_.nom AS nom6, a0_.image_id AS image_id7 FROM Actus a0_ LEFT JOIN Actus_Quartier a2_ ON a0_.id = a2_.actus_id LEFT JOIN Quartier q1_ ON q1_.id = a2_.quartier_id WHERE q1_.nom = ?


                             avec ? = 'capitole'

                            -
                            Edité par gaetanclemenceau 23 juin 2013 à 4:20:21

                            • Partager sur Facebook
                            • Partager sur Twitter
                              23 juin 2013 à 11:10:55

                              La requête m'a l'air correcte, je pense qu'il te faudrait regarder ce qui t'est retourné si tu enlèves la clause WHERE. Vérifie aussi que tes données sont cohérentes, parce que je pense que c'est un peu le souci. Notamment si tes changements ont créé une nouvelle table actus_quartier, celle-ci sera très probablement vide – note que tu devrais récupérer tes actus depuis la table actus tout de même (suivant ton installation serveur, la casse est importante), donc est-ce que ce serait cette table qui serait vide ?

                              • Partager sur Facebook
                              • Partager sur Twitter
                                23 juin 2013 à 19:37:03

                                Effectivement sans la clause WHERE ma requête fonctionne et récupère toutes mes actualités. 

                                Comme tu le dis je pense que c'st une question de cohérence, et la définition des annotations avec la nouvelle classe Actus_Quartier me perturbe un peu même si je pense que cette table permet simplement l'id d'une actualité à l'id d'un quartier. 

                                Mon entité actus n'est pas vide ça j'en suis certain, en revanche la table Actus_Quartier je ne sais pas trop même si ele ne devrait pas être vide comme j'ai associé mes actualités à un quartier lors de leur enregistrement en BDD. 

                                 Aurais-tu une idée sur ce qu'il faudrait que je mette dans le WHERE pour récupérer correctement mes actualités ? car là je vois bien le problème mais je ne sais pas du tout ce que je pourrais changer sans faire une grosse erreur ;) !

                                Au cas où, voici ma requête qui fonctionne sans la clause Where:

                                "SELECT a0_.id AS id0, a0_.titre AS titre1, a0_.motcle AS motcle2, a0_.contenu AS contenu3, a0_.date AS date4, q1_.id AS id5, q1_.nom AS nom6, a0_.image_id AS image_id7 FROM Actus a0_ LEFT JOIN Actus_Quartier a2_ ON a0_.id = a2_.actus_id LEFT JOIN Quartier q1_ ON q1_.id = a2_.quartier_id "

                                EDIT :

                                Je pense avoir trouvé la source du problème :). En regardant bien dans phpmyadmin je m'aperçois qu'en fait dans ma table Actus je n'ai pas de champs "quartier_id" et de même dans ma table Quartier je n'ai pas de "actus_id" ; et enfin, la table actus_quartier est vide donc les relations entre mes actualités et leur quartier ne sont pas enregistrées, du coup il est logique que je ne puisse pas les récupérer comme je le souhaite.

                                Alors je sais qu'il ne faut pas raisonner en terme de BDD ou de tables mais au moins je sais d'où vient l'erreur, en revanche je ne vois pas pourquoi la relation n'est pas enregistrées malgré les annotations que j'ai ajouté dans mes entités... Mon erreur pourait-elle aussi venir de mes Datafixtures lorsque j'enregistre mes données en BDD ?

                                -
                                Edité par gaetanclemenceau 23 juin 2013 à 20:34:55

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  27 juin 2013 à 16:23:43

                                  Je passe le sujet en résolu, je n'ai pas vraiment trouvé la solution au problème du début de ce topic, mais du coup j'ai complètement refait ma relation entre les entités exactement comme sur le tutoriel de WInzou et cela fonctionne, si jamais quelqu'un rencontre un problème du genre je vous mettrez les codes avec plaisir.

                                  Merci quand même à ceux qui m'ont aidé, notamment Ymox ! :)

                                  • Partager sur Facebook
                                  • Partager sur Twitter

                                  [Symfony2] Problème de requête avec QueryBuilder

                                  × 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