Adding a little more information to the dql chapter.
This commit is contained in:
parent
5a79963bd2
commit
f99a096d01
@ -12,7 +12,6 @@ In essence, DQL provides powerful querying capabilities over your object model.
|
||||
|
||||
DQL is case in-sensitive, except for namespace, class and field names, which are case sensitive.
|
||||
|
||||
|
||||
++ Types of DQL queries
|
||||
|
||||
DQL as a query language has SELECT, UPDATE and DELETE constructs that map to their corresponding
|
||||
@ -21,7 +20,7 @@ have to be introduced into the persistence context through `EntityManager#persis
|
||||
consistency of your object model.
|
||||
|
||||
DQL SELECT statements are a very powerful way of retrieving parts of your domain model that are
|
||||
not accessible via assocations. Additionally they allow to retrieve entities and their associations
|
||||
not accessible via associations. Additionally they allow to retrieve entities and their associations
|
||||
in one single sql select statement which can make a huge difference in performance in contrast
|
||||
to using several queries.
|
||||
|
||||
@ -37,8 +36,9 @@ The select clause of a DQL query specifies what appears in the query result. The
|
||||
|
||||
Here is an example that selects all users with an age > 20:
|
||||
|
||||
[sql]
|
||||
SELECT u FROM MyProject\Model\User u WHERE u.age > 20
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u FROM MyProject\Model\User u WHERE u.age > 20');
|
||||
$users = $query->getResult();
|
||||
|
||||
Lets examine the query:
|
||||
|
||||
@ -52,7 +52,7 @@ The SELECT clause allows to specify both class identification variables that sig
|
||||
of a complete entity class or just fields of the entity using the syntax `u.name`.
|
||||
Combinations of both are also allowed and it is possible to wrap both fields and
|
||||
identification values into aggregation and DQL functions. Numerical fields can
|
||||
be part of computations using mathematical operatos. See the sub-section on [DQL Functions, Aggregates and Operations](#dqlfn)
|
||||
be part of computations using mathematical operations. See the sub-section on [DQL Functions, Aggregates and Operations](#dqlfn)
|
||||
on more information.
|
||||
|
||||
+++ Joins
|
||||
@ -69,21 +69,23 @@ Example:
|
||||
|
||||
Regular join of the address:
|
||||
|
||||
[sql]
|
||||
SELECT u FROM User u JOIN u.address a WHERE a.city = 'Berlin'
|
||||
[php]
|
||||
$query = $em->createQuery("SELECT u FROM User u JOIN u.address a WHERE a.city = 'Berlin'");
|
||||
$users = $query->getResult();
|
||||
|
||||
Fetch join of the address:
|
||||
|
||||
[sql]
|
||||
SELECT u, a FROM User u JOIN u.address a WHERE a.city = 'Berlin'
|
||||
[php]
|
||||
$query = $em->createQuery("SELECT u, a FROM User u JOIN u.address a WHERE a.city = 'Berlin'");
|
||||
$users = $query->getResult();
|
||||
|
||||
When Doctrine hydrates a query with fetch-join it returns the class in the FROM clause on the
|
||||
root level of the result array. In the previous example an array of User instances is returned
|
||||
and the address of each user is fetched and hydrated into the `User#address` variable. If you access
|
||||
the address Doctrine does not need to
|
||||
the address Doctrine does not need to lazy load the association with another query.
|
||||
|
||||
> **NOTE**
|
||||
> Doctrine allows you to walk all the assocations between all the objects in your domain model.
|
||||
> Doctrine allows you to walk all the associations between all the objects in your domain model.
|
||||
> Objects that were not already loaded from the database are replaced with lazy load proxy instances.
|
||||
> Non-loaded Collections are also replaced by lazy-load instances that fetch all the contained objects upon
|
||||
> first access. However relying on the lazy-load mechanism leads to many small queries executed
|
||||
@ -99,95 +101,194 @@ Named parameters are specified with ":name1", ":name2" and so on.
|
||||
|
||||
+++ DQL SELECT Examples
|
||||
|
||||
This section contains a large set of DQL queries and some explainations of what is happening.
|
||||
This section contains a large set of DQL queries and some explanations of what is happening.
|
||||
The actual result also depends on the hydration mode.
|
||||
|
||||
[sql]
|
||||
-- Hydrate all Users
|
||||
SELECT u FROM MyProject\Model\User u
|
||||
Hydrate all User entities:
|
||||
|
||||
-- Retrieve the IDs of all CmsUsers
|
||||
SELECT u.id FROM CmsUser u
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u FROM MyProject\Model\User u');
|
||||
$users = $query->getResult(); // array of User objects
|
||||
|
||||
-- Retrieve the IDs of all users that have written an article
|
||||
SELECT DISTINCT a.user.id FROM CmsArticle a
|
||||
Retrieve the IDs of all CmsUsers:
|
||||
|
||||
-- Retrieve all articles and sort them by the name of the articles users instance
|
||||
SELECT a FROM CmsArticle a ORDER BY a.user.name ASC
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u.id FROM CmsUser u');
|
||||
$ids = $query->getResult(); // array of CmsUser ids
|
||||
|
||||
-- Retrieve the Username and Name of a CmsUser
|
||||
SELECT u.username, u.name FROM CmsUser u
|
||||
Retrieve the IDs of all users that have written an article:
|
||||
|
||||
-- Retrieve a ForumUser and his single associated entity
|
||||
SELECT u, a FROM ForumUser u JOIN u.avatar a
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT DISTINCT a.user.id FROM CmsArticle a');
|
||||
$ids = $query->getResult(); // array of CmsUser ids
|
||||
|
||||
-- Retrieve a CmsUser and fetch join all the phonenumbers he has
|
||||
SELECT u, p FROM CmsUser u JOIN u.phonenumbers p
|
||||
Retrieve all articles and sort them by the name of the articles users instance:
|
||||
|
||||
-- Hydrate a result in Ascending or Descending Order
|
||||
SELECT u FROM ForumUser u ORDER BY u.id ASC
|
||||
SELECT u FROM ForumUser u ORDER BY u.id DESC
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT a FROM CmsArticle a ORDER BY a.user.name ASC');
|
||||
$articles = $query->getResult(); // array of CmsArticle objects
|
||||
|
||||
-- Using Aggregate Functions
|
||||
SELECT COUNT(u.id) FROM CmsUser u GROUP BY u.id
|
||||
Retrieve the Username and Name of a CmsUser:
|
||||
|
||||
-- With WHERE Clause and Positional Parameter
|
||||
SELECT u FROM ForumUser u WHERE u.id = ?1
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u.username, u.name FROM CmsUser u');
|
||||
$users = $query->getResults(); // array of CmsUser username and id values
|
||||
echo $users[0]['username'];
|
||||
|
||||
-- With WHERE Clause and Named Parameter
|
||||
SELECT u FROM ForumUser u WHERE u.username = :name
|
||||
Retrieve a ForumUser and his single associated entity:
|
||||
|
||||
-- With Nested Conditions in WHERE Clause
|
||||
SELECT u from ForumUser u WHERE (u.username = :name OR u.username = :name2) AND u.id = :id
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u, a FROM ForumUser u JOIN u.avatar a');
|
||||
$users = $query->getResult(); // array of ForumUser objects with the avatar association loaded
|
||||
echo get_class($users[0]->getAvatar());
|
||||
|
||||
-- With COUNT DISTINCT
|
||||
SELECT COUNT(DISTINCT u.name) FROM CmsUser
|
||||
Retrieve a CmsUser and fetch join all the phonenumbers he has:
|
||||
|
||||
-- With Arithmetic Expression in WHERE clause
|
||||
SELECT u FROM CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u, p FROM CmsUser u JOIN u.phonenumbers p');
|
||||
$users = $query->getResult(); // array of CmsUser objects with the phonenumbers association loaded
|
||||
$phonenumbers = $users[0]->getPhonenumbers();
|
||||
|
||||
-- Using multiple classes fetched using a FROM clause (all returned on the root level of the result)
|
||||
SELECT u, a FROM CmsUser u, CmsArticle a WHERE u.id = a.user.id
|
||||
Hydrate a result in Ascending:
|
||||
|
||||
-- Using a LEFT JOIN to hydrate all user-ids and optionally associated article-ids
|
||||
SELECT u.id, a.id FROM CmsUser u LEFT JOIN u.articles a
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u FROM ForumUser u ORDER BY u.id ASC');
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
-- Restricting a JOIN clause by additional conditions
|
||||
SELECT u FROM CmsUser u LEFT JOIN u.articles a WITH a.topic LIKE '%foo%'
|
||||
Or in Descending Order:
|
||||
|
||||
-- Using several Fetch JOINs
|
||||
SELECT u, a, p, c FROM CmsUser u
|
||||
JOIN u.articles a
|
||||
JOIN u.phonenumbers p
|
||||
JOIN a.comments c
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u FROM ForumUser u ORDER BY u.id DESC');
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
-- BETWEEN in WHERE clause
|
||||
SELECT u.name FROM CmsUser u WHERE u.id BETWEEN ?1 AND ?2
|
||||
Using Aggregate Functions:
|
||||
|
||||
-- DQL Functions in WHERE clause
|
||||
SELECT u.name FROM CmsUser u WHERE TRIM(u.name) = 'someone'
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT COUNT(u.id) FROM Entities\User u');
|
||||
$count = $query->getSingleScalarResult();
|
||||
|
||||
-- IN() Expression
|
||||
SELECT u.name FROM CmsUser u WHERE u.id IN(46)
|
||||
SELECT u FROM CmsUser u WHERE u.id IN (1, 2)
|
||||
SELECT u FROM CmsUser u WHERE u.id NOT IN (1)
|
||||
With WHERE Clause and Positional Parameter:
|
||||
|
||||
-- CONCAT() DQL Function
|
||||
SELECT u.id FROM CmsUser u WHERE CONCAT(u.name, 's') = ?1
|
||||
SELECT CONCAT(u.id, u.name) FROM CmsUser u WHERE u.id = ?1
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u FROM ForumUser u WHERE u.id = ?1');
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
-- EXISTS in WHERE clause with correlated Subquery
|
||||
SELECT u.id FROM CmsUser u
|
||||
WHERE EXISTS (SELECT p.phonenumber FROM CmsPhonenumber p WHERE p.phonenumber = u.id)
|
||||
With WHERE Clause and Named Parameter:
|
||||
|
||||
-- Get all users who are members of $group.
|
||||
SELECT u.id FROM CmsUser u WHERE :param MEMBER OF u.groups
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u FROM ForumUser u WHERE u.username = :name');
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
-- Get all users that have more than 1 phonenumber
|
||||
SELECT u FROM CmsUser u WHERE SIZE(u.phonenumbers) > 1
|
||||
With Nested Conditions in WHERE Clause:
|
||||
|
||||
-- Get all users that have no phonenumber
|
||||
SELECT u FROM CmsUser u WHERE u.phonenumbers IS EMPTY
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u from ForumUser u WHERE (u.username = :name OR u.username = :name2) AND u.id = :id');
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
With COUNT DISTINCT:
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT COUNT(DISTINCT u.name) FROM CmsUser
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
With Arithmetic Expression in WHERE clause:
|
||||
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000');
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
Using a LEFT JOIN to hydrate all user-ids and optionally associated article-ids:
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u.id, a.id as article_id FROM CmsUser u LEFT JOIN u.articles a');
|
||||
$results = $query->getResult(); // array of user ids and every article_id for each user
|
||||
|
||||
Restricting a JOIN clause by additional conditions:
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery("SELECT u FROM CmsUser u LEFT JOIN u.articles a WITH a.topic LIKE '%foo%'");
|
||||
$users = $query->getResult();
|
||||
|
||||
Using several Fetch JOINs:
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u, a, p, c FROM CmsUser u JOIN u.articles a JOIN u.phonenumbers p JOIN a.comments c');
|
||||
$users = $query->getResult();
|
||||
|
||||
BETWEEN in WHERE clause:
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u.name FROM CmsUser u WHERE u.id BETWEEN ?1 AND ?2');
|
||||
$usernames = $query->getResult();
|
||||
|
||||
DQL Functions in WHERE clause:
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery("SELECT u.name FROM CmsUser u WHERE TRIM(u.name) = 'someone'");
|
||||
$usernames = $query->getResult();
|
||||
|
||||
IN() Expression:
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u.name FROM CmsUser u WHERE u.id IN(46)');
|
||||
$usernames = $query->getResult();
|
||||
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u WHERE u.id IN (1, 2)');
|
||||
$users = $query->getResult();
|
||||
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u WHERE u.id NOT IN (1)');
|
||||
$users = $query->getResult();
|
||||
|
||||
CONCAT() DQL Function:
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery("SELECT u.id FROM CmsUser u WHERE CONCAT(u.name, 's') = ?1");
|
||||
$ids = $query->getResult();
|
||||
|
||||
$query = $em->createQuery('SELECT CONCAT(u.id, u.name) FROM CmsUser u WHERE u.id = ?1');
|
||||
$idUsernames = $query->getResult();
|
||||
|
||||
EXISTS in WHERE clause with correlated Subquery
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u.id FROM CmsUser u WHERE EXISTS (SELECT p.phonenumber FROM CmsPhonenumber p WHERE p.phonenumber = u.id)');
|
||||
$ids = $query->getResult();
|
||||
|
||||
Get all users who are members of $group.
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u.id FROM CmsUser u WHERE :param MEMBER OF u.groups');
|
||||
$ids = $query->getResult();
|
||||
|
||||
Get all users that have more than 1 phonenumber
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u WHERE SIZE(u.phonenumbers) > 1');
|
||||
$users = $query->getResult();
|
||||
|
||||
Get all users that have no phonenumber
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u WHERE u.phonenumbers IS EMPTY');
|
||||
$users = $query->getResult();
|
||||
|
||||
++++ Partial Object Syntax
|
||||
|
||||
By default when you run a DQL query in Doctrine and select only a subset of the
|
||||
fields for a given entity, you do not receive objects back. Instead, you receive
|
||||
only arrays as a flat rectangular result set, similar to how you would if you
|
||||
were just using SQL directly and joining some data.
|
||||
|
||||
If you want to select partial objects you can use the `partial` DQL keyword:
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT partial u.{id, username} FROM CmsUser u');
|
||||
$users = $query->getResult(); // array of partially loaded CmsUser objects
|
||||
|
||||
You use the partial syntax when joining as well:
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT partial u.{id, username}, partial a.{id, name} FROM CmsUser u JOIN u.articles a');
|
||||
$users = $query->getResult(); // array of partially loaded CmsUser objects
|
||||
|
||||
+++ Using INDEX BY
|
||||
|
||||
@ -365,7 +466,6 @@ An instance of the `Doctrine\ORM\Query` class represents a DQL query. You create
|
||||
$q = $em->createQuery();
|
||||
$q->setDql('select u from MyProject\Model\User u');
|
||||
|
||||
|
||||
+++ Query Result Formats
|
||||
|
||||
The format in which the result of a DQL SELECT query is returned can be influenced by a so-called `hydration mode`. A hydration mode specifies a particular way in which an SQL result set is transformed. Each hydration mode has its own dedicated method on the Query class. Here they are:
|
||||
@ -440,21 +540,69 @@ And here is how you would access it in PHP code:
|
||||
|
||||
You may have observed that in a mixed result, the object always ends up on index 0 of a result row.
|
||||
|
||||
+++ Hydration Mode Asumptions
|
||||
+++ Hydration Modes
|
||||
|
||||
Each of the Hydration Modes makes assumptions about how the result is returned to userland. You should
|
||||
Each of the Hydration Modes makes assumptions about how the result is returned to user land. You should
|
||||
know about all the details to make best use of the different result formats:
|
||||
|
||||
The constants for the different hydration modes are:
|
||||
|
||||
* Query::HYDRATE_OBJECT
|
||||
* Query::HYDRATE_ARRAY
|
||||
* Query::HYDRATE_SCALAR
|
||||
* Query::HYDRATE_SINGLE_SCALAR
|
||||
|
||||
++++ Object Hydration
|
||||
|
||||
Object hydration hydrates the result set into the object graph:
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u');
|
||||
$users = $query->getResult(Query::HYDRATE_OBJECT);
|
||||
|
||||
++++ Array Hydration
|
||||
|
||||
++++ Scalar Hydration Details
|
||||
You can run the same query with array hydration and the result set is hydrated into
|
||||
an array that represents the object graph:
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u');
|
||||
$users = $query->getResult(Query::HYDRATE_ARRAY);
|
||||
|
||||
You can use the `getArrayResult()` shortcut as well:
|
||||
|
||||
[php]
|
||||
$users = $query->getArrayResult();
|
||||
|
||||
++++ Scalar Hydration
|
||||
|
||||
If you want to return a flat rectangular result set instead of an object graph
|
||||
you can use scalar hydration:
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u');
|
||||
$users = $query->getResult(Query::HYDRATE_SCALAR);
|
||||
echo $users[0]['u_id'];
|
||||
|
||||
The following assumptions are made about selected fields using Scalar Hydration:
|
||||
|
||||
1. Fields from classes are prefixed by the DQL alias in the result. A query of the kind 'SELECT u.name ..' returns a key 'u_name' in the result rows.
|
||||
|
||||
++++ Single Scalar Hydration
|
||||
|
||||
If you a query which returns just a single scalar value you can use single scalar
|
||||
hydration:
|
||||
|
||||
[php]
|
||||
$query = $em->createQuery('SELECT COUNT(a.id) FROM CmsUser u LEFT JOIN u.articles a WHERE u.username = ?1 GROUP BY u.id');
|
||||
$query->setParameter(1, 'jwage');
|
||||
$numArticles = $query->getResult(Query::HYDRATE_SCALAR);
|
||||
|
||||
You can use the `getSingleScalarResult()` shortcut as well:
|
||||
|
||||
[php]
|
||||
$numArticles = $query->getSingleScalarResult();
|
||||
|
||||
+++ Iterating Large Resultsets
|
||||
|
||||
There are situations when a query you want to execute returns a very large result-set that needs
|
||||
@ -794,10 +942,3 @@ QUANTIFIED/BETWEEN/COMPARISON/LIKE/NULL/EXISTS
|
||||
"TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" |
|
||||
"LOWER" "(" StringPrimary ")" |
|
||||
"UPPER" "(" StringPrimary ")"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user