2012-12-31 16 views
18

Tôi có một bảng cho các danh mục của tôi, mỗi danh mục có và id, tên và parent_id.Chuyển đổi MySQL thành Trình tạo truy vấn tài nguyên. Các vấn đề với IF và CONCAT. Hoặc một cách tiếp cận khác cho truy vấn phụ trên một số lựa chọn

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 

Tôi muốn chuyển nó sang Doctrine2 Query Builder tôi

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

Something như thế, nhưng tôi không thể sử dụng IF, và CONCAT.

Trả lời

45

Ok tôi đã tìm được giải pháp.

Bạn có thể sử dụng CASE thay vì IF. Kiểm tra này ra, nhưng khi tôi đang sử dụng CASE tôi không thể CONCAT lĩnh vực của tôi:

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

Giải pháp khác là tạo ra chức năng DQL của riêng bạn, như IF và sử dụng nó như thế này:

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

Đối tạo NẾU này, bạn có thể truy cập liên kết này và tìm hiểu: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#adding-your-own-functions-to-the-dql-language

Tôi sẽ đăng ở đây lớp của mình cho IF này và config.yml để trợ giúp người khác. Đây là lớp IfFunction (Tôi đã nhận rằng từ 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])); 
    } 
} 

Sau đó bạn cần cập nhật config.yml bạn như thế này (chỉ cần thêm 3 dòng cuối):

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 

Cảm ơn

+2

với phiên bản mới nhất của symfony - nút dql phải nằm bên dưới entity_manager – Harold

+0

Tuyệt vời! nó hoạt động, Tuyên bố trường hợp. –

0

Giả sử bạn có mối quan hệ có tên "cha mẹ" trong Danh mục, điều này có hoạt động tốt hơn không? :

$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

Cảm ơn lời khuyên, cách này cũng sẽ hoạt động. Nhưng tôi không biết tôi không thể sử dụng 'IF' trên' addSelect'. Tôi nhận được lỗi này: '[Lỗi Cú pháp] dòng 0, col 13: Lỗi: Đã biết hàm đã biết, nhận 'IF'' –

1

Bạn cũng có thể kết hợp CASECONCAT:

$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) 
    ... ; 
Các vấn đề liên quan