Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Doctrine: how to add custom functions that take optional parameter?

Following the official explanation, I want to create my custom MySQL function ROUND() that can take (not mandatory) another second argument.

Sor far I've done this:

<?php
namespace HQF\Bundle\PizzasBundle\DQL;

use \Doctrine\ORM\Query\AST\Functions\FunctionNode;
use \Doctrine\ORM\Query\Lexer;

class MysqlRound extends FunctionNode
{
    public $simpleArithmeticExpression;

    public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
    {
        return 'ROUND(' . $sqlWalker->walkSimpleArithmeticExpression(
            $this->simpleArithmeticExpression
        ) . ')';
    }

    public function parse(\Doctrine\ORM\Query\Parser $parser)
    {
        $lexer = $parser->getLexer();

        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);

        $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();

        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    }
}

But how to implement the fact that ROUND(XX) is ok, and ROUND(XX, YY) is ok too?

like image 293
Olivier Pons Avatar asked Feb 26 '13 12:02

Olivier Pons


2 Answers

You need to declare a second argument, and use the Lexer like this:

namespace HQF\Bundle\PizzasBundle\DQL;

use \Doctrine\ORM\Query\AST\Functions\FunctionNode;
use \Doctrine\ORM\Query\Lexer;

class MysqlRound extends FunctionNode
{
    private $firstExpression = null;
    private $secondExpression = null;

    public function parse(\Doctrine\ORM\Query\Parser $parser)
    {
        $lexer = $parser->getLexer();
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);
        $this->firstExpression = $parser->ArithmeticPrimary();

        // parse second parameter if available
        if(Lexer::T_COMMA === $lexer->lookahead['type']){
            $parser->match(Lexer::T_COMMA);
            $this->secondExpression = $parser->ArithmeticPrimary();
        }

        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    }

    public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
    {
        // use second parameter if parsed
        if (null !== $this->secondExpression){
            return 'ROUND(' 
                . $this->firstExpression->dispatch($sqlWalker)
                . ', '
                . $this->secondExpression->dispatch($sqlWalker)
                . ')';
        }

        return 'ROUND(' . $this->firstExpression->dispatch($sqlWalker) . ')';
    }
}

Edit

Many Doctrine2 extensions have been written here. All credits to @beberlei for his great work. Many functions are available (IFELSE, IFNULL, NULLIF, COS, ACOS, etc...) but not all of them (ROUND, GREATEST or LEAST are missing but you can still write them yourself if you need them.)

like image 110
Mick Avatar answered Sep 20 '22 03:09

Mick


I also found GREATEST implementation here https://raw.githubusercontent.com/rodgermd/mura-show.com/master/src/Rodger/GalleryBundle/DoctrineExtension/Greatest.php (c) rodgermd@github :

namespace Rodger\GalleryBundle\DoctrineExtension;

use Doctrine\ORM\Query\Lexer,
    Doctrine\ORM\Query\AST\Functions;


class Greatest extends Functions\FunctionNode {

  protected $firstExpression, $secondExpression;

  public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
  {
    return sprintf("GREATEST(%s, %s)", 
            $this->firstExpression->dispatch($sqlWalker), 
            $this->secondExpression->dispatch($sqlWalker));
  }

  public function parse(\Doctrine\ORM\Query\Parser $parser)
    {
        $parser->match(Lexer::T_IDENTIFIER); // (2)
        $parser->match(Lexer::T_OPEN_PARENTHESIS); // (3)
        $this->firstExpression = $parser->ArithmeticPrimary(); // (4)
        $parser->match(Lexer::T_COMMA); // (5)
        $this->secondExpression = $parser->ArithmeticPrimary(); // (6)
        $parser->match(Lexer::T_CLOSE_PARENTHESIS); // (3)
    }
}
like image 39
gorodezkiy Avatar answered Sep 24 '22 03:09

gorodezkiy