Przeglądaj źródła

[translatable] translation query walker feature, references #29

gediminasm 14 lat temu
rodzic
commit
9cf38105aa

+ 37 - 0
lib/Gedmo/Translatable/Hydrator/ORM/ArrayHydrator.php

@@ -0,0 +1,37 @@
+<?php
+
+namespace Gedmo\Translatable\Hydrator\ORM;
+
+use Gedmo\Translatable\Query\TreeWalker\TranslationWalker;
+use Doctrine\ORM\Internal\Hydration\ArrayHydrator as BaseArrayHydrator;
+
+/**
+ * If query uses TranslationQueryWalker and is hydrating
+ * objects - when it requires this custom object hydrator
+ * in order to skip onLoad event from triggering retranslation
+ * of the fields
+ *
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
+ * @package Gedmo.Translatable.Hydrator.ORM
+ * @subpackage ObjectHydrator
+ * @link http://www.gediminasm.org
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class ArrayHydrator extends BaseArrayHydrator
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function _hydrateRow(array $data, array &$cache, array &$result)
+    {
+        if (isset($this->_hints[TranslationWalker::HINT_TRANSLATION_FALLBACKS])) {
+            foreach ($this->_hints[TranslationWalker::HINT_TRANSLATION_FALLBACKS] as $field => $alias) {
+                if ($data[$field] && !$data[$alias]) {
+                    $data[$alias] = $data[$field];
+                }
+                unset($data[$field]);
+            }
+        }
+        return parent::_hydrateRow($data, $cache, $result);
+    }
+}

+ 49 - 0
lib/Gedmo/Translatable/Hydrator/ORM/ObjectHydrator.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace Gedmo\Translatable\Hydrator\ORM;
+
+use Gedmo\Translatable\Query\TreeWalker\TranslationWalker;
+use Doctrine\ORM\Internal\Hydration\ObjectHydrator as BaseObjectHydrator;
+
+/**
+ * If query uses TranslationQueryWalker and is hydrating
+ * objects - when it requires this custom object hydrator
+ * in order to skip onLoad event from triggering retranslation
+ * of the fields
+ *
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
+ * @package Gedmo.Translatable.Hydrator.ORM
+ * @subpackage ObjectHydrator
+ * @link http://www.gediminasm.org
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class ObjectHydrator extends BaseObjectHydrator
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function _hydrateAll()
+    {
+        $listener = $this->_hints[TranslationWalker::HINT_TRANSLATION_LISTENER];
+        $listener->setSkipOnLoad(true);
+        $result = parent::_hydrateAll();
+        $listener->setSkipOnLoad(false);
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function _hydrateRow(array $data, array &$cache, array &$result)
+    {
+        if (isset($this->_hints[TranslationWalker::HINT_TRANSLATION_FALLBACKS])) {
+            foreach ($this->_hints[TranslationWalker::HINT_TRANSLATION_FALLBACKS] as $field => $alias) {
+                if ($data[$field] && !$data[$alias]) {
+                    $data[$alias] = $data[$field];
+                }
+                unset($data[$field]);
+            }
+        }
+        return parent::_hydrateRow($data, $cache, $result);
+    }
+}

+ 49 - 0
lib/Gedmo/Translatable/Hydrator/ORM/SimpleObjectHydrator.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace Gedmo\Translatable\Hydrator\ORM;
+
+use Gedmo\Translatable\Query\TreeWalker\TranslationWalker;
+use Doctrine\ORM\Internal\Hydration\SimpleObjectHydrator as BaseSimpleObjectHydrator;
+
+/**
+ * If query uses TranslationQueryWalker and is hydrating
+ * objects - when it requires this custom object hydrator
+ * in order to skip onLoad event from triggering retranslation
+ * of the fields
+ *
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
+ * @package Gedmo.Translatable.Hydrator.ORM
+ * @subpackage ObjectHydrator
+ * @link http://www.gediminasm.org
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class SimpleObjectHydrator extends BaseSimpleObjectHydrator
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function _hydrateAll()
+    {
+        $listener = $this->_hints[TranslationWalker::HINT_TRANSLATION_LISTENER];
+        $listener->setSkipOnLoad(true);
+        $result = parent::_hydrateAll();
+        $listener->setSkipOnLoad(false);
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function _hydrateRow(array $data, array &$cache, array &$result)
+    {
+        if (isset($this->_hints[TranslationWalker::HINT_TRANSLATION_FALLBACKS])) {
+            foreach ($this->_hints[TranslationWalker::HINT_TRANSLATION_FALLBACKS] as $field => $alias) {
+                if ($data[$field] && !$data[$alias]) {
+                    $data[$alias] = $data[$field];
+                }
+                unset($data[$field]);
+            }
+        }
+        return parent::_hydrateRow($data, $cache, $result);
+    }
+}

+ 345 - 0
lib/Gedmo/Translatable/Query/TreeWalker/TranslationWalker.php

@@ -0,0 +1,345 @@
+<?php
+
+namespace Gedmo\Translatable\Query\TreeWalker;
+
+use Gedmo\Translatable\Mapping\Event\Adapter\ORM as TranslatableEventAdapter;
+use Gedmo\Translatable\TranslationListener;
+use Doctrine\ORM\Query;
+use Doctrine\ORM\Query\SqlWalker;
+use Doctrine\ORM\Query\TreeWalkerAdapter;
+use Doctrine\ORM\Query\AST\SelectStatement;
+use Doctrine\ORM\Query\Exec\SingleSelectExecutor;
+
+/**
+ * The translation sql output walker makes it possible
+ * to translate all query components during single query.
+ * It works with any select query, any hydration method.
+ *
+ * Behind the scenes, during the object hydration it forces
+ * custom hydrator in order to interact with TranslationListener
+ * and skip postLoad event which would couse automatic retranslation
+ * of the fields.
+ *
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
+ * @package Gedmo.Translatable.Query.TreeWalker
+ * @subpackage TranslationWalker
+ * @link http://www.gediminasm.org
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class TranslationWalker extends SqlWalker
+{
+    /**
+     * Name for translation fallback hint
+     */
+    const HINT_TRANSLATION_FALLBACKS = 'translation_fallbacks';
+
+    /**
+     * Name for translation listener hint
+     */
+    const HINT_TRANSLATION_LISTENER = 'translation_listener';
+
+    /**
+     * Customized object hydrator name
+     */
+    const HYDRATE_OBJECT_TRANSLATION = 'object_translation_hydrator';
+
+    /**
+     * Customized object hydrator name
+     */
+    const HYDRATE_ARRAY_TRANSLATION = 'array_translation_hydrator';
+
+    /**
+     * Customized object hydrator name
+     */
+    const HYDRATE_SIMPLE_OBJECT_TRANSLATION = 'simple_object_translation_hydrator';
+
+    /**
+     * Stores all component references from select clause
+     *
+     * @var array
+     */
+    private $translatedComponents = array();
+
+    /**
+     * Current TranslationListener instance used
+     * in EntityManager
+     *
+     * @var TranslationListener
+     */
+    private $listener;
+
+    /**
+     * DBAL database platform
+     *
+     * @var Doctrine\DBAL\Platforms\AbstractPlatform
+     */
+    private $platform;
+
+    /**
+     * DBAL database connection
+     *
+     * @var Doctrine\DBAL\Connection
+     */
+    private $conn;
+
+    /**
+     * List of aliases to replace with translation
+     * content reference
+     *
+     * @var array
+     */
+    private $replacements = array();
+
+    /**
+     * Translation joins on current query
+     * components
+     *
+     * @var string
+     */
+    private $translationJoinSql;
+
+    /**
+     * {@inheritDoc}
+     */
+    public function __construct($query, $parserResult, array $queryComponents)
+    {
+        parent::__construct($query, $parserResult, $queryComponents);
+        $this->conn = $this->getConnection();
+        $this->platform = $this->getConnection()->getDatabasePlatform();
+        $this->listener = $this->getTranslationListener();
+        $this->extractTranslatedComponents($queryComponents);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getExecutor($AST)
+    {
+        if (!$AST instanceof SelectStatement) {
+            throw new \Gedmo\Exception\UnexpectedValueException('Translation walker should be used only on select statement');
+        }
+        $this->translationJoinSql = $this->getTranslationJoinsSql();
+        return new SingleSelectExecutor($AST, $this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function walkSelectStatement(SelectStatement $AST)
+    {
+        $result = parent::walkSelectStatement($AST);
+        if (!count($this->translatedComponents)) {
+            return $result;
+        }
+
+        $hydrationMode = $this->getQuery()->getHydrationMode();
+        if ($this->needsFallback()) {
+            // in case if fallback is used and hydration is array, it needs custom hydrator
+            if ($hydrationMode === Query::HYDRATE_ARRAY) {
+                $this->getQuery()->setHydrationMode(self::HYDRATE_ARRAY_TRANSLATION);
+                $this->getEntityManager()->getConfiguration()->addCustomHydrationMode(
+                    self::HYDRATE_ARRAY_TRANSLATION,
+                    'Gedmo\\Translatable\\Hydrator\\ORM\\ArrayHydrator'
+                );
+            }
+        }
+
+        $this->getQuery()->setHint(self::HINT_TRANSLATION_LISTENER, $this->listener);
+        if ($hydrationMode === Query::HYDRATE_OBJECT) {
+            $this->getQuery()->setHydrationMode(self::HYDRATE_OBJECT_TRANSLATION);
+            $this->getEntityManager()->getConfiguration()->addCustomHydrationMode(
+                self::HYDRATE_OBJECT_TRANSLATION,
+                'Gedmo\\Translatable\\Hydrator\\ORM\\ObjectHydrator'
+            );
+            $this->getQuery()->setHint(Query::HINT_REFRESH, true);
+        } elseif ($hydrationMode === Query::HYDRATE_SIMPLEOBJECT) {
+            $this->getQuery()->setHydrationMode(self::HYDRATE_SIMPLE_OBJECT_TRANSLATION);
+            $this->getEntityManager()->getConfiguration()->addCustomHydrationMode(
+                self::HYDRATE_SIMPLE_OBJECT_TRANSLATION,
+                'Gedmo\\Translatable\\Hydrator\\ORM\\SimpleObjectHydrator'
+            );
+            $this->getQuery()->setHint(Query::HINT_REFRESH, true);
+        }
+        return $result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function walkSelectClause($selectClause)
+    {
+        $result = parent::walkSelectClause($selectClause);
+        $fallbackSql = '';
+        if ($this->needsFallback() && count($this->translatedComponents)) {
+            $fallbackAliases = array();
+            foreach ($this->replacements as $dqlAlias => $trans) {
+                if (preg_match("/{$dqlAlias} AS ([^\s]+)/smi", $result, $m)) {
+                    list($tblAlias, $colName) = explode('.', $dqlAlias);
+                    $fallback = $this->getSQLColumnAlias($colName.'_fallback');
+                    $fallbackSql .= ', '.$dqlAlias.' AS '.$fallback;
+                    $fallbackAliases[$fallback] = rtrim($m[1], ',');
+                }
+            }
+            $this->getQuery()->setHint(self::HINT_TRANSLATION_FALLBACKS, $fallbackAliases);
+        }
+        $result = str_replace(
+            array_keys($this->replacements),
+            array_values($this->replacements),
+            $result
+        );
+        $result .= $fallbackSql;
+        return $result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function walkFromClause($fromClause)
+    {
+        $result = parent::walkFromClause($fromClause);
+        $result .= $this->translationJoinSql;
+        return $result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function walkWhereClause($whereClause)
+    {
+        $result = parent::walkWhereClause($whereClause);
+        return str_replace(
+            array_keys($this->replacements),
+            array_values($this->replacements),
+            $result
+        );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function walkHavingClause($havingClause)
+    {
+        $result = parent::walkHavingClause($havingClause);
+        return str_replace(
+            array_keys($this->replacements),
+            array_values($this->replacements),
+            $result
+        );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function walkOrderByClause($orderByClause)
+    {
+        $result = parent::walkOrderByClause($orderByClause);
+        return str_replace(
+            array_keys($this->replacements),
+            array_values($this->replacements),
+            $result
+        );
+    }
+
+    /**
+     * Creates a left join list for translations
+     * on used query components
+     *
+     * @todo: make it cleaner
+     * @return string
+     */
+    private function getTranslationJoinsSql()
+    {
+        $result = '';
+        $em = $this->getEntityManager();
+        $ea = new TranslatableEventAdapter;
+        $locale = $this->listener->getListenerLocale();
+
+        foreach ($this->translatedComponents as $dqlAlias => $comp) {
+            $meta = $comp['metadata'];
+            $config = $this->listener->getConfiguration($em, $meta->name);
+            $transClass = $this->listener->getTranslationClass($ea, $meta->name);
+            $transMeta = $em->getClassMetadata($transClass);
+            $transTable = $transMeta->getQuotedTableName($this->platform);
+            foreach ($config['fields'] as $field) {
+                $compTableName = $meta->getQuotedTableName($this->platform);
+                $compTblAlias = $this->getSQLTableAlias($compTableName, $dqlAlias);
+                $tblAlias = $this->getSQLTableAlias('trans'.$compTblAlias.$field);
+                $sql = ' LEFT 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);
+                $colAlias = $this->getSQLColumnAlias($colName);
+                $sql .= ' AND '.$tblAlias.'.'.$transMeta->getQuotedColumnName('foreignKey', $this->platform)
+                    .' = '.$compTblAlias.'.'.$colName;
+                $result .= $sql;
+                $this->replacements[$compTblAlias.'.'.$meta->getQuotedColumnName($field, $this->platform)]
+                    = $tblAlias.'.'.$transMeta->getQuotedColumnName('content', $this->platform);
+            }
+        }
+        return $result;
+    }
+
+    /**
+     * Checks if translation fallbacks are needed
+     *
+     * @return boolean
+     */
+    private function needsFallback()
+    {
+        $q = $this->getQuery();
+        return $this->listener->getTranslationFallback()
+            && $q->getHydrationMode() !== Query::HYDRATE_SCALAR
+            && $q->getHydrationMode() !== Query::HYDRATE_SINGLE_SCALAR;
+    }
+
+    /**
+     * Search for translated components in the select clause
+     *
+     * @param array $queryComponents
+     * @return void
+     */
+    private function extractTranslatedComponents(array $queryComponents)
+    {
+        $em = $this->getEntityManager();
+        foreach ($queryComponents as $alias => $comp) {
+            $meta = $comp['metadata'];
+            $config = $this->listener->getConfiguration($em, $meta->name);
+            if ($config && isset($config['fields'])) {
+                $this->translatedComponents[$alias] = $comp;
+            }
+        }
+    }
+
+    /**
+     * Get the currently used TranslationListener
+     *
+     * @throws \Gedmo\Exception\RuntimeException - if listener is not found
+     * @return TranslationListener
+     */
+    private function getTranslationListener()
+    {
+        $translationListener = null;
+        $em = $this->getEntityManager();
+        foreach ($em->getEventManager()->getListeners() as $event => $listeners) {
+            foreach ($listeners as $hash => $listener) {
+                if ($listener instanceof TranslationListener) {
+                    $translationListener = $listener;
+                    break;
+                }
+            }
+            if ($translationListener) {
+                break;
+            }
+        }
+
+        if (is_null($translationListener)) {
+            throw new \Gedmo\Exception\RuntimeException('The translation listener could not be found');
+        }
+        return $translationListener;
+    }
+}

+ 81 - 41
lib/Gedmo/Translatable/TranslationListener.php

@@ -3,7 +3,6 @@
 namespace Gedmo\Translatable;
 
 use Doctrine\Common\EventArgs,
-    Doctrine\Common\Persistence\ObjectManager,
     Doctrine\Common\Persistence\Mapping\ClassMetadata,
     Gedmo\Mapping\MappedEventSubscriber,
     Gedmo\Translatable\Mapping\Event\TranslatableAdapter;
@@ -55,7 +54,7 @@ class TranslationListener extends MappedEventSubscriber
      *
      * @var boolean
      */
-    private $translationFallback = true;
+    private $translationFallback = false;
 
     /**
      * List of translations which do not have the foreign
@@ -66,6 +65,15 @@ class TranslationListener extends MappedEventSubscriber
      */
     private $pendingTranslationInserts = array();
 
+    /**
+     * Currently in case if there is TranslationQueryWalker
+     * in charge. We need to skip issuing additional queries
+     * on load
+     *
+     * @var boolean
+     */
+    private $skipOnLoad = false;
+
     /**
      * Specifies the list of events to listen
      *
@@ -81,6 +89,18 @@ class TranslationListener extends MappedEventSubscriber
         );
     }
 
+    /**
+     * Set to skip or not onLoad event
+     *
+     * @param boolean $bool
+     * @return TranslationListener
+     */
+    public function setSkipOnLoad($bool)
+    {
+        $this->skipOnLoad = (bool)$bool;
+        return $this;
+    }
+
     /**
      * Mapps additional metadata
      *
@@ -98,11 +118,23 @@ class TranslationListener extends MappedEventSubscriber
      * to original record value
      *
      * @param boolean $bool
-     * @return void
+     * @return TranslationListener
      */
     public function setTranslationFallback($bool)
     {
         $this->translationFallback = (bool)$bool;
+        return $this;
+    }
+
+    /**
+     * Weather or not is using the translation
+     * fallback to original record
+     *
+     * @return boolean
+     */
+    public function getTranslationFallback()
+    {
+        return $this->translationFallback;
     }
 
     /**
@@ -124,11 +156,38 @@ class TranslationListener extends MappedEventSubscriber
      * Set the locale to use for translation listener
      *
      * @param string $locale
-     * @return void
+     * @return TranslationListener
      */
     public function setTranslatableLocale($locale)
     {
         $this->locale = $locale;
+        return $this;
+    }
+
+    /**
+     * Sets the default locale, this changes behavior
+     * to not update the original record field if locale
+     * which is used for updating is not default
+     *
+     * @param string $locale
+     * @return TranslationListener
+     */
+    public function setDefaultLocale($locale)
+    {
+        $this->defaultLocale = $locale;
+        return $this;
+    }
+
+    /**
+     * Get currently set global locale, used
+     * extensively during query execution
+     *
+     * @return string
+     */
+    public function getListenerLocale()
+    {
+        $this->validateLocale($this->locale);
+        return strtolower($this->locale);
     }
 
     /**
@@ -202,9 +261,7 @@ class TranslationListener extends MappedEventSubscriber
             $meta = $om->getClassMetadata(get_class($object));
             $config = $this->getConfiguration($om, $meta->name);
             if (isset($config['fields'])) {
-                $identifierField = $ea->getSingleIdentifierFieldName($meta);
-                $objectId = $meta->getReflectionProperty($identifierField)->getValue($object);
-
+                $objectId = $ea->extractIdentifier($om, $object);
                 $transClass = $this->getTranslationClass($ea, $meta->name);
                 $ea->removeAssociatedTranslations($objectId, $transClass);
             }
@@ -223,14 +280,11 @@ class TranslationListener extends MappedEventSubscriber
         $ea = $this->getEventAdapter($args);
         $om = $ea->getObjectManager();
         $object = $ea->getObject();
-        $uow = $om->getUnitOfWork();
         $meta = $om->getClassMetadata(get_class($object));
         // check if entity is tracked by translatable and without foreign key
         if (array_key_exists($meta->name, $this->configurations) && count($this->pendingTranslationInserts)) {
             $oid = spl_object_hash($object);
 
-            // there should be single identifier
-            $identifierField = $ea->getSingleIdentifierFieldName($meta);
             $translationMeta = $om->getClassMetadata($this->getTranslationClass($ea, $meta->name));
             if (array_key_exists($oid, $this->pendingTranslationInserts)) {
                 // load the pending translations without key
@@ -238,7 +292,7 @@ class TranslationListener extends MappedEventSubscriber
                 foreach ($translations as $translation) {
                     $translationMeta->getReflectionProperty('foreignKey')->setValue(
                         $translation,
-                        $meta->getReflectionProperty($identifierField)->getValue($object)
+                        $ea->extractIdentifier($om, $object)
                     );
                     $ea->insertTranslationRecord($translation);
                 }
@@ -256,6 +310,9 @@ class TranslationListener extends MappedEventSubscriber
      */
     public function postLoad(EventArgs $args)
     {
+        if ($this->skipOnLoad) {
+            return;
+        }
         $ea = $this->getEventAdapter($args);
         $om = $ea->getObjectManager();
         $object = $ea->getObject();
@@ -294,24 +351,25 @@ class TranslationListener extends MappedEventSubscriber
     }
 
     /**
-     * Sets the default locale, this changes behavior
-     * to not update the original record field if locale
-     * which is used for updating is not default
-     *
-     * @param string $locale
-     * @return void
+     * {@inheritDoc}
      */
-    public function setDefaultLocale($locale)
+    protected function getNamespace()
     {
-        $this->defaultLocale = $locale;
+        return __NAMESPACE__;
     }
 
     /**
-     * {@inheritDoc}
+     * Validates the given locale
+     *
+     * @param string $locale - locale to validate
+     * @throws InvalidArgumentException if locale is not valid
+     * @return void
      */
-    protected function getNamespace()
+    protected function validateLocale($locale)
     {
-        return __NAMESPACE__;
+        if (!is_string($locale) || !strlen($locale)) {
+            throw new \Gedmo\Exception\InvalidArgumentException('Locale or language cannot be empty and must be set through Listener or Entity');
+        }
     }
 
     /**
@@ -324,7 +382,7 @@ class TranslationListener extends MappedEventSubscriber
      *      primary key is composite, missing or invalid
      * @return void
      */
-    protected function handleTranslatableObjectUpdate(TranslatableAdapter $ea, $object, $isInsert)
+    private function handleTranslatableObjectUpdate(TranslatableAdapter $ea, $object, $isInsert)
     {
         $om = $ea->getObjectManager();
         $meta = $om->getClassMetadata(get_class($object));
@@ -334,10 +392,6 @@ class TranslationListener extends MappedEventSubscriber
 
         // check for the availability of the primary key
         $objectId = $ea->extractIdentifier($om, $object);
-        if (!$object && $isInsert) {
-            $objectId = null;
-        }
-
         // load the currently used locale
         $locale = $this->getTranslatableLocale($object, $meta);
 
@@ -403,18 +457,4 @@ class TranslationListener extends MappedEventSubscriber
             }
         }
     }
-
-    /**
-     * Validates the given locale
-     *
-     * @param string $locale - locale to validate
-     * @throws InvalidArgumentException if locale is not valid
-     * @return void
-     */
-    protected function validateLocale($locale)
-    {
-        if (!is_string($locale) || !strlen($locale)) {
-            throw new \Gedmo\Exception\InvalidArgumentException('Locale or language cannot be empty and must be set through Listener or Entity');
-        }
-    }
 }