1
0
mirror of synced 2024-12-15 15:46:02 +03:00

Adding a little more information to the dql chapter.

This commit is contained in:
Jonathan H. Wage 2010-05-14 15:23:51 -04:00
parent 5a79963bd2
commit f99a096d01

View File

@ -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. DQL is case in-sensitive, except for namespace, class and field names, which are case sensitive.
++ Types of DQL queries ++ Types of DQL queries
DQL as a query language has SELECT, UPDATE and DELETE constructs that map to their corresponding 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. consistency of your object model.
DQL SELECT statements are a very powerful way of retrieving parts of your domain model that are 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 in one single sql select statement which can make a huge difference in performance in contrast
to using several queries. 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: Here is an example that selects all users with an age > 20:
[sql] [php]
SELECT u FROM MyProject\Model\User u WHERE u.age > 20 $query = $em->createQuery('SELECT u FROM MyProject\Model\User u WHERE u.age > 20');
$users = $query->getResult();
Lets examine the query: 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`. 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 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 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. on more information.
+++ Joins +++ Joins
@ -69,21 +69,23 @@ Example:
Regular join of the address: Regular join of the address:
[sql] [php]
SELECT u FROM User u JOIN u.address a WHERE a.city = 'Berlin' $query = $em->createQuery("SELECT u FROM User u JOIN u.address a WHERE a.city = 'Berlin'");
$users = $query->getResult();
Fetch join of the address: Fetch join of the address:
[sql] [php]
SELECT u, a FROM User u JOIN u.address a WHERE a.city = 'Berlin' $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 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 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 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** > **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. > 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 > 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 > 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 +++ 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. The actual result also depends on the hydration mode.
[sql] Hydrate all User entities:
-- Hydrate all Users
SELECT u FROM MyProject\Model\User u
-- Retrieve the IDs of all CmsUsers [php]
SELECT u.id FROM CmsUser u $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 Retrieve the IDs of all CmsUsers:
SELECT DISTINCT a.user.id FROM CmsArticle a
-- Retrieve all articles and sort them by the name of the articles users instance [php]
SELECT a FROM CmsArticle a ORDER BY a.user.name ASC $query = $em->createQuery('SELECT u.id FROM CmsUser u');
$ids = $query->getResult(); // array of CmsUser ids
-- Retrieve the Username and Name of a CmsUser Retrieve the IDs of all users that have written an article:
SELECT u.username, u.name FROM CmsUser u
-- Retrieve a ForumUser and his single associated entity [php]
SELECT u, a FROM ForumUser u JOIN u.avatar a $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 Retrieve all articles and sort them by the name of the articles users instance:
SELECT u, p FROM CmsUser u JOIN u.phonenumbers p
-- Hydrate a result in Ascending or Descending Order [php]
SELECT u FROM ForumUser u ORDER BY u.id ASC $query = $em->createQuery('SELECT a FROM CmsArticle a ORDER BY a.user.name ASC');
SELECT u FROM ForumUser u ORDER BY u.id DESC $articles = $query->getResult(); // array of CmsArticle objects
-- Using Aggregate Functions Retrieve the Username and Name of a CmsUser:
SELECT COUNT(u.id) FROM CmsUser u GROUP BY u.id
-- With WHERE Clause and Positional Parameter [php]
SELECT u FROM ForumUser u WHERE u.id = ?1 $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 Retrieve a ForumUser and his single associated entity:
SELECT u FROM ForumUser u WHERE u.username = :name
-- With Nested Conditions in WHERE Clause [php]
SELECT u from ForumUser u WHERE (u.username = :name OR u.username = :name2) AND u.id = :id $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 Retrieve a CmsUser and fetch join all the phonenumbers he has:
SELECT COUNT(DISTINCT u.name) FROM CmsUser
-- With Arithmetic Expression in WHERE clause [php]
SELECT u FROM CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000 $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) Hydrate a result in Ascending:
SELECT u, a FROM CmsUser u, CmsArticle a WHERE u.id = a.user.id
-- Using a LEFT JOIN to hydrate all user-ids and optionally associated article-ids [php]
SELECT u.id, a.id FROM CmsUser u LEFT JOIN u.articles a $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 Or in Descending Order:
SELECT u FROM CmsUser u LEFT JOIN u.articles a WITH a.topic LIKE '%foo%'
-- Using several Fetch JOINs [php]
SELECT u, a, p, c FROM CmsUser u $query = $em->createQuery('SELECT u FROM ForumUser u ORDER BY u.id DESC');
JOIN u.articles a $users = $query->getResult(); // array of ForumUser objects
JOIN u.phonenumbers p
JOIN a.comments c
-- BETWEEN in WHERE clause Using Aggregate Functions:
SELECT u.name FROM CmsUser u WHERE u.id BETWEEN ?1 AND ?2
-- DQL Functions in WHERE clause [php]
SELECT u.name FROM CmsUser u WHERE TRIM(u.name) = 'someone' $query = $em->createQuery('SELECT COUNT(u.id) FROM Entities\User u');
$count = $query->getSingleScalarResult();
-- IN() Expression With WHERE Clause and Positional Parameter:
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)
-- CONCAT() DQL Function [php]
SELECT u.id FROM CmsUser u WHERE CONCAT(u.name, 's') = ?1 $query = $em->createQuery('SELECT u FROM ForumUser u WHERE u.id = ?1');
SELECT CONCAT(u.id, u.name) FROM CmsUser u WHERE u.id = ?1 $users = $query->getResult(); // array of ForumUser objects
-- EXISTS in WHERE clause with correlated Subquery With WHERE Clause and Named Parameter:
SELECT u.id FROM CmsUser u
WHERE EXISTS (SELECT p.phonenumber FROM CmsPhonenumber p WHERE p.phonenumber = u.id)
-- Get all users who are members of $group. [php]
SELECT u.id FROM CmsUser u WHERE :param MEMBER OF u.groups $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 With Nested Conditions in WHERE Clause:
SELECT u FROM CmsUser u WHERE SIZE(u.phonenumbers) > 1
-- Get all users that have no phonenumber [php]
SELECT u FROM CmsUser u WHERE u.phonenumbers IS EMPTY $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 +++ 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 = $em->createQuery();
$q->setDql('select u from MyProject\Model\User u'); $q->setDql('select u from MyProject\Model\User u');
+++ Query Result Formats +++ 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: 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. 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 user land. 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: 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
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 ++++ 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: 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. 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 +++ Iterating Large Resultsets
There are situations when a query you want to execute returns a very large result-set that needs 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 ")" | "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" |
"LOWER" "(" StringPrimary ")" | "LOWER" "(" StringPrimary ")" |
"UPPER" "(" StringPrimary ")" "UPPER" "(" StringPrimary ")"