Erweiterte Abfragen
Die Criteria API und NQL sind deine Werkzeuge, um Datenbank-Abfragen auf Basis von Entities und ihrer Eigenschaften zu formulieren. Sie bieten dir dabei dieselben Möglichkeiten wie normales SQL, weshalb du im Normalfall auf native SQL-Abfragen verzichten kannst.
Criteria API und CriteriaItem
Mit der Criteria API werden Vergleiche (z. B. im WHERE
- HAVING
- oder ON
-Abschnitt) über den n2n\persistence\orm\criteria\compare\CriteriaComparator
beschrieben. Eine Instanz dieses Typs erhältst du zum Beispiel als Rückgabewert von Criteria::where()
oder Criteria::join()
.
Vergleichs-Methoden wie CriteriaComparator::match()
erwarten als ersten Parameter den Namen der Eigenschaft und als zweiten Parameter den Vergleichsoperator. Nutzt du den CriteriaComparator
, um einen WHERE
- und HAVING
-Abschnitt zu beschreiben, so erwartet CriteriaComparator::match()
als letzten Parameter den Wert, mit dem die Eigenschaft verglichen werden soll. Beschreibst du hingegen einen ON
-Abschnitt (Joins), so erwartet er als letzten Parameter den Namen der Eigenschaft, die mit der ersten Eigenschaft verglichen werden soll.
$criteria = $this->em->createCriteria(); $criteria->select('a') ->from(Article::getClass(), 'a') ->where()->match('a.title', '=', 'Holeradio');
Um in einem WHERE
- oder HAVING
-Abschnitt zwei Eigenschaften miteinander zu vergleichen, musst du die Parameter-Typen genauer spezifizieren. Dies erreichst du, indem du CriteriaComparator::match()
Objekte vom Typ n2n\persistence\orm\criteria\item\CriteriaItem
übergibst. Diese kannst du über n2n\persistence\orm\criteria\item\CrIt
generieren.
Es existieren folgende CriteriaItem
-Typen:
Typ | CrIt | Beispiel |
---|---|---|
n2n\persistence\orm\criteria\item\CriteriaProperty |
CrIt::p() |
CrIt::p('a.text') |
n2n\persistence\orm\criteria\item\CriteriaFunction |
CrIt::f() |
CrIt::f('COUNT', 'a.id') |
n2n\persistence\orm\criteria\item\CriteriaConstant |
CrIt::c() |
CrIt::c('holeradio') |
Möchten wir im WHERE
-Abschnitt die Eigenschaften Article::$title
und Article::$text
über CriteriaComparator::match()
miteinander vergleichen, so müssen wir mindestens für den dritten Parameter ein CriteriaItem
generieren.
$criteria->select('a') ->from(Article::getClass(), 'a') ->where()->match('a.title', '=', CrIt::p('a.text'));
Du kannst CriteriaComparator::match()
, anstelle eines Eigenschaftsnamens, auch einen Ausdruck einer Funktion übergeben. Das Selbe gilt für Criteria::select()
.
$criteria->select('COUNT(a)')->from(Article::getClass(), 'a');
Um ein Criteria
einheitlich zu beschreiben, kannst du natürlich einfach für alle Parameter CriteriaItem
-Objekte verwenden. Übrigens ermöglicht es dir CriteriaComparator::endClause()
den WHERE
-Abschnitt zu beenden und mit der Beschreibung der Criteria fortzufahren.
$criteria->select(CrIt::f('COUNT', 'a')) ->from(Article::getClass(), 'a') ->where()->match(CrIt::p('a.title'), '=', CrIt::c('holeradio'))->endClause() ->order(CrIt::f('RAND'));
Dieses Criteria lässt sich über NQL folgendermassen beschreiben:
$criteria = $this->em->createNqlCriteria( 'SELECT COUNT(a) FROM Article a WHERE a.title = :title ORDER BY RAND()', array('title' => 'holeradio'));
Joins
Criteria::join()
ermöglicht es dir Entities miteinander zu verknüpfen. Als ersten Parameter erwartet diese Methode die zu verknüpfende Entity und als zweiten deren Alias. Über den dritten Parameter kannst du optional den entsprechenden Join-Typ (INNER
, LEFT
, RIGHT
) definieren. Criteria::join()
gibt dir dabei ein CriteriaComparator
-Objekt zurück, über welches du den zugehörigen ON
-Abschnitt beschreiben kannst. Mit CriteriaComparator::endClause()
kannst du den ON
-Abschnitt beenden und mit der Beschreibung der Criteria fortfahren.
$criteria = $em->createCriteria(); $criteria->select('ba') ->from(BlogArticle::getClass(), 'ba') ->join(Category::getClass(), 'cat')->match('ba.title', '=', 'cat.name')->endClause() ->order('ba.title', 'ASC');
Über NQL lässt sich dieselbe Abfrage folgendermassen beschreiben:
$criteria = $this->em->createNqlCriteria( 'SELECT ba FROM BlogArticle ba JOIN Category cat ON ba.title = cat.name ORDER BY ba.title ASC');
Sub-Queries
Criteria::fromCriteria()
und Criteria::joinCriteria()
ermöglichen es dir Sub-Queries zu realisieren. Sie erwarten beide als ersten Parameter ein Criteria
-Objekt, welches das Sub-Query beschreibt. Dieses kannst du über Criteria::subCriteria()
generieren. Ansonsten unterscheiden sich Criteria::fromCriteria()
und Criteria::joinCriteria()
nicht von Criteria::from()
und Criteria::join()
.
$criteria = $this->em->createCriteria(); $criteria->select('ba')->select('lc.numComments') ->from(BlogArticle::getClass(), 'ba') ->joinCriteria($criteria->subCriteria() ->select('COUNT(c)', 'numComments')->select('c.blogArticle', 'blogArticle') ->from(Comment::getClass(), 'c') ->group('c.blogArticle'), 'lc', 'LEFT')->match('lc.blogArticle', '=', 'ba')->endClause() ->order('ba.id', 'DESC');
Die oben stehende Abfrage liefert alle Artikel und deren Anzahl Kommentare zurück.
Sub-Queries mit der Criteria API zu beschreiben, macht die Abfrage schnell unübersichtlich. Deshalb ist es zu empfehlen, in solchen Situationen NQL zu verwenden.
$criteria = $this->em->createNqlCriteria(' SELECT ba, lc.numComments FROM BlogArticle ba LEFT JOIN ( SELECT COUNT(c.id) AS numComments, c.blogArticle AS blogArticle FROM Comment c GROUP BY c.blogArticle) lc ON lc.blogArticle = ba ORDER BY ba.id DESC');
Sub-Queries kannst du, wie aus SQL gewohnt, auch im WHERE
-, HAVING
- oder ON
-Abschnitt nutzen.
$deniedTitle = 'Test'; $criteria = $em->createCriteria(); $criteria->select('c') ->from(Comment::getClass(), 'c') ->where()->match('c.blogArticle', 'NOT IN', $criteria->subCriteria() ->select('ba') ->from(BlogArticle::getClass(), 'ba') ->where()->match('ba.title', '=', $deniedTitle)->endWhere());
$deniedTitle = 'Test'; $criteria = $em->createNqlCriteria( 'SELECT c FROM Comment c WHERE c.blogArticle NOT IN ( SELECT ba FROM BlogArticle ba WHERE ba.title = :deniedTitle)', array('deniedTitle' => $deniedTitle));
Möchtest du im WHERE
-, HAVING
- oder ON
-Abschnitt auf EXISTS
- und NOT EXISTS
-Bedinungen zurückgreifen, so kannst du diese über die Methode CriteriaComparator::test()
beschreiben.
$deniedTitle = 'Test'; $criteria = $this->em->createCriteria(); $criteria->select('c') ->from(Comment::getClass(), 'c') ->where()->test('NOT EXISTS', $criteria->subCriteria() ->select('ba') ->from(BlogArticle::getClass(), 'ba') ->where()->match('ba.title', '=', $deniedTitle)->andMatch('c.blogArticle', '=', CrIt::p('ba')) ->endClause());
EXISTS
- und NOT EXISTS
-Bedinungen lassen sich natürlich auch in NQL-Statements integrieren.
$deniedTitle = 'Test'; $criteria = $em->createNqlCriteria( 'SELECT c FROM Comment c WHERE NOT EXISTS ( SELECT ba FROM BlogArticle ba WHERE ba.title = :deniedTitle AND c.blogArticle = ba)', array('deniedTitle' => $deniedTitle)); return $criteria->toQuery()->fetchArray();
Placeholders
$criteria = $this->em->createSimpleCriteria(Article::getClass(), array('type' => new CriteriaPlaceholder('type'))); $query->setParameter('type', 'news'); $newsArticles = $query->fetchArray(); $query->setParameter('type', 'event'); $eventArticles = $query->fetchArray();
$criteria = $this->em->createNqlCriteria('SELECT a FROM Article a WHERE a.type = :type'); $query = $criteria->toQuery(); $query->setParameter('type', 'news'); $newsArticles = $query->fetchArray(); $query->setParameter('type', 'event'); $eventArticles = $query->fetchArray();
IS NULL = null
Solange du auf Placeholders verzichtest, werden = "null"-Bedinungen automatisch in IS NULL
umgewandelt.
Criteria API
$critera = $this->em->createSimpleCriteria(Article::getClass(), array('type' => null));
NQL
$critera = $this->em->createNqlCriteria('SELECT a FROM Article WHERE type = :type', array('type' => null));
Nutzt du Placeholders, so musst du dich selbst darum kümmern, da die Parameter-Werte dadurch erst nach dem Kompilieren der Abfrage (Criteria::toQuery()) bekannt werden.
Criteria API
$critera = $this->em->createCriteria(); $critera->select('a')->from(Article::getClass(), 'a')->where() ->match('type', 'IS', new CriteriaPlaceholder('type')); $critera->setParameter('type', null);
NQL
$critera = $this->em->createNqlCriteria('SELECT a FROM Article WHERE type IS :type'); $critera->setParameter('type', null);