Doctrine Queries Ohne QueryBuilder

geposted am 14.03.2018, in Symfony

Der QueryBuilder ist vermutlich nur die zweitbeste Möglichkeit, komplexe Queries umzusetzen. Wenn man beispielsweise viele n:m-Relationen hat, wie User und Skills, die mit user_has_skills-Tables verknüpft sind, würde man erwarten, daß diese Query funktioniert:

$this->em->getRepository('AppBundle:user')->findBySkills($skill)

Tuts aber bei n:m-Beziehungen nicht.

Dann versucht man im UserRepository sowas wie 

  $qb = $this->_em->createQueryBuilder();
        $qb->select('u')
                ->from($this->_entityName, 'u')
                ->leftJoin('AppBundle\Entity\Skill', 's', \Doctrine\ORM\Query\Expr\Join::WITH, 's.id = u.skill')

Dann stellt sich schon mal leichte Panik ein und man würde gerne auf "einfaches" SQL zurückgreifen:

//      natives SQL geht zwar auch, aber die Keys stimmen nicht mehr, wenn die Table-Columns nicht genauso heißen wie die entity-properties
        $conn = $this->_em->getConnection();

        $sql = 'SELECT * FROM user u
                    INNER JOIN auftrag a ON a.bbt_id = ak.bbkid
                    LEFT JOIN auftrag_skill s ON s.auftrag_id = a.id
                    WHERE a.team_id = :team AND s.skill_id = :skill
                    LIMIT 500;';

        $stmt = $conn->prepare($sql);
        $stmt->execute(['skill' => intval($skill), 'team' => intval($team) ]);

//        // returns an array of arrays (i.e. a raw data set)
        return $stmt->fetchAll();

Das funktioniert! Aber leider nur mit einem gräßlichen Twig-Fehler, weil jetzt alle Keys der Entity nicht mehr stimmen. Man bekommt außerdem nur die Skill-Id aus der Kreuztabelle und nicht das gesamte Skill-Objekt.

Wirklich funktionieren mit dem richtigen Entity-Mapping tut dann nur noch DQL , das sieht dann (anderes Beispiel) so aus:

   $query = $this->_em->createQuery('SELECT ak FROM AppBundle:Aktivitaet ak
                        INNER JOIN ak.auftrag a
                        LEFT JOIN a.skill s
                        WHERE a.team = :team
                        AND s.id = :sid'
                )
                ->setParameter('team', $team)
                ->setParameter('sid', $skill)
                ->setMaxResults($limit);

        return $query->getResult();

Das ist sogar simpler als gedacht. Und wer denkt, warum denn ein eigenes DQL nötig sein soll, der wird durch die Erfahrung mit den verwürfelten Keys eines Besseren belehrt. 

Mit der createQuery-Methode sollten sich auch die komplexesten Queries auf vernünftige Weise umsetzen lassen ohne auf den Komfort von Entities und ORM verzichten zu müssen.