Преглед на файлове

[translatable] translation walker adjustments to personal translations

gedi преди 13 години
родител
ревизия
bed115d163

+ 26 - 2
lib/Gedmo/Translatable/Mapping/Event/Adapter/ORM.php

@@ -2,12 +2,13 @@
 
 namespace Gedmo\Translatable\Mapping\Event\Adapter;
 
-use Gedmo\Mapping\Event\Adapter\ORM as BaseAdapterORM;
+use Doctrine\DBAL\Types\Type;
+use Doctrine\ORM\Proxy\Proxy;
 use Doctrine\ORM\Query;
 use Doctrine\ORM\Mapping\ClassMetadataInfo;
+use Gedmo\Mapping\Event\Adapter\ORM as BaseAdapterORM;
 use Gedmo\Translatable\Mapping\Event\TranslatableAdapter;
 use Gedmo\Tool\Wrapper\AbstractWrapper;
-use Doctrine\DBAL\Types\Type;
 
 /**
  * Doctrine event adapter for ORM adapted
@@ -105,6 +106,29 @@ final class ORM extends BaseAdapterORM implements TranslatableAdapter
     public function findTranslation(AbstractWrapper $wrapped, $locale, $field, $translationClass)
     {
         $em = $this->getObjectManager();
+        // first look in identityMap, will save one SELECT query
+        foreach ($em->getUnitOfWork()->getIdentityMap() as $className => $objects) {
+            if ($className === $translationClass) {
+                foreach ($objects as $trans) {
+                    $match = !$trans instanceof Proxy
+                        && $trans->getLocale() === $locale
+                        && $trans->getField() === $field
+                    ;
+                    if ($match) {
+                        if ($this->usesPersonalTranslation($translationClass)) {
+                            $match = $match && $trans->getObject() === $wrapped->getObject();
+                        } else {
+                            $match = $match && $trans->getForeignKey() === $wrapped->getIdentifier();
+                            $match = $match && $trans->getObjectClass() === $wrapped->getMetadata()->name;
+                        }
+                    }
+                    if ($match) {
+                        return $trans;
+                    }
+                }
+            }
+        }
+
         $qb = $em->createQueryBuilder();
         $qb->select('trans')
             ->from($translationClass, 'trans')

+ 11 - 5
lib/Gedmo/Translatable/Query/TreeWalker/TranslationWalker.php

@@ -264,6 +264,7 @@ class TranslationWalker extends SqlWalker
         }
         $em = $this->getEntityManager();
         $ea = new TranslatableEventAdapter;
+        $ea->setEntityManager($em);
         $joinStrategy = $q->getHint(TranslatableListener::HINT_INNER_JOIN) ? 'INNER' : 'LEFT';
 
         foreach ($this->translatedComponents as $dqlAlias => $comp) {
@@ -279,14 +280,19 @@ class TranslationWalker extends SqlWalker
                 $sql = " {$joinStrategy} JOIN ".$transTable.' '.$tblAlias;
                 $sql .= ' ON '.$tblAlias.'.'.$transMeta->getQuotedColumnName('locale', $this->platform)
                     .' = '.$this->conn->quote($locale);
-                $sql .= ' AND '.$tblAlias.'.'.$transMeta->getQuotedColumnName('objectClass', $this->platform)
-                    .' = '.$this->conn->quote($meta->name);
                 $sql .= ' AND '.$tblAlias.'.'.$transMeta->getQuotedColumnName('field', $this->platform)
                     .' = '.$this->conn->quote($field);
                 $identifier = $meta->getSingleIdentifierFieldName();
-                $colName = $meta->getQuotedColumnName($identifier, $this->platform);
-                $sql .= ' AND '.$tblAlias.'.'.$transMeta->getQuotedColumnName('foreignKey', $this->platform)
-                    .' = '.$compTblAlias.'.'.$colName;
+                $idColName = $meta->getQuotedColumnName($identifier, $this->platform);
+                if ($ea->usesPersonalTranslation($transClass)) {
+                    $sql .= ' AND '.$tblAlias.'.'.$transMeta->getSingleAssociationJoinColumnName('object')
+                        .' = '.$compTblAlias.'.'.$idColName;
+                } else {
+                    $sql .= ' AND '.$tblAlias.'.'.$transMeta->getQuotedColumnName('objectClass', $this->platform)
+                        .' = '.$this->conn->quote($meta->name);
+                    $sql .= ' AND '.$tblAlias.'.'.$transMeta->getQuotedColumnName('foreignKey', $this->platform)
+                        .' = '.$compTblAlias.'.'.$idColName;
+                }
                 isset($this->components[$dqlAlias]) ? $this->components[$dqlAlias] .= $sql : $this->components[$dqlAlias] = $sql;
 
                 $originalField = $compTblAlias.'.'.$meta->getQuotedColumnName($field, $this->platform);

+ 25 - 5
lib/Gedmo/Translatable/TranslatableListener.php

@@ -2,8 +2,8 @@
 
 namespace Gedmo\Translatable;
 
-use Gedmo\Tool\Wrapper\AbstractWrapper;
 use Doctrine\Common\EventArgs;
+use Gedmo\Tool\Wrapper\AbstractWrapper;
 use Gedmo\Mapping\MappedEventSubscriber;
 use Gedmo\Translatable\Mapping\Event\TranslatableAdapter;
 
@@ -446,14 +446,30 @@ class TranslatableListener extends MappedEventSubscriber
 
         $translatableFields = $config['fields'];
         foreach ($translatableFields as $field) {
+            $wasPersistedSeparetely = false;
             $skip = isset($this->translatedInLocale[$oid]) && $locale === $this->translatedInLocale[$oid];
             $skip = $skip && !isset($changeSet[$field]);
             if ($skip) {
                 continue; // locale is same and nothing changed
             }
             $translation = null;
+            // lookup persisted translations
+            if ($ea->usesPersonalTranslation($translationClass)) {
+                foreach ($ea->getScheduledObjectInsertions($uow) as $trans) {
+                    $match = get_class($trans) === $translationClass
+                        && $trans->getLocale() === $locale
+                        && $trans->getField() === $field
+                        && $trans->getObject() === $object
+                    ;
+                    if ($match) {
+                        $translation = $trans;
+                        $wasPersistedSeparetely = true;
+                        break;
+                    }
+                }
+            }
             // check if translation allready is created
-            if (!$isInsert) {
+            if (!$isInsert && !$translation) {
                 $translation = $ea->findTranslation(
                     $wrapped,
                     $locale,
@@ -475,7 +491,7 @@ class TranslatableListener extends MappedEventSubscriber
                 $scheduleUpdate = !$isInsert;
             }
 
-            if (!empty($translation)) {
+            if ($translation) {
                 // set the translated field, take value using reflection
                 $value = $wrapped->getPropertyValue($field);
                 $translation->setContent($ea->getTranslationValue($object, $field));
@@ -485,8 +501,12 @@ class TranslatableListener extends MappedEventSubscriber
                     $this->pendingTranslationInserts[spl_object_hash($object)][] = $translation;
                 } else {
                     // persist and compute change set for translation
-                    $om->persist($translation);
-                    $uow->computeChangeSet($translationMetadata, $translation);
+                    if ($wasPersistedSeparetely) {
+                        $ea->recomputeSingleObjectChangeset($uow, $translationMetadata, $translation);
+                    } else {
+                        $om->persist($translation);
+                        $uow->computeChangeSet($translationMetadata, $translation);
+                    }
                 }
             }
         }

+ 1 - 0
tests/Gedmo/Translatable/Fixture/Personal/PersonalArticleTranslation.php

@@ -12,6 +12,7 @@ class PersonalArticleTranslation extends AbstractPersonalTranslation
 {
     /**
      * @ORM\ManyToOne(targetEntity="Article", inversedBy="translations")
+     * @ORM\JoinColumn(name="object_id", referencedColumnName="id", onDelete="CASCADE")
      */
     protected $object;
 }

+ 97 - 0
tests/Gedmo/Translatable/PersonalTranslationTest.php

@@ -3,6 +3,9 @@
 namespace Gedmo\Translatable;
 
 use Doctrine\Common\EventManager;
+use Doctrine\ORM\Query;
+use Gedmo\Translatable\TranslatableListener;
+use Gedmo\Translatable\Query\TreeWalker\TranslationWalker;
 use Tool\BaseTestCaseORM;
 use Translatable\Fixture\Personal\Article;
 use Translatable\Fixture\Personal\PersonalArticleTranslation;
@@ -19,6 +22,7 @@ class PersonalTranslationTest extends BaseTestCaseORM
 {
     const ARTICLE = 'Translatable\Fixture\Personal\Article';
     const TRANSLATION = 'Translatable\Fixture\Personal\PersonalArticleTranslation';
+    const TREE_WALKER_TRANSLATION = 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker';
 
     private $translatableListener;
 
@@ -71,6 +75,99 @@ class PersonalTranslationTest extends BaseTestCaseORM
         $this->assertEquals('lt', $article->getTitle());
     }
 
+    /**
+     * @test
+     */
+    function shouldCascadeDeletionsByForeignKeyConstraints()
+    {
+        if ($this->em->getConnection()->getDatabasePlatform()->getName() == 'sqlite') {
+            $this->markTestSkipped('Foreign key constraints does not map in sqlite.');
+        }
+        $this->populate();
+        $this->em->createQuery('DELETE FROM '.self::ARTICLE.' a')->getSingleScalarResult();
+        $trans = $this->em->getRepository(self::TRANSLATION)->findAll();
+
+        $this->assertEquals(0, count($trans));
+    }
+
+    /**
+     * @test
+     */
+    function shouldOverrideTranslationInEntityBeingTranslated()
+    {
+        $this->translatableListener->setDefaultLocale('de');
+        $article = new Article;
+        $article->setTitle('override');
+
+        $enTranslation = new PersonalArticleTranslation;
+        $enTranslation
+            ->setField('title')
+            ->setContent('en')
+            ->setObject($article)
+            ->setLocale('en')
+        ;
+        $this->em->persist($enTranslation);
+        $this->em->persist($article);
+        $this->em->flush();
+
+        $trans = $this->em->createQuery('SELECT t FROM '.self::TRANSLATION.' t')->getArrayResult();
+        $this->assertEquals(1, count($trans));
+        $this->assertEquals('override', $trans[0]['content']);
+    }
+
+    /**
+     * @test
+     */
+    function shouldFindFromIdentityMap()
+    {
+        $article = new Article;
+        $article->setTitle('en');
+
+        $ltTranslation = new PersonalArticleTranslation;
+        $ltTranslation
+            ->setField('title')
+            ->setContent('lt')
+            ->setObject($article)
+            ->setLocale('lt')
+        ;
+        $this->em->persist($ltTranslation);
+        $this->em->persist($article);
+        $this->em->flush();
+
+        $this->startQueryLog();
+        $this->translatableListener->setTranslatableLocale('lt');
+        $article->setTitle('change lt');
+
+        $this->em->persist($article);
+        $this->em->flush();
+        $sqlQueriesExecuted = $this->queryAnalyzer->getExecutedQueries();
+        $this->assertEquals(1, count($sqlQueriesExecuted));
+        $this->assertEquals("UPDATE article_translations SET content = 'change lt' WHERE id = 1", $sqlQueriesExecuted[0]);
+    }
+
+    /**
+     * @test
+     */
+    function shouldBeAbleToUseTranslationQueryHint()
+    {
+        $this->populate();
+        $dql = 'SELECT a.title FROM ' . self::ARTICLE . ' a';
+        $query = $this
+            ->em->createQuery($dql)
+            ->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, self::TREE_WALKER_TRANSLATION)
+            ->setHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE, 'lt')
+        ;
+
+        $this->startQueryLog();
+        $result = $query->getArrayResult();
+
+        $this->assertEquals(1, count($result));
+        $this->assertEquals('lt', $result[0]['title']);
+        $sqlQueriesExecuted = $this->queryAnalyzer->getExecutedQueries();
+        $this->assertEquals(1, count($sqlQueriesExecuted));
+        $this->assertEquals("SELECT t1_.content AS title0 FROM Article a0_ LEFT JOIN article_translations t1_ ON t1_.locale = 'lt' AND t1_.field = 'title' AND t1_.object_id = a0_.id", $sqlQueriesExecuted[0]);
+    }
+
     private function populate()
     {
         $article = new Article;