瀏覽代碼

multi field translation support for single entity

gediminasm 15 年之前
父節點
當前提交
1445baca1f

+ 5 - 12
README.markdown

@@ -1,6 +1,6 @@
 # Some Doctrine 2 Extensions
 
-This package contains several extensions to Doctrine 2 that hook into the facilities of Doctrine and
+This package contains extensions for Doctrine 2 that hook into the facilities of Doctrine and
 offer new functionality or tools to use Doctrine 2 more efficently.
 
 ## Including DoctrineExtensions
@@ -26,19 +26,14 @@ the Translatable extension Translation entity:
 
     $chainDriverImpl = new Doctrine\ORM\Mapping\Driver\DriverChain();
     $yourDefaultDriverImpl = new Doctrine\ORM\Mapping\Driver\YamlDriver('/yml/mapping/files');
-    $translatableDriverImpl = $doctrineOrmConfig->newDefaultAnnotationDriver(
-        'path/to/extensions/DoctrineExtensions/Translatable/Entity'
-    );
+    $translatableDriverImpl = $doctrineOrmConfig->newDefaultAnnotationDriver('path/to/extensions/DoctrineExtensions/Translatable/Entity');
     $chainDriverImpl->addDriver($yourDefaultDriverImpl, 'Entities');
     $chainDriverImpl->addDriver($translatableDriverImpl, 'DoctrineExtensions/Translatable');
     $doctrineOrmConfig->setMetadataDriverImpl($chainDriverImpl);
 
 2. Another path for Annotation driver:
 
-    $driverImpl = $doctrineOrmConfig->newDefaultAnnotationDriver(array(
-        'default/annotation/entities/path',
-        'path/to/extensions/DoctrineExtensions/Translatable/Entity'
-    ));
+    $driverImpl = $doctrineOrmConfig->newDefaultAnnotationDriver(array('default/annotation/entities/path', 'path/to/extensions/DoctrineExtensions/Translatable/Entity'));
 
 To attach the Translatable listener to your event system and to set the translation locale
 to use in global scope for all entities:
@@ -113,7 +108,7 @@ Article Entity:
         
         public function getTranslatableFields()
         {
-            return array('title');
+            return array('title', 'content');
         }
         
         public function setTranslatableLocale($locale)
@@ -131,9 +126,7 @@ To save article with its translations:
 
     $article = new Entities\Article;
     $article->setTitle('my title in en');
-    $article->setContent('my content');
+    $article->setContent('my content in en');
     $em->persist($article);
     $em->flush();
     
-**Notice:** current implementation is in alfa version supports only one field translation
-for now. Working on fix to support list of fields

+ 5 - 0
lib/DoctrineExtensions/Translatable/Exception.php

@@ -27,4 +27,9 @@ class Exception extends \Exception
     	$type = gettype($id);
         return new self("Currently there is only integer identifiers supported, [{$type}] is given.");
     }
+    
+    static public function pendingInserts()
+    {
+        return new self("Unit of work has pending inserts, cannot request query execute");
+    }
 }

+ 42 - 12
lib/DoctrineExtensions/Translatable/TranslationListener.php

@@ -46,7 +46,16 @@ class TranslationListener implements EventSubscriber
 	 * 
 	 * @var array
 	 */
-	protected $_pendingTranslations = array();
+	protected $_pendingTranslationInserts = array();
+
+	/**
+	 * Translations which should be inserted during
+	 * update must be sheduled for later persisting
+	 * to avoid query while insert is pending
+	 * 
+	 * @var array
+	 */
+	protected $_pendingTranslationUpdates = array();
 	
 	/**
 	 * Specifies the events to listen
@@ -108,6 +117,13 @@ class TranslationListener implements EventSubscriber
                 $this->_handleTranslatableEntityUpdate($em, $entity, false);
             }
         }
+        
+        // all translations which should have been inserted are processed now
+        $translationMetadata = $em->getClassMetadata(self::TRANSLATION_ENTITY_CLASS);
+        foreach ($this->_pendingTranslationUpdates as $translation) {
+        	$em->persist($translation);
+            $uow->computeChangeSet($translationMetadata, $translation);
+        }
     }
     
     /**
@@ -122,11 +138,11 @@ class TranslationListener implements EventSubscriber
         $em = $args->getEntityManager();
         $entity = $args->getEntity();
         // check if entity is Translatable and without foreign key
-        if ($entity instanceof Translatable && count($this->_pendingTranslations)) {
+        if ($entity instanceof Translatable && count($this->_pendingTranslationInserts)) {
         	$oid = spl_object_hash($entity);
-        	if (array_key_exists($oid, $this->_pendingTranslations)) {
+        	if (array_key_exists($oid, $this->_pendingTranslationInserts)) {
                 // load the pending translations without key
-        		$translations = $this->_pendingTranslations[$oid];
+        		$translations = $this->_pendingTranslationInserts[$oid];
         		foreach ($translations as $translation) {
 	                // schedule an extra update for the foreign key
 	                $uow = $em->getUnitOfWork();
@@ -143,7 +159,7 @@ class TranslationListener implements EventSubscriber
      * by currently used locale
      * 
      * @param LifecycleEventArgs $args
-     * @throws TranslatableException if locale is not valid
+     * @throws Translatable\Exception if locale is not valid
      * @return void
      */
     public function postLoad(LifecycleEventArgs $args)
@@ -180,7 +196,7 @@ class TranslationListener implements EventSubscriber
      * @param EntityManager $em
      * @param object $entity
      * @param boolean $isInsert
-     * @throws TranslatableException if locale is not valid, or
+     * @throws Translatable\Exception if locale is not valid, or
      *      primary key is composite, missing or invalid
      * @return void
      */
@@ -223,26 +239,33 @@ class TranslationListener implements EventSubscriber
                 );
         	}
             // create new translation
+            $scheduleUpdate = false;
             if (!$translation) {
                 $translation = new Translation;
                 $translation->setLocale($locale);
 	            $translation->setField($field);
 	            $translation->setEntity($entityClass);
 	            $translation->setForeignKey($entityId);
+	            $scheduleUpdate = !$isInsert;
             }
             
             // set the translated field, take value using getter
             $fnc = 'get' . ucfirst($field);
             $translation->setContent($entity->$fnc());
             
-            // persist and compute change set for translation
-            $em->persist($translation);
-            $uow = $em->getUnitOfWork();
-            $uow->computeChangeSet($translationMetadata, $translation);
+            if ($scheduleUpdate) {
+                // need to shedule new Translation insert to avoid query on pending insert
+                $this->_pendingTranslationUpdates[] = $translation;
+            } else {
+            	// persist and compute change set for translation
+                $em->persist($translation);
+                $uow = $em->getUnitOfWork();
+                $uow->computeChangeSet($translationMetadata, $translation);
+            }
             // if we do not have the primary key yet available
             // keep this translation in memory for later update
             if ($isInsert && is_null($entityId)) {
-            	$this->_pendingTranslations[spl_object_hash($entity)][$field] = $translation;
+            	$this->_pendingTranslationInserts[spl_object_hash($entity)][$field] = $translation;
             }
         }
     }
@@ -257,10 +280,17 @@ class TranslationListener implements EventSubscriber
      * @param string $locale
      * @param string $field
      * @param boolean $contentOnly - true if field translation only
+     * @throws Translatable\Exception if unit of work has pending inserts
+     *      to avoid infinite loop
      * @return mixed - null if nothing is found
      */
     protected function _findTranslation(EntityManager $em, $entityId, $entityClass, $locale, $field, $contentOnly = false)
     {
+    	// @TODO: cannot use query if doctrine has pending inserts, this is annoying
+    	if ($em->getUnitOfWork()->hasPendingInsertions()) {
+    		throw Exception::pendingInserts();
+    	}
+    	
         $qb = $em->createQueryBuilder();
         $qb->select('trans')
             ->from(self::TRANSLATION_ENTITY_CLASS, 'trans')
@@ -290,7 +320,7 @@ class TranslationListener implements EventSubscriber
      * Validates the given locale
      * 
      * @param string $locale - locale to validate
-     * @throws TranslatableException if locale is not valid
+     * @throws Translatable\Exception if locale is not valid
      * @return void
      */
     protected function _validateLocale($locale)