2012-12-31 1 views
18

Ho una tabella per le mie categorie, ogni categoria ha e id, name e parent_id.Conversione di MySQL in Doctrine Query Builder. Problemi con IF e CONCAT. O un altro approccio per le subquery su select

Select IF(a.parent_id IS NULL, a.name, CONCAT((SELECT b.name FROM category b WHERE b.id = a.parent_id), "/", a.name)) as n, a.id, a.parent_id 
FROM category a 
ORDER BY n 

voglio convertirlo al mio Doctrine2 Query Builder

$em = $this->getDoctrine()->getEntityManager(); 
    $qb = $em->createQueryBuilder(); 
    $q = $qb 
      ->select("c.id") 
      ->addSelect(
       "IF(c.parent_id IS NULL, c.name, CONCAT((" . 
       $em->createQueryBuilder() 
        ->select("t.name") 
        ->from("MyBundle:Category", "t") 
        ->getQuery()->getDQL() . 
       "), \"/\", c.name))" 
      ) 
      ->from("MyBundle:Category", "c"); 
    echo $q->getQuery()->getSQL(); 
    exit; 

Qualcosa del genere, ma non posso usare il IF e CONCAT.

risposta

45

Ok ho trovato la soluzione.

È possibile utilizzare CASE anziché IF. Check this out, ma quando sto usando CASE posso non CONCAT miei campi:

$em = $this->getDoctrine()->getEntityManager(); 
$qb = $em->createQueryBuilder(); 
$q = $qb 
     ->select("c.id") 
     ->addSelect("CASE WHEN (c.parent IS NULL) THEN c.name ELSE 'something' END") 
     ->from("MyBundle:Category", "c") 
     ->leftJoin("c.parent", "t"); 

echo $q->getQuery()->getSQL(); 

Un'altra soluzione è quella di creare la propria funzione DQL, come IF e usarlo in questo modo:

$em = $this->getDoctrine()->getEntityManager(); 
$qb = $em->createQueryBuilder(); 
$q = $qb 
     ->select("c.id") 
     ->addSelect("IF(c.parent IS NULL, c.name, CONCAT(CONCAT(t.name, '/'), c.name))") 
     ->from("MyBundle:Category", "c") 
     ->leftJoin("c.parent", "t"); 

echo $q->getQuery()->getSQL(); 

Per crea questo SE puoi andare a questo link e imparare: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#adding-your-own-functions-to-the-dql-language

Inserirò qui la mia classe per questo IF e il config.yml per aiutare le altre persone. Qui è la classe IfFunction (ho ottenuto che dal https://github.com/beberlei/DoctrineExtensions/blob/master/src/Query/Mysql/IfElse.php):

<?php 
namespace MyName\MiscBundle\Doctrine\ORM\Query\AST\Functions; 

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

/** 
* Usage: IF(expr1, expr2, expr3) 
* 
* If expr1 is TRUE (expr1 <> 0 and expr1 <> NULL) then IF() returns expr2; 
* otherwise it returns expr3. IF() returns a numeric or string value, 
* depending on the context in which it is used. 
* 
* @author Andrew Mackrodt <[email protected]> 
* @version 2011.06.19 
*/ 
class IfFunction extends FunctionNode 
{ 
    private $expr = array(); 

    public function parse(\Doctrine\ORM\Query\Parser $parser) 
    { 
     $parser->match(Lexer::T_IDENTIFIER); 
     $parser->match(Lexer::T_OPEN_PARENTHESIS); 
     $this->expr[] = $parser->ConditionalExpression(); 

     for ($i = 0; $i < 2; $i++) 
     { 
      $parser->match(Lexer::T_COMMA); 
      $this->expr[] = $parser->ArithmeticExpression(); 
     } 

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

    public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) 
    { 
     return sprintf('IF(%s, %s, %s)', 
      $sqlWalker->walkConditionalExpression($this->expr[0]), 
      $sqlWalker->walkArithmeticPrimary($this->expr[1]), 
      $sqlWalker->walkArithmeticPrimary($this->expr[2])); 
    } 
} 

Dopo di che è necessario aggiornare il config.yml come questo (appena aggiunto le ultime 3 righe):

doctrine: 
    dbal: 
     driver: "%database_driver%" 
     host:  "%database_host%" 
     port:  "%database_port%" 
     dbname: "%database_name%" 
     user:  "%database_user%" 
     password: "%database_password%" 
     charset: UTF8 

    orm: 
     auto_generate_proxy_classes: "%kernel.debug%" 
     auto_mapping: true 
     dql: #ADDED THIS LINE 
      string_functions: #ADDED THIS LINE 
       IF: MyName\MiscBundle\Doctrine\ORM\Query\AST\Functions\IfFunction #ADDED THIS LINE 

Grazie

+2

con la versione più recente di symfony - il nodo dql deve essere sotto l'entity_manager – Harold

+0

Fantastic! ha funzionato, la dichiarazione del caso. –

0

Supponendo di avere una relazione denominata "genitore" nella categoria, funziona meglio? :

$em = $this->getDoctrine()->getEntityManager(); 
$qb = $em->createQueryBuilder(); 
$q = $qb 
     ->select("c.id") 
    ->addSelect("IF (c.parent_id IS NULL, c.name, CONCAT(t.name, '/', c.name))" 
     ->from("MyBundle:Category", "c") 
    ->leftJoin("c.parent t")); 

echo $q->getQuery()->getSQL(); 
+0

Grazie per il suggerimento, anche in questo modo funzionerà. Ma non so che non posso usare il 'SE' su' addSelect'. Ottengo questo errore: '[Errore di sintassi] riga 0, colonna 13: Errore: funzione conosciuta prevista, ottenuto 'IF'' –

1

È possibile anche combinare CASE e CONCAT:

$q = $this->createQueryBuilder('a') 
    ->select('a.id') 
    ->addSelect('CASE WHEN(a.parent IS NULL) THEN \'\' else CONCAT(:variable, a.name, \'string\') END as name') 
    ->setParameter('variable', $variable) 
    ... ;