TIL that Doctrine 2 doesn't support LIMIT
s within subqueries which can be frustrating. In my case, I wanted to LEFT JOIN
on a table using a subquery with a single result - something like this:
$dqb->from('MyAppBundle:Foo', 'foo')
->leftJoin('foo.bar', 'bar', 'WITH', 'bar = (SELECT b FROM MyAppBundle:Bar b WHERE b.foo = foo AND b.published_date >= :now ORDER BY t.startDate LIMIT 1)');
But Doctrine kept throwing this error:
Expected Doctrine\ORM\Query\Lexer::T_CLOSE_PARENTHESIS, got 'LIMIT'
To get around this limitation, I ended creating a custom Doctrine function FIRST()
. Basically, you wrap a subquery within this function and Doctrine will apply LIMIT 1
for you:
$dqb->from('MyAppBundle:Foo', 'foo')
->leftJoin('foo.bar', 'bar', 'WITH', 'bar = FIRST(SELECT b FROM MyAppBundle:Bar b WHERE b.foo = foo AND b.published_date >= :now ORDER BY t.startDate)');
Here's what the EBNF looks like:
FirstFunction ::= "FIRST" "(" Subselect ")"
And here's the class I implemented:
<?php
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\Subselect;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* FirstFunction ::=
* "FIRST" "(" Subselect ")"
*/
class FirstFunction extends FunctionNode
{
/**
* @var Subselect
*/
private $subselect;
/**
* {@inheritdoc}
*/
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->subselect = $parser->Subselect();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
/**
* {@inheritdoc}
*/
public function getSql(SqlWalker $sqlWalker)
{
return '(' . $this->subselect->dispatch($sqlWalker) . ' LIMIT 1)';
}
}
If you're using Symfony, you can tell Doctrine about this new function by setting the following in your app/config/config.yml
file:
doctrine:
# ...
orm:
# ...
dql:
string_functions:
FIRST: AppBundle\DBAL\FirstFunction # Your namespace might be different
It should be possible to extend this to make the LIMIT
configurable - that exercise is left to the reader.
Enjoy this article?
Support my open-source work via Github or follow me on Twitter for more blog posts and other interesting articles from around the web. I'd also love to hear your thoughts on this post - simply drop a comment below!