2014-05-21 12 views
2

基本上我想作出這樣的查詢:Symfony2的原則 - 凡作爲對

SELECT * FROM `table` WHERE (`c1`,`c2`) in (('a','b'),('c','d')) 

我嘗試了一些事情,但我找不到任何建議如何實現這一目標。我嘗試了一些類似的東西:

foreach ($pairs as $pair) { 
    $columns[] = "(" . $pair->getc1() . "," . $pair->getc2() . ")"; 
} 

$query = $this->entityManager->getRepository('AcmeTestBundle:Combinations')->createQueryBuilder('c') 
       ->select() 
       ->where('(c.c1,c.c2) in (:pairs)') 
       ->setParameter('pairs', $columns) 
       ->getQuery(); 

這顯然並沒有爲我工作,你有什麼想法如何在where in語句中使用多個值嗎?

編輯:

我設法做這種噁心的方式。我懷疑這是最好的方式,我仍然認爲應該有更好的解決方案,所以我很樂意看到它。

$query = $this->entityManager 
    ->getRepository('AcmeTestBundle:Combinations') 
    ->createQueryBuilder('c') 
    ->select(); 

$expr = $query->expr(); 

$i = 1; 
foreach ($pairs as $pair) { 
    $query = $query->orWhere($expr->andX($expr->eq('c.c1', ':c1' . $i), $expr->eq('c.c2', ':c2' . $i))) 
      ->setParameter('c1' . $i, $pair->getc1()) 
      ->setParameter('c2' . $i, $pair->getc2()) 
    ; 

    $i++; 
} 

$query = $query->getQuery(); 
+0

不幸的是,你不能有'WHERE(COL1,COL2)'教義......不過,我能想象的延伸,這將是在教義的自定義功能。它可能類似於'PAIRS((col1,col2),(val1_1,val2_1),(val1_2,val2_2),(val1_N,val2_N))',最終會轉變爲正常的原始sql。問題是驗證輸入會很棘手。 –

+0

該死的:(謝謝你的信息,至少我不會再尋找這個 –

+0

請檢查我的答案,也許它可以以某種方式爲你工作 –

回答

1

我發現了一些你絕對不會喜歡的解決方案,但是它能以某種方式工作。

有三個問題基本上是: 1.你不能有WHERE(COL1,COL2)在教義 2.用戶不能傳遞數組的數組作爲參數(建立對) 3.不能有一個其中表達沒有比較。

但請檢查我的解決方法。

擴展類:

/* 
* This file is part of the Acme package. 
* 
* (c) Denis V. 
* 
* For the full copyright and license information, please view the LICENSE 
* file that was distributed with this source code. 
*/ 

namespace Acme\Bundle\AcmeBundle\Query; 

use Doctrine\ORM\Query\AST\Functions\FunctionNode, 
    Doctrine\ORM\Query\Lexer, 
    Doctrine\ORM\Query\Parser, 
    Doctrine\ORM\Query\SqlWalker; 

/** 
* Class Pairs 
* 
* @author Denis V. 
* 
* @package Acme\Bundle\AcmeBundle\Query 
*/ 
class Pairs extends FunctionNode 
{ 
    public $cols = array(); 
    public $pathExp = array(); 

    /** 
    * {@inheritdoc} 
    */ 
    public function parse(Parser $parser) 
    { 
     $parser->match(Lexer::T_IDENTIFIER); 
     $parser->match(Lexer::T_OPEN_PARENTHESIS); 

     $lexer = $parser->getLexer(); 

     // first Path Expression is mandatory 
     $this->pathExp = array(); 
     $parser->match(Lexer::T_OPEN_PARENTHESIS); 
     $this->cols[] = $parser->SingleValuedPathExpression(); 
     while ($lexer->isNextToken(Lexer::T_COMMA)) { 
      $parser->match(Lexer::T_COMMA); 
      $this->cols[] = $parser->SingleValuedPathExpression(); 
     } 
     $parser->match(Lexer::T_CLOSE_PARENTHESIS); 

     $j = 1; 
     while ($lexer->isNextToken(Lexer::T_COMMA)) { 
      $parser->match(Lexer::T_COMMA); 
      $parser->match(Lexer::T_OPEN_PARENTHESIS); 
      $this->pathExp[$j] = array($parser->StringPrimary()); 
      while ($lexer->isNextToken(Lexer::T_COMMA)) { 
       $parser->match(Lexer::T_COMMA); 
       $this->pathExp[$j][] = $parser->StringPrimary(); 
      } 

      // commented out as it should be possible to accept array parameters 
      //if (count($this->cols) != count($this->pathExp[$j])) { 
      // $parser->semanticalError('Number of columns must be the same as number of compared values'); 
      //} 

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

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

    /** 
    * {@inheritdoc} 
    */ 
    public function getSql(SqlWalker $sqlWalker) 
    { 
     $fields = array(); 
     foreach ($this->cols as $col) { 
      $fields[] = $col->dispatch($sqlWalker); 
     } 

     $result = sprintf('(%s)', implode(', ', $fields)); 

     $result .= ' IN '; 

     $expr = array(); 
     foreach ($this->pathExp as $pathExp) { 
      $fields = array(); 
      foreach ($pathExp as $item) { 
       $fields[] = $item->dispatch($sqlWalker); 
      } 
      $expr[] = sprintf('(%s)', implode(', ', $fields)); 
     } 

     $result .= sprintf('(%s)', implode(', ', $expr)); 

     return $result; 
    } 

} 

現在你可以做以下(假設你是在實體庫):

$qb = $this 
     ->createQueryBuilder('c') 
     ->where('PAIRS((c.c1, c.c2), (:pair)) != \'dummy\'') 
     ->setParameter('pair', array('val1', 'val2')); 
    $q = $qb->getQuery(); 

    return $q; 

正如你可能會注意到,在代碼!= 'dummy'。這是因爲不經過比較就無法傳遞WHERE表達式。我檢查了一下,它實際上是以MySQL的方式工作的。但是你可以使用自定義的SqlWalker來刪除它。

要傳遞更多對,您將需要使用循環,因爲您無法將數組數組作爲單個參數傳遞。

事情是這樣的:

$dqlParts = array(); 
foreach ($pairs as $i => $pair) { 
    $dqlParts[] = sprintf('(?%d)', $i); 
    $qb->setParameter($i, $pair); 
} 
$dqlPartsStr = implode(', ', $dqlParts); 

然後用這個$ dqlPartsStr在DQL(注:最後一個代碼塊在計算器上寫​​未經測試)。

我知道,這看起來更像是一個黑客,但這是你可以在Doctrine2中做的所有事情。請讓我知道,如果你有任何問題。

+0

即使它不是我正在尋找的東西我分配指向你,因爲你做了最多的研究,謝謝。 –

+0

@KeluThatsall謝謝你的賞金。至於你不是在尋找什麼,好吧,似乎唯一的區別是這個技巧與'虛擬',否則DQL ('val1.1','val2.2'),('val.2','val2.2'),('val1.N')被轉換爲相當不錯的配對SQL:WHERE(col1,col2)IN ','val2.N'))''這只是與'虛擬'(沒有副作用)進行比較。就像我提到的,這個'虛擬'字符串可以很容易地通過自定義SqlWalker去除。無論如何,再次感謝賞金;) –

+0

aye,但tbh我希望能夠使用不同數量的參數,不僅雙:PI希望有一個解決方案已經納入了教義,但我只是沒有找到它。最後,我建立了這個查詢,甚至沒有使用QueryBuilder - 不值得使用IMo;) –

0

這也許可以嘗試:

$separator = '|||'; 
$columns = []; 
foreach ($pairs as $pair) { 
    $columns[] = $pair->getc1() . $separator . $pair->getc2(); 
} 
$query = $this 
    ->entityManager 
    ->getRepository('AcmeTestBundle:Combinations') 
    ->createQueryBuilder('c') 
    ->select() 
    ->where("CONCAT_WS('" . $separator . "', c.c1, c.c2) in (:pairs)") 
    ->setParameter('pairs', $columns) 
    ->getQuery(); 
+1

你檢查它,或者只是猜測? –

-1

你試過指定者()?

像這樣:

->select() 
->where('(c.c1,c.c2) in (:pairs)') 
->setParameter('pairs', $columns->toArray()) 
->getQuery(); 
+1

這將如何工作? '$ columns'已經是一個數組了,它沒有任何方法,它不是一個對象 –