I'm building an app using Symfony2 framework and using Doctrine ORM. I have a table with airlines for which some IATA codes are missing. I'm outputting a list, ordered by this IATA code, but I'm getting the undesirable result that the records with null IATA codes are sorted at the top.
In MySQL this is simple enough to do, with ORDER BY ISNULL(code_iata), code_iata
but I'm clueless as to what the equivalent would be for DQL. I tried
$er->createQueryBuilder('airline')->orderBy('ISNULL(airline.codeIata), airline.codeIata', 'ASC')
but this gives me a syntax error.
The Doctrine docs give me no answer either. Is there a way?
You can use the following trick in DQL to order NULL values last
The
HIDDEN
keyword (available since Doctrine 2.2) will result in omitting theinverseWeight
field from the result set and thus preventing undesirable mixed results.(The sort fields value is inverted therefore the order has to be inverted too, that's why the query uses
DESC
order, notASC
.)Credits belong to this answer.
The most unobtrusive generic solution would be to use the
CASE
expression in combination with theHIDDEN
keyword.Works with both numeric as well as other field types and doesn't require extending Doctrine.
Here it is an example for a custom walker to get exactly what you want. I have taken it from Doctrine in its github issues:
https://github.com/doctrine/doctrine2/pull/100
But the code as it is there didn't work for me in MySQL. I have modified it to work in MySQL, but I haven't test at all for other engines.
Put following walker class for example in
YourNS\Doctrine\Waler\
directory;Then:
If you want to do something similar to "NULLS LAST" in SQL (with PostgreSQL in my case):
You can use the COALESCE function with the Doctrine Query Builder (HIDDEN will hide the field "freq" on your query result set).
By default, MySQL will still sort a
NULL
value; it will just place it at the beginning of the result set if it was sortedASC
, and at the end if it was sortedDESC
. Here, you're looking to sortASC
, but you want theNULL
values to be at the bottom.Unfortunately, as powerful as it is, Doctrine isn't going to offer much support here, since function support is limited, and most of it is limited to
SELECT
,WHERE
, andHAVING
clauses. You actually wouldn't have a problem at all if any of the following were true about the QueryBuilder:select()
acceptedISNULL()
orderBy()
oraddOrderBy()
supportedISNULL()
UNION
s (with this, you could run two queries: one where thecodeIata
wasNULL
, and one where it wasn't, and you could sort each independently)So that said, you can go with the user-defined functions that ArtWorkAD mentioned already, or you could replicate that last point with two different Doctrine queries:
Then you can combine these into a single array, or treat them independently in your templates.
Another idea is to have DQL return everything in one data set, and let PHP do the heavy lifting. Something like:
The downside to both of these is that you won't be able to do
LIMIT
s in MySQL, so it might only work well for relatively small data sets.Anyway, hope this gets you on your way!
DQL does not contain every function of plain SQL. Fortunately you can define your custom DQL method to accomplish this.
Some resources:
http://punkave.com/window/2012/07/24/for-the-php-crowd-adding-custom-functions-to-doctrine-2-dql
http://docs.doctrine-project.org/en/2.1/cookbook/dql-user-defined-functions.html
http://symfony.com/doc/2.0/cookbook/doctrine/custom_dql_functions.html