2012-12-31 13 views
18

Mam tabelę dla moich kategorii, każda kategoria mają i id, nazwa i parent_id.Konwersja MySQL na Doctrine Query Builder. Problemy z IF i CONCAT. Lub inne podejście do podzapytań na wybierz

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 

Chcę, aby przekształcić go do mojego 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; 

czymś takim, ale nie mogę użyć IF i CONCAT.

Odpowiedz

45

Ok Znalazłem rozwiązanie.

Można użyć CASE zamiast IF. To sprawdzić, ale kiedy używam CASE nie mogę CONCAT moje pola:

$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(); 

Innym rozwiązaniem jest utworzenie własnej funkcji DQL, jak IF i używać go tak:

$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(); 

Dla utwórz to, JEŚLI możesz przejść do tego linku i uczyć się: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#adding-your-own-functions-to-the-dql-language

Opublikuję tutaj moją klasę dla tego IF i config.yml, aby pomóc innym ludziom. Oto klasa IfFunction (mam, że od 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])); 
    } 
} 

Potem trzeba zaktualizować config.yml takiego (tylko dodaje ostatnie 3 linie):

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 

Thanks

+2

z najnowszą wersją symfony - węzeł dql musi znajdować się poniżej entity_manager – Harold

+0

Fantastic! zadziałało, The case statement. –

0

Zakładając, że masz relację o nazwie "rodzic" w kategorii, to działa lepiej? :

$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

Dzięki za napiwek, ten sposób też zadziała. Ale nie wiem, nie mogę użyć 'IF' na' addSelect'. Otrzymuję ten błąd: '[Błąd składni] wiersz 0, kolumna 13: Błąd: Oczekiwana znana funkcja, otrzymała 'IF'' –

1

można również połączyć CASE i 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) 
    ... ;