Ver código fonte

[translatable] translation query hint support for subselects

Gediminas Morkevicius 14 anos atrás
pai
commit
4c697a99a3

+ 19 - 1
doc/translatable.md

@@ -410,9 +410,27 @@ Now enough talking, here is an example:
     $articles = $query->getResult(); // object hydration
     $articles = $query->getArrayResult(); // array hydration
 
+And even a subselect:
+
+    $subSelect = "SELECT a2.id FROM Article a2 "
+        . "WHERE a2.title LIKE '%something_translated%'";
+    $dql = "SELECT a, c, u FROM Article a "
+        . "LEFT JOIN a.comments c "
+        . "JOIN c.author u "
+        . "WHERE a.id IN ({$subSelect}) "
+        . "ORDER BY a.title";
+    
+    $query = $em->createQuery($dql);
+    $query->setHint(
+        \Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER,
+        'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
+    );
+    
+    $articles = $query->getResult(); // object hydration
+
 Theres no need for any words anymore.. right?
 I recommend you to use it extensively since it is a way better performance, even in
-cases where you need a single object query.
+cases where you need to load single translated entity.
 
 Notice: Even in **COUNT** select statements translations are joined to leave a
 possibility to filter by translated field, if you do not need it, just do not set

+ 51 - 6
lib/Gedmo/Translatable/Query/TreeWalker/TranslationWalker.php

@@ -94,9 +94,16 @@ class TranslationWalker extends SqlWalker
      * Translation joins on current query
      * components
      *
-     * @var string
+     * @var array
+     */
+    private $translationJoinSql = array();
+
+    /**
+     * Processed subselect number
+     *
+     * @var integer
      */
-    private $translationJoinSql;
+    private $processedNestingLevel = 0;
 
     /**
      * {@inheritDoc}
@@ -197,7 +204,7 @@ class TranslationWalker extends SqlWalker
     public function walkFromClause($fromClause)
     {
         $result = parent::walkFromClause($fromClause);
-        $result .= $this->translationJoinSql;
+        $result .= $this->translationJoinSql[0];
         return $result;
     }
 
@@ -240,6 +247,41 @@ class TranslationWalker extends SqlWalker
         );
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    public function walkSubselect($subselect)
+    {
+        $this->processedNestingLevel++;
+        $result = parent::walkSubselect($subselect);
+        return $result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function walkSubselectFromClause($subselectFromClause)
+    {
+        $result = parent::walkSubselectFromClause($subselectFromClause);
+        if (isset($this->translationJoinSql[$this->processedNestingLevel])) {
+            $result .= $this->translationJoinSql[$this->processedNestingLevel];
+        }
+        return $result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function walkSimpleSelectClause($simpleSelectClause)
+    {
+        $result = parent::walkSimpleSelectClause($simpleSelectClause);
+        return str_replace(
+            array_keys($this->replacements),
+            array_values($this->replacements),
+            $result
+        );
+    }
+
     /**
      * Creates a left join list for translations
      * on used query components
@@ -249,12 +291,15 @@ class TranslationWalker extends SqlWalker
      */
     private function getTranslationJoinsSql()
     {
-        $result = '';
+        $result = array();
         $em = $this->getEntityManager();
         $ea = new TranslatableEventAdapter;
         $locale = $this->listener->getListenerLocale();
 
         foreach ($this->translatedComponents as $dqlAlias => $comp) {
+            if (!isset($result[$comp['nestingLevel']])) {
+                $result[$comp['nestingLevel']] = '';
+            }
             $meta = $comp['metadata'];
             $config = $this->listener->getConfiguration($em, $meta->name);
             $transClass = $this->listener->getTranslationClass($ea, $meta->name);
@@ -276,7 +321,7 @@ class TranslationWalker extends SqlWalker
                 $colAlias = $this->getSQLColumnAlias($colName);
                 $sql .= ' AND '.$tblAlias.'.'.$transMeta->getQuotedColumnName('foreignKey', $this->platform)
                     .' = '.$compTblAlias.'.'.$colName;
-                $result .= $sql;
+                $result[$comp['nestingLevel']] .= $sql;
                 $this->replacements[$compTblAlias.'.'.$meta->getQuotedColumnName($field, $this->platform)]
                     = $tblAlias.'.'.$transMeta->getQuotedColumnName('content', $this->platform);
             }
@@ -342,4 +387,4 @@ class TranslationWalker extends SqlWalker
         }
         return $translationListener;
     }
-}
+}

+ 39 - 9
tests/Gedmo/Translatable/TranslationQueryWalkerTest.php

@@ -36,23 +36,53 @@ class TranslationQueryWalkerTest extends BaseTestCaseORM
         $this->translationListener->setTranslatableLocale('en_us');
         $evm->addEventSubscriber($this->translationListener);
 
-        $conn = array(
-            'driver' => 'pdo_mysql',
-            'host' => '127.0.0.1',
-            'dbname' => 'test',
-            'user' => 'root',
-            'password' => 'nimda'
-        );
-        //$this->getMockCustomEntityManager($conn, $evm);
         $this->getMockSqliteEntityManager($evm);
         $this->populate();
     }
 
+    public function testSubselectByTranslatedField()
+    {
+        $this->populateMore();
+        $dql = 'SELECT a FROM ' . self::ARTICLE . ' a';
+        $subSelect = 'SELECT a2.title FROM ' . self::ARTICLE . ' a2';
+        $subSelect .= " WHERE a2.title LIKE '%ab%'";
+        $dql .= " WHERE a.title IN ({$subSelect})";
+        $dql .= ' ORDER BY a.title';
+        $q = $this->em->createQuery($dql);
+        $q->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, self::TREE_WALKER_TRANSLATION);
+
+        // array hydration
+        $this->translationListener->setTranslatableLocale('en_us');
+        $result = $q->getArrayResult();
+        $this->assertEquals(2, count($result));
+        $this->assertEquals('Alfabet', $result[0]['title']);
+        $this->assertEquals('Cabbages', $result[1]['title']);
+    }
+
+    public function testSubselectStatements()
+    {
+        $this->populateMore();
+        $dql = 'SELECT a FROM ' . self::ARTICLE . ' a';
+        $subSelect = 'SELECT a2.id FROM ' . self::ARTICLE . ' a2';
+        $subSelect .= " WHERE a2.title LIKE '%ab%'";
+        $dql .= " WHERE a.id IN ({$subSelect})";
+        $dql .= ' ORDER BY a.title';
+        $q = $this->em->createQuery($dql);
+        $q->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, self::TREE_WALKER_TRANSLATION);
+
+        // array hydration
+        $this->translationListener->setTranslatableLocale('en_us');
+        $result = $q->getArrayResult();
+        $this->assertEquals(2, count($result));
+        $this->assertEquals('Alfabet', $result[0]['title']);
+        $this->assertEquals('Cabbages', $result[1]['title']);
+    }
+
     public function testJoinedWithStatements()
     {
         $this->populateMore();
         $dql = 'SELECT a, c FROM ' . self::ARTICLE . ' a';
-        //@todo: its imposible to support translated values in WITH statement
+        //@todo: its impossible to support translated values in WITH statement
         $dql .= ' LEFT JOIN a.comments c WITH c.subject LIKE :lookup';
         $dql .= ' WHERE a.title LIKE :filter';
         $dql .= ' ORDER BY a.title';