init without trunk
This commit is contained in:
parent
ed24ac4994
commit
bb809e7233
14652 changed files with 177862 additions and 94817 deletions
|
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable\Document\MappedSuperclass;
|
||||
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoODM;
|
||||
|
||||
/**
|
||||
* Gedmo\Translatable\Document\AbstractPersonalTranslation
|
||||
*
|
||||
* @MongoODM\MappedSuperclass
|
||||
*/
|
||||
abstract class AbstractPersonalTranslation
|
||||
{
|
||||
/**
|
||||
* @var integer $id
|
||||
*
|
||||
* @MongoODM\Id
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @var string $locale
|
||||
*
|
||||
* @MongoODM\Field(type="string")
|
||||
*/
|
||||
protected $locale;
|
||||
|
||||
/**
|
||||
* Related entity with ManyToOne relation
|
||||
* must be mapped by user
|
||||
*/
|
||||
protected $object;
|
||||
|
||||
/**
|
||||
* @var string $field
|
||||
*
|
||||
* @MongoODM\Field(type="string")
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
/**
|
||||
* @var string $content
|
||||
*
|
||||
* @MongoODM\Field(type="string")
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* Get id
|
||||
*
|
||||
* @return integer $id
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set locale
|
||||
*
|
||||
* @param string $locale
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setLocale($locale)
|
||||
{
|
||||
$this->locale = $locale;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get locale
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocale()
|
||||
{
|
||||
return $this->locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set field
|
||||
*
|
||||
* @param string $field
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setField($field)
|
||||
{
|
||||
$this->field = $field;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getField()
|
||||
{
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set object related
|
||||
*
|
||||
* @param object $object
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setObject($object)
|
||||
{
|
||||
$this->object = $object;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get object related
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getObject()
|
||||
{
|
||||
return $this->object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set content
|
||||
*
|
||||
* @param string $content
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->content = $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable\Document\MappedSuperclass;
|
||||
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoODM;
|
||||
|
||||
/**
|
||||
* Gedmo\Translatable\Document\MappedSuperclass\AbstractTranslation
|
||||
*
|
||||
* @MongoODM\MappedSuperclass
|
||||
*/
|
||||
abstract class AbstractTranslation
|
||||
{
|
||||
/**
|
||||
* @var integer $id
|
||||
*
|
||||
* @MongoODM\Id
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @var string $locale
|
||||
*
|
||||
* @MongoODM\Field(type="string")
|
||||
*/
|
||||
protected $locale;
|
||||
|
||||
/**
|
||||
* @var string $objectClass
|
||||
*
|
||||
* @MongoODM\Field(type="string")
|
||||
*/
|
||||
protected $objectClass;
|
||||
|
||||
/**
|
||||
* @var string $field
|
||||
*
|
||||
* @MongoODM\Field(type="string")
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
/**
|
||||
* @var string $foreignKey
|
||||
*
|
||||
* @MongoODM\Field(type="string", name="foreign_key")
|
||||
*/
|
||||
protected $foreignKey;
|
||||
|
||||
/**
|
||||
* @var string $content
|
||||
*
|
||||
* @MongoODM\Field(type="string")
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* Get id
|
||||
*
|
||||
* @return integer $id
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set locale
|
||||
*
|
||||
* @param string $locale
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setLocale($locale)
|
||||
{
|
||||
$this->locale = $locale;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get locale
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocale()
|
||||
{
|
||||
return $this->locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set field
|
||||
*
|
||||
* @param string $field
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setField($field)
|
||||
{
|
||||
$this->field = $field;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getField()
|
||||
{
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set object class
|
||||
*
|
||||
* @param string $objectClass
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setObjectClass($objectClass)
|
||||
{
|
||||
$this->objectClass = $objectClass;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get objectClass
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getObjectClass()
|
||||
{
|
||||
return $this->objectClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set foreignKey
|
||||
*
|
||||
* @param string $foreignKey
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setForeignKey($foreignKey)
|
||||
{
|
||||
$this->foreignKey = $foreignKey;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get foreignKey
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getForeignKey()
|
||||
{
|
||||
return $this->foreignKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set content
|
||||
*
|
||||
* @param string $content
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->content = $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,254 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable\Document\Repository;
|
||||
|
||||
use Gedmo\Translatable\TranslatableListener;
|
||||
use Doctrine\ODM\MongoDB\DocumentRepository;
|
||||
use Doctrine\ODM\MongoDB\Cursor;
|
||||
use Doctrine\ODM\MongoDB\DocumentManager;
|
||||
use Doctrine\ODM\MongoDB\UnitOfWork;
|
||||
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
|
||||
use Gedmo\Tool\Wrapper\MongoDocumentWrapper;
|
||||
use Gedmo\Translatable\Mapping\Event\Adapter\ODM as TranslatableAdapterODM;
|
||||
|
||||
/**
|
||||
* The TranslationRepository has some useful functions
|
||||
* to interact with translations.
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class TranslationRepository extends DocumentRepository
|
||||
{
|
||||
/**
|
||||
* Current TranslatableListener instance used
|
||||
* in EntityManager
|
||||
*
|
||||
* @var TranslatableListener
|
||||
*/
|
||||
private $listener;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(DocumentManager $dm, UnitOfWork $uow, ClassMetadata $class)
|
||||
{
|
||||
if ($class->getReflectionClass()->isSubclassOf('Gedmo\Translatable\Document\MappedSuperclass\AbstractPersonalTranslation')) {
|
||||
throw new \Gedmo\Exception\UnexpectedValueException('This repository is useless for personal translations');
|
||||
}
|
||||
parent::__construct($dm, $uow, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes additional translation of $document $field into $locale
|
||||
* using $value
|
||||
*
|
||||
* @param object $document
|
||||
* @param string $field
|
||||
* @param string $locale
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function translate($document, $field, $locale, $value)
|
||||
{
|
||||
$meta = $this->dm->getClassMetadata(get_class($document));
|
||||
$listener = $this->getTranslatableListener();
|
||||
$config = $listener->getConfiguration($this->dm, $meta->name);
|
||||
if (!isset($config['fields']) || !in_array($field, $config['fields'])) {
|
||||
throw new \Gedmo\Exception\InvalidArgumentException("Document: {$meta->name} does not translate field - {$field}");
|
||||
}
|
||||
$modRecordValue = (!$listener->getPersistDefaultLocaleTranslation() && $locale === $listener->getDefaultLocale())
|
||||
|| $listener->getTranslatableLocale($document, $meta, $this->getDocumentManager()) === $locale
|
||||
;
|
||||
if ($modRecordValue) {
|
||||
$meta->getReflectionProperty($field)->setValue($document, $value);
|
||||
$this->dm->persist($document);
|
||||
} else {
|
||||
if (isset($config['translationClass'])) {
|
||||
$class = $config['translationClass'];
|
||||
} else {
|
||||
$ea = new TranslatableAdapterODM();
|
||||
$class = $listener->getTranslationClass($ea, $config['useObjectClass']);
|
||||
}
|
||||
$foreignKey = $meta->getReflectionProperty($meta->identifier)->getValue($document);
|
||||
$objectClass = $config['useObjectClass'];
|
||||
$transMeta = $this->dm->getClassMetadata($class);
|
||||
$trans = $this->findOneBy(compact('locale', 'field', 'objectClass', 'foreignKey'));
|
||||
if (!$trans) {
|
||||
$trans = $transMeta->newInstance();
|
||||
$transMeta->getReflectionProperty('foreignKey')->setValue($trans, $foreignKey);
|
||||
$transMeta->getReflectionProperty('objectClass')->setValue($trans, $objectClass);
|
||||
$transMeta->getReflectionProperty('field')->setValue($trans, $field);
|
||||
$transMeta->getReflectionProperty('locale')->setValue($trans, $locale);
|
||||
}
|
||||
$mapping = $meta->getFieldMapping($field);
|
||||
$type = $this->getType($mapping['type']);
|
||||
$transformed = $type->convertToDatabaseValue($value);
|
||||
$transMeta->getReflectionProperty('content')->setValue($trans, $transformed);
|
||||
if ($this->dm->getUnitOfWork()->isInIdentityMap($document)) {
|
||||
$this->dm->persist($trans);
|
||||
} else {
|
||||
$oid = spl_object_hash($document);
|
||||
$listener->addPendingTranslationInsert($oid, $trans);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all translations with all translatable
|
||||
* fields from the given entity
|
||||
*
|
||||
* @param object $document
|
||||
*
|
||||
* @return array list of translations in locale groups
|
||||
*/
|
||||
public function findTranslations($document)
|
||||
{
|
||||
$result = array();
|
||||
$wrapped = new MongoDocumentWrapper($document, $this->dm);
|
||||
if ($wrapped->hasValidIdentifier()) {
|
||||
$documentId = $wrapped->getIdentifier();
|
||||
|
||||
$translationMeta = $this->getClassMetadata(); // table inheritance support
|
||||
|
||||
$config = $this
|
||||
->getTranslatableListener()
|
||||
->getConfiguration($this->dm, $wrapped->getMetadata()->name);
|
||||
|
||||
if (!$config) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$documentClass = $config['useObjectClass'];
|
||||
|
||||
$translationClass = isset($config['translationClass']) ?
|
||||
$config['translationClass'] :
|
||||
$translationMeta->rootDocumentName;
|
||||
|
||||
$qb = $this->dm->createQueryBuilder($translationClass);
|
||||
$q = $qb->field('foreignKey')->equals($documentId)
|
||||
->field('objectClass')->equals($documentClass)
|
||||
->field('content')->exists(true)->notEqual(null)
|
||||
->sort('locale', 'asc')
|
||||
->getQuery();
|
||||
|
||||
$q->setHydrate(false);
|
||||
$data = $q->execute();
|
||||
if ($data instanceof Cursor) {
|
||||
$data = $data->toArray();
|
||||
}
|
||||
if ($data && is_array($data) && count($data)) {
|
||||
foreach ($data as $row) {
|
||||
$result[$row['locale']][$row['field']] = $row['content'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the object $class by the translated field.
|
||||
* Result is the first occurrence of translated field.
|
||||
* Query can be slow, since there are no indexes on such
|
||||
* columns
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
* @param string $class
|
||||
*
|
||||
* @return object - instance of $class or null if not found
|
||||
*/
|
||||
public function findObjectByTranslatedField($field, $value, $class)
|
||||
{
|
||||
$document = null;
|
||||
$meta = $this->dm->getClassMetadata($class);
|
||||
if ($meta->hasField($field)) {
|
||||
$qb = $this->createQueryBuilder();
|
||||
$q = $qb->field('field')->equals($field)
|
||||
->field('objectClass')->equals($meta->rootDocumentName)
|
||||
->field('content')->equals($value)
|
||||
->getQuery();
|
||||
|
||||
$q->setHydrate(false);
|
||||
$result = $q->execute();
|
||||
if ($result instanceof Cursor) {
|
||||
$result = $result->toArray();
|
||||
}
|
||||
$id = count($result) ? $result[0]['foreignKey'] : null;
|
||||
if ($id) {
|
||||
$document = $this->dm->find($class, $id);
|
||||
}
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all translations with all translatable
|
||||
* fields by a given document primary key
|
||||
*
|
||||
* @param mixed $id - primary key value of document
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function findTranslationsByObjectId($id)
|
||||
{
|
||||
$result = array();
|
||||
if ($id) {
|
||||
$qb = $this->createQueryBuilder();
|
||||
$q = $qb->field('foreignKey')->equals($id)
|
||||
->field('content')->exists(true)->notEqual(null)
|
||||
->sort('locale', 'asc')
|
||||
->getQuery();
|
||||
|
||||
$q->setHydrate(false);
|
||||
$data = $q->execute();
|
||||
|
||||
if ($data instanceof Cursor) {
|
||||
$data = $data->toArray();
|
||||
}
|
||||
if ($data && is_array($data) && count($data)) {
|
||||
foreach ($data as $row) {
|
||||
$result[$row['locale']][$row['field']] = $row['content'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently used TranslatableListener
|
||||
*
|
||||
* @throws \Gedmo\Exception\RuntimeException - if listener is not found
|
||||
*
|
||||
* @return TranslatableListener
|
||||
*/
|
||||
private function getTranslatableListener()
|
||||
{
|
||||
if (!$this->listener) {
|
||||
foreach ($this->dm->getEventManager()->getListeners() as $event => $listeners) {
|
||||
foreach ($listeners as $hash => $listener) {
|
||||
if ($listener instanceof TranslatableListener) {
|
||||
return $this->listener = $listener;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new \Gedmo\Exception\RuntimeException('The translation listener could not be found');
|
||||
}
|
||||
|
||||
return $this->listener;
|
||||
}
|
||||
|
||||
private function getType($type)
|
||||
{
|
||||
// due to change in ODM beta 9
|
||||
return class_exists('Doctrine\ODM\MongoDB\Types\Type') ? \Doctrine\ODM\MongoDB\Types\Type::getType($type)
|
||||
: \Doctrine\ODM\MongoDB\Mapping\Types\Type::getType($type);
|
||||
}
|
||||
}
|
||||
30
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/Translation.php
vendored
Normal file
30
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/Translation.php
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable\Document;
|
||||
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations\Document;
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations\UniqueIndex;
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations\Index;
|
||||
|
||||
/**
|
||||
* Gedmo\Translatable\Document\Translation
|
||||
*
|
||||
* @Document(repositoryClass="Gedmo\Translatable\Document\Repository\TranslationRepository")
|
||||
* @UniqueIndex(name="lookup_unique_idx", keys={
|
||||
* "locale" = "asc",
|
||||
* "object_class" = "asc",
|
||||
* "foreign_key" = "asc",
|
||||
* "field" = "asc"
|
||||
* })
|
||||
* @Index(name="translations_lookup_idx", keys={
|
||||
* "locale" = "asc",
|
||||
* "object_class" = "asc",
|
||||
* "foreign_key" = "asc"
|
||||
* })
|
||||
*/
|
||||
class Translation extends MappedSuperclass\AbstractTranslation
|
||||
{
|
||||
/**
|
||||
* All required columns are mapped through inherited superclass
|
||||
*/
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable\Entity\MappedSuperclass;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation
|
||||
*
|
||||
* @ORM\MappedSuperclass
|
||||
*/
|
||||
abstract class AbstractPersonalTranslation
|
||||
{
|
||||
/**
|
||||
* @var integer $id
|
||||
*
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue(strategy="IDENTITY")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @var string $locale
|
||||
*
|
||||
* @ORM\Column(type="string", length=8)
|
||||
*/
|
||||
protected $locale;
|
||||
|
||||
/**
|
||||
* @var string $field
|
||||
*
|
||||
* @ORM\Column(type="string", length=32)
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
/**
|
||||
* Related entity with ManyToOne relation
|
||||
* must be mapped by user
|
||||
*/
|
||||
protected $object;
|
||||
|
||||
/**
|
||||
* @var string $content
|
||||
*
|
||||
* @ORM\Column(type="text", nullable=true)
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* Get id
|
||||
*
|
||||
* @return integer $id
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set locale
|
||||
*
|
||||
* @param string $locale
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setLocale($locale)
|
||||
{
|
||||
$this->locale = $locale;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get locale
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocale()
|
||||
{
|
||||
return $this->locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set field
|
||||
*
|
||||
* @param string $field
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setField($field)
|
||||
{
|
||||
$this->field = $field;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get field
|
||||
*
|
||||
* @return string $field
|
||||
*/
|
||||
public function getField()
|
||||
{
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set object related
|
||||
*
|
||||
* @param object $object
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setObject($object)
|
||||
{
|
||||
$this->object = $object;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get related object
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function getObject()
|
||||
{
|
||||
return $this->object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set content
|
||||
*
|
||||
* @param string $content
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->content = $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable\Entity\MappedSuperclass;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* Gedmo\Translatable\Entity\MappedSuperclass\AbstractTranslation
|
||||
*
|
||||
* @ORM\MappedSuperclass
|
||||
*/
|
||||
abstract class AbstractTranslation
|
||||
{
|
||||
/**
|
||||
* @var integer $id
|
||||
*
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue(strategy="IDENTITY")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @var string $locale
|
||||
*
|
||||
* @ORM\Column(type="string", length=8)
|
||||
*/
|
||||
protected $locale;
|
||||
|
||||
/**
|
||||
* @var string $objectClass
|
||||
*
|
||||
* @ORM\Column(name="object_class", type="string", length=255)
|
||||
*/
|
||||
protected $objectClass;
|
||||
|
||||
/**
|
||||
* @var string $field
|
||||
*
|
||||
* @ORM\Column(type="string", length=32)
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
/**
|
||||
* @var string $foreignKey
|
||||
*
|
||||
* @ORM\Column(name="foreign_key", type="string", length=64)
|
||||
*/
|
||||
protected $foreignKey;
|
||||
|
||||
/**
|
||||
* @var string $content
|
||||
*
|
||||
* @ORM\Column(type="text", nullable=true)
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* Get id
|
||||
*
|
||||
* @return integer $id
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set locale
|
||||
*
|
||||
* @param string $locale
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setLocale($locale)
|
||||
{
|
||||
$this->locale = $locale;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get locale
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocale()
|
||||
{
|
||||
return $this->locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set field
|
||||
*
|
||||
* @param string $field
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setField($field)
|
||||
{
|
||||
$this->field = $field;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getField()
|
||||
{
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set object class
|
||||
*
|
||||
* @param string $objectClass
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setObjectClass($objectClass)
|
||||
{
|
||||
$this->objectClass = $objectClass;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get objectClass
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getObjectClass()
|
||||
{
|
||||
return $this->objectClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set foreignKey
|
||||
*
|
||||
* @param string $foreignKey
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setForeignKey($foreignKey)
|
||||
{
|
||||
$this->foreignKey = $foreignKey;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get foreignKey
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getForeignKey()
|
||||
{
|
||||
return $this->foreignKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set content
|
||||
*
|
||||
* @param string $content
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->content = $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
}
|
||||
249
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/Repository/TranslationRepository.php
vendored
Normal file
249
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/Repository/TranslationRepository.php
vendored
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable\Entity\Repository;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Gedmo\Translatable\TranslatableListener;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Gedmo\Tool\Wrapper\EntityWrapper;
|
||||
use Gedmo\Translatable\Mapping\Event\Adapter\ORM as TranslatableAdapterORM;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
|
||||
/**
|
||||
* The TranslationRepository has some useful functions
|
||||
* to interact with translations.
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class TranslationRepository extends EntityRepository
|
||||
{
|
||||
/**
|
||||
* Current TranslatableListener instance used
|
||||
* in EntityManager
|
||||
*
|
||||
* @var TranslatableListener
|
||||
*/
|
||||
private $listener;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $em, ClassMetadata $class)
|
||||
{
|
||||
if ($class->getReflectionClass()->isSubclassOf('Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation')) {
|
||||
throw new \Gedmo\Exception\UnexpectedValueException('This repository is useless for personal translations');
|
||||
}
|
||||
parent::__construct($em, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes additional translation of $entity $field into $locale
|
||||
* using $value
|
||||
*
|
||||
* @param object $entity
|
||||
* @param string $field
|
||||
* @param string $locale
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws \Gedmo\Exception\InvalidArgumentException
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function translate($entity, $field, $locale, $value)
|
||||
{
|
||||
$meta = $this->_em->getClassMetadata(get_class($entity));
|
||||
$listener = $this->getTranslatableListener();
|
||||
$config = $listener->getConfiguration($this->_em, $meta->name);
|
||||
if (!isset($config['fields']) || !in_array($field, $config['fields'])) {
|
||||
throw new \Gedmo\Exception\InvalidArgumentException("Entity: {$meta->name} does not translate field - {$field}");
|
||||
}
|
||||
$needsPersist = true;
|
||||
if ($locale === $listener->getTranslatableLocale($entity, $meta, $this->getEntityManager())) {
|
||||
$meta->getReflectionProperty($field)->setValue($entity, $value);
|
||||
$this->_em->persist($entity);
|
||||
} else {
|
||||
if (isset($config['translationClass'])) {
|
||||
$class = $config['translationClass'];
|
||||
} else {
|
||||
$ea = new TranslatableAdapterORM();
|
||||
$class = $listener->getTranslationClass($ea, $config['useObjectClass']);
|
||||
}
|
||||
$foreignKey = $meta->getReflectionProperty($meta->getSingleIdentifierFieldName())->getValue($entity);
|
||||
$objectClass = $config['useObjectClass'];
|
||||
$transMeta = $this->_em->getClassMetadata($class);
|
||||
$trans = $this->findOneBy(compact('locale', 'objectClass', 'field', 'foreignKey'));
|
||||
if (!$trans) {
|
||||
$trans = $transMeta->newInstance();
|
||||
$transMeta->getReflectionProperty('foreignKey')->setValue($trans, $foreignKey);
|
||||
$transMeta->getReflectionProperty('objectClass')->setValue($trans, $objectClass);
|
||||
$transMeta->getReflectionProperty('field')->setValue($trans, $field);
|
||||
$transMeta->getReflectionProperty('locale')->setValue($trans, $locale);
|
||||
}
|
||||
if ($listener->getDefaultLocale() != $listener->getTranslatableLocale($entity, $meta, $this->getEntityManager()) &&
|
||||
$locale === $listener->getDefaultLocale()) {
|
||||
$listener->setTranslationInDefaultLocale(spl_object_hash($entity), $field, $trans);
|
||||
$needsPersist = $listener->getPersistDefaultLocaleTranslation();
|
||||
}
|
||||
$type = Type::getType($meta->getTypeOfField($field));
|
||||
$transformed = $type->convertToDatabaseValue($value, $this->_em->getConnection()->getDatabasePlatform());
|
||||
$transMeta->getReflectionProperty('content')->setValue($trans, $transformed);
|
||||
if ($needsPersist) {
|
||||
if ($this->_em->getUnitOfWork()->isInIdentityMap($entity)) {
|
||||
$this->_em->persist($trans);
|
||||
} else {
|
||||
$oid = spl_object_hash($entity);
|
||||
$listener->addPendingTranslationInsert($oid, $trans);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all translations with all translatable
|
||||
* fields from the given entity
|
||||
*
|
||||
* @param object $entity Must implement Translatable
|
||||
*
|
||||
* @return array list of translations in locale groups
|
||||
*/
|
||||
public function findTranslations($entity)
|
||||
{
|
||||
$result = array();
|
||||
$wrapped = new EntityWrapper($entity, $this->_em);
|
||||
if ($wrapped->hasValidIdentifier()) {
|
||||
$entityId = $wrapped->getIdentifier();
|
||||
$config = $this
|
||||
->getTranslatableListener()
|
||||
->getConfiguration($this->_em, $wrapped->getMetadata()->name);
|
||||
|
||||
if (!$config) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$entityClass = $config['useObjectClass'];
|
||||
$translationMeta = $this->getClassMetadata(); // table inheritance support
|
||||
|
||||
$translationClass = isset($config['translationClass']) ?
|
||||
$config['translationClass'] :
|
||||
$translationMeta->rootEntityName;
|
||||
|
||||
$qb = $this->_em->createQueryBuilder();
|
||||
$qb->select('trans.content, trans.field, trans.locale')
|
||||
->from($translationClass, 'trans')
|
||||
->where('trans.foreignKey = :entityId', 'trans.objectClass = :entityClass')
|
||||
->orderBy('trans.locale');
|
||||
$q = $qb->getQuery();
|
||||
$data = $q->execute(
|
||||
compact('entityId', 'entityClass'),
|
||||
Query::HYDRATE_ARRAY
|
||||
);
|
||||
|
||||
if ($data && is_array($data) && count($data)) {
|
||||
foreach ($data as $row) {
|
||||
$result[$row['locale']][$row['field']] = $row['content'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the entity $class by the translated field.
|
||||
* Result is the first occurrence of translated field.
|
||||
* Query can be slow, since there are no indexes on such
|
||||
* columns
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
* @param string $class
|
||||
*
|
||||
* @return object - instance of $class or null if not found
|
||||
*/
|
||||
public function findObjectByTranslatedField($field, $value, $class)
|
||||
{
|
||||
$entity = null;
|
||||
$meta = $this->_em->getClassMetadata($class);
|
||||
$translationMeta = $this->getClassMetadata(); // table inheritance support
|
||||
if ($meta->hasField($field)) {
|
||||
$dql = "SELECT trans.foreignKey FROM {$translationMeta->rootEntityName} trans";
|
||||
$dql .= ' WHERE trans.objectClass = :class';
|
||||
$dql .= ' AND trans.field = :field';
|
||||
$dql .= ' AND trans.content = :value';
|
||||
$q = $this->_em->createQuery($dql);
|
||||
$q->setParameters(compact('class', 'field', 'value'));
|
||||
$q->setMaxResults(1);
|
||||
$result = $q->getArrayResult();
|
||||
$id = count($result) ? $result[0]['foreignKey'] : null;
|
||||
|
||||
if ($id) {
|
||||
$entity = $this->_em->find($class, $id);
|
||||
}
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all translations with all translatable
|
||||
* fields by a given entity primary key
|
||||
*
|
||||
* @param mixed $id - primary key value of an entity
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function findTranslationsByObjectId($id)
|
||||
{
|
||||
$result = array();
|
||||
if ($id) {
|
||||
$translationMeta = $this->getClassMetadata(); // table inheritance support
|
||||
$qb = $this->_em->createQueryBuilder();
|
||||
$qb->select('trans.content, trans.field, trans.locale')
|
||||
->from($translationMeta->rootEntityName, 'trans')
|
||||
->where('trans.foreignKey = :entityId')
|
||||
->orderBy('trans.locale');
|
||||
$q = $qb->getQuery();
|
||||
$data = $q->execute(
|
||||
array('entityId' => $id),
|
||||
Query::HYDRATE_ARRAY
|
||||
);
|
||||
|
||||
if ($data && is_array($data) && count($data)) {
|
||||
foreach ($data as $row) {
|
||||
$result[$row['locale']][$row['field']] = $row['content'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently used TranslatableListener
|
||||
*
|
||||
* @throws \Gedmo\Exception\RuntimeException - if listener is not found
|
||||
*
|
||||
* @return TranslatableListener
|
||||
*/
|
||||
private function getTranslatableListener()
|
||||
{
|
||||
if (!$this->listener) {
|
||||
foreach ($this->_em->getEventManager()->getListeners() as $event => $listeners) {
|
||||
foreach ($listeners as $hash => $listener) {
|
||||
if ($listener instanceof TranslatableListener) {
|
||||
return $this->listener = $listener;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new \Gedmo\Exception\RuntimeException('The translation listener could not be found');
|
||||
}
|
||||
|
||||
return $this->listener;
|
||||
}
|
||||
}
|
||||
30
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/Translation.php
vendored
Normal file
30
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/Translation.php
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping\Table;
|
||||
use Doctrine\ORM\Mapping\Index;
|
||||
use Doctrine\ORM\Mapping\UniqueConstraint;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
|
||||
/**
|
||||
* Gedmo\Translatable\Entity\Translation
|
||||
*
|
||||
* @Table(
|
||||
* name="ext_translations",
|
||||
* options={"row_format":"DYNAMIC"},
|
||||
* indexes={@Index(name="translations_lookup_idx", columns={
|
||||
* "locale", "object_class", "foreign_key"
|
||||
* })},
|
||||
* uniqueConstraints={@UniqueConstraint(name="lookup_unique_idx", columns={
|
||||
* "locale", "object_class", "field", "foreign_key"
|
||||
* })}
|
||||
* )
|
||||
* @Entity(repositoryClass="Gedmo\Translatable\Entity\Repository\TranslationRepository")
|
||||
*/
|
||||
class Translation extends MappedSuperclass\AbstractTranslation
|
||||
{
|
||||
/**
|
||||
* All required columns are mapped through inherited superclass
|
||||
*/
|
||||
}
|
||||
78
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Hydrator/ORM/ObjectHydrator.php
vendored
Normal file
78
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Hydrator/ORM/ObjectHydrator.php
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable\Hydrator\ORM;
|
||||
|
||||
use Gedmo\Translatable\TranslatableListener;
|
||||
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>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class ObjectHydrator extends BaseObjectHydrator
|
||||
{
|
||||
/**
|
||||
* State of skipOnLoad for listener between hydrations
|
||||
*
|
||||
* @see ObjectHydrator::prepare()
|
||||
* @see ObjectHydrator::cleanup()
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $savedSkipOnLoad;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepare()
|
||||
{
|
||||
$listener = $this->getTranslatableListener();
|
||||
$this->savedSkipOnLoad = $listener->isSkipOnLoad();
|
||||
$listener->setSkipOnLoad(true);
|
||||
parent::prepare();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function cleanup()
|
||||
{
|
||||
parent::cleanup();
|
||||
$listener = $this->getTranslatableListener();
|
||||
$listener->setSkipOnLoad($this->savedSkipOnLoad !== null ? $this->savedSkipOnLoad : false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently used TranslatableListener
|
||||
*
|
||||
* @throws \Gedmo\Exception\RuntimeException - if listener is not found
|
||||
*
|
||||
* @return TranslatableListener
|
||||
*/
|
||||
protected function getTranslatableListener()
|
||||
{
|
||||
$translatableListener = null;
|
||||
foreach ($this->_em->getEventManager()->getListeners() as $event => $listeners) {
|
||||
foreach ($listeners as $hash => $listener) {
|
||||
if ($listener instanceof TranslatableListener) {
|
||||
$translatableListener = $listener;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($translatableListener) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($translatableListener)) {
|
||||
throw new \Gedmo\Exception\RuntimeException('The translation listener could not be found');
|
||||
}
|
||||
|
||||
return $translatableListener;
|
||||
}
|
||||
}
|
||||
78
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Hydrator/ORM/SimpleObjectHydrator.php
vendored
Normal file
78
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Hydrator/ORM/SimpleObjectHydrator.php
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable\Hydrator\ORM;
|
||||
|
||||
use Gedmo\Translatable\TranslatableListener;
|
||||
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>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class SimpleObjectHydrator extends BaseSimpleObjectHydrator
|
||||
{
|
||||
/**
|
||||
* State of skipOnLoad for listener between hydrations
|
||||
*
|
||||
* @see SimpleObjectHydrator::prepare()
|
||||
* @see SimpleObjectHydrator::cleanup()
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $savedSkipOnLoad;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepare()
|
||||
{
|
||||
$listener = $this->getTranslatableListener();
|
||||
$this->savedSkipOnLoad = $listener->isSkipOnLoad();
|
||||
$listener->setSkipOnLoad(true);
|
||||
parent::prepare();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function cleanup()
|
||||
{
|
||||
parent::cleanup();
|
||||
$listener = $this->getTranslatableListener();
|
||||
$listener->setSkipOnLoad($this->savedSkipOnLoad !== null ? $this->savedSkipOnLoad : false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently used TranslatableListener
|
||||
*
|
||||
* @throws \Gedmo\Exception\RuntimeException - if listener is not found
|
||||
*
|
||||
* @return TranslatableListener
|
||||
*/
|
||||
protected function getTranslatableListener()
|
||||
{
|
||||
$translatableListener = null;
|
||||
foreach ($this->_em->getEventManager()->getListeners() as $event => $listeners) {
|
||||
foreach ($listeners as $hash => $listener) {
|
||||
if ($listener instanceof TranslatableListener) {
|
||||
$translatableListener = $listener;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($translatableListener) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($translatableListener)) {
|
||||
throw new \Gedmo\Exception\RuntimeException('The translation listener could not be found');
|
||||
}
|
||||
|
||||
return $translatableListener;
|
||||
}
|
||||
}
|
||||
117
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Annotation.php
vendored
Normal file
117
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Annotation.php
vendored
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable\Mapping\Driver;
|
||||
|
||||
use Gedmo\Mapping\Driver\AbstractAnnotationDriver;
|
||||
use Gedmo\Exception\InvalidMappingException;
|
||||
|
||||
/**
|
||||
* This is an annotation mapping driver for Translatable
|
||||
* behavioral extension. Used for extraction of extended
|
||||
* metadata from Annotations specifically for Translatable
|
||||
* extension.
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class Annotation extends AbstractAnnotationDriver
|
||||
{
|
||||
/**
|
||||
* Annotation to identity translation entity to be used for translation storage
|
||||
*/
|
||||
const ENTITY_CLASS = 'Gedmo\\Mapping\\Annotation\\TranslationEntity';
|
||||
|
||||
/**
|
||||
* Annotation to identify field as translatable
|
||||
*/
|
||||
const TRANSLATABLE = 'Gedmo\\Mapping\\Annotation\\Translatable';
|
||||
|
||||
/**
|
||||
* Annotation to identify field which can store used locale or language
|
||||
* alias is LANGUAGE
|
||||
*/
|
||||
const LOCALE = 'Gedmo\\Mapping\\Annotation\\Locale';
|
||||
|
||||
/**
|
||||
* Annotation to identify field which can store used locale or language
|
||||
* alias is LOCALE
|
||||
*/
|
||||
const LANGUAGE = 'Gedmo\\Mapping\\Annotation\\Language';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function readExtendedMetadata($meta, array &$config)
|
||||
{
|
||||
$class = $this->getMetaReflectionClass($meta);
|
||||
// class annotations
|
||||
if ($annot = $this->reader->getClassAnnotation($class, self::ENTITY_CLASS)) {
|
||||
if (!$cl = $this->getRelatedClassName($meta, $annot->class)) {
|
||||
throw new InvalidMappingException("Translation class: {$annot->class} does not exist.");
|
||||
}
|
||||
$config['translationClass'] = $cl;
|
||||
}
|
||||
|
||||
// property annotations
|
||||
foreach ($class->getProperties() as $property) {
|
||||
if ($meta->isMappedSuperclass && !$property->isPrivate() ||
|
||||
$meta->isInheritedField($property->name) ||
|
||||
isset($meta->associationMappings[$property->name]['inherited'])
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
// translatable property
|
||||
if ($translatable = $this->reader->getPropertyAnnotation($property, self::TRANSLATABLE)) {
|
||||
$field = $property->getName();
|
||||
if (!$meta->hasField($field)) {
|
||||
throw new InvalidMappingException("Unable to find translatable [{$field}] as mapped property in entity - {$meta->name}");
|
||||
}
|
||||
// fields cannot be overrided and throws mapping exception
|
||||
$config['fields'][] = $field;
|
||||
if (isset($translatable->fallback)) {
|
||||
$config['fallback'][$field] = $translatable->fallback;
|
||||
}
|
||||
}
|
||||
// locale property
|
||||
if ($this->reader->getPropertyAnnotation($property, self::LOCALE)) {
|
||||
$field = $property->getName();
|
||||
if ($meta->hasField($field)) {
|
||||
throw new InvalidMappingException("Locale field [{$field}] should not be mapped as column property in entity - {$meta->name}, since it makes no sense");
|
||||
}
|
||||
$config['locale'] = $field;
|
||||
} elseif ($this->reader->getPropertyAnnotation($property, self::LANGUAGE)) {
|
||||
$field = $property->getName();
|
||||
if ($meta->hasField($field)) {
|
||||
throw new InvalidMappingException("Language field [{$field}] should not be mapped as column property in entity - {$meta->name}, since it makes no sense");
|
||||
}
|
||||
$config['locale'] = $field;
|
||||
}
|
||||
}
|
||||
|
||||
// Embedded entity
|
||||
if (property_exists($meta, 'embeddedClasses') && $meta->embeddedClasses) {
|
||||
foreach ($meta->embeddedClasses as $propertyName => $embeddedClassInfo) {
|
||||
if ($meta->isInheritedEmbeddedClass($propertyName)) {
|
||||
continue;
|
||||
}
|
||||
$embeddedClass = new \ReflectionClass($embeddedClassInfo['class']);
|
||||
foreach ($embeddedClass->getProperties() as $embeddedProperty) {
|
||||
if ($translatable = $this->reader->getPropertyAnnotation($embeddedProperty, self::TRANSLATABLE)) {
|
||||
$field = $propertyName . '.' . $embeddedProperty->getName();
|
||||
|
||||
$config['fields'][] = $field;
|
||||
if (isset($translatable->fallback)) {
|
||||
$config['fallback'][$field] = $translatable->fallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$meta->isMappedSuperclass && $config) {
|
||||
if (is_array($meta->identifier) && count($meta->identifier) > 1) {
|
||||
throw new InvalidMappingException("Translatable does not support composite identifiers in class - {$meta->name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
108
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Xml.php
vendored
Normal file
108
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Xml.php
vendored
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable\Mapping\Driver;
|
||||
|
||||
use Gedmo\Mapping\Driver\Xml as BaseXml;
|
||||
use Gedmo\Exception\InvalidMappingException;
|
||||
|
||||
/**
|
||||
* This is a xml mapping driver for Translatable
|
||||
* behavioral extension. Used for extraction of extended
|
||||
* metadata from xml specifically for Translatable
|
||||
* extension.
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @author Miha Vrhovnik <miha.vrhovnik@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class Xml extends BaseXml
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function readExtendedMetadata($meta, array &$config)
|
||||
{
|
||||
/**
|
||||
* @var \SimpleXmlElement $xml
|
||||
*/
|
||||
$xml = $this->_getMapping($meta->name);
|
||||
$xmlDoctrine = $xml;
|
||||
|
||||
$xml = $xml->children(self::GEDMO_NAMESPACE_URI);
|
||||
|
||||
if (($xmlDoctrine->getName() == 'entity' || $xmlDoctrine->getName() == 'mapped-superclass')) {
|
||||
if ($xml->count() && isset($xml->translation)) {
|
||||
/**
|
||||
* @var \SimpleXmlElement $data
|
||||
*/
|
||||
$data = $xml->translation;
|
||||
if ($this->_isAttributeSet($data, 'locale')) {
|
||||
$config['locale'] = $this->_getAttribute($data, 'locale');
|
||||
} elseif ($this->_isAttributeSet($data, 'language')) {
|
||||
$config['locale'] = $this->_getAttribute($data, 'language');
|
||||
}
|
||||
if ($this->_isAttributeSet($data, 'entity')) {
|
||||
$entity = $this->_getAttribute($data, 'entity');
|
||||
if (!$cl = $this->getRelatedClassName($meta, $entity)) {
|
||||
throw new InvalidMappingException("Translation entity class: {$entity} does not exist.");
|
||||
}
|
||||
$config['translationClass'] = $cl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (property_exists($meta, 'embeddedClasses') && $meta->embeddedClasses) {
|
||||
foreach ($meta->embeddedClasses as $propertyName => $embeddedClassInfo) {
|
||||
if ($meta->isInheritedEmbeddedClass($propertyName)) {
|
||||
continue;
|
||||
}
|
||||
$xmlEmbeddedClass = $this->_getMapping($embeddedClassInfo['class']);
|
||||
$this->inspectElementsForTranslatableFields($xmlEmbeddedClass, $config, $propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
if ($xmlDoctrine->{'attribute-overrides'}->count() > 0) {
|
||||
foreach ($xmlDoctrine->{'attribute-overrides'}->{'attribute-override'} as $overrideMapping) {
|
||||
$this->buildFieldConfiguration($this->_getAttribute($overrideMapping, 'name'), $overrideMapping->field, $config);
|
||||
}
|
||||
}
|
||||
|
||||
$this->inspectElementsForTranslatableFields($xmlDoctrine, $config);
|
||||
|
||||
if (!$meta->isMappedSuperclass && $config) {
|
||||
if (is_array($meta->identifier) && count($meta->identifier) > 1) {
|
||||
throw new InvalidMappingException("Translatable does not support composite identifiers in class - {$meta->name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function inspectElementsForTranslatableFields(\SimpleXMLElement $xml, array &$config, $prefix = null)
|
||||
{
|
||||
if (!isset($xml->field)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($xml->field as $mapping) {
|
||||
$mappingDoctrine = $mapping;
|
||||
|
||||
$fieldName = $this->_getAttribute($mappingDoctrine, 'name');
|
||||
if ($prefix !== null) {
|
||||
$fieldName = $prefix . '.' . $fieldName;
|
||||
}
|
||||
$this->buildFieldConfiguration($fieldName, $mapping, $config);
|
||||
}
|
||||
}
|
||||
|
||||
private function buildFieldConfiguration($fieldName, \SimpleXMLElement $mapping, array &$config)
|
||||
{
|
||||
$mapping = $mapping->children(self::GEDMO_NAMESPACE_URI);
|
||||
if ($mapping->count() > 0 && isset($mapping->translatable)) {
|
||||
$config['fields'][] = $fieldName;
|
||||
/** @var \SimpleXmlElement $data */
|
||||
$data = $mapping->translatable;
|
||||
if ($this->_isAttributeSet($data, 'fallback')) {
|
||||
$config['fallback'][$fieldName] = 'true' == $this->_getAttribute($data, 'fallback') ? true : false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Yaml.php
vendored
Normal file
88
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Yaml.php
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable\Mapping\Driver;
|
||||
|
||||
use Gedmo\Mapping\Driver\File;
|
||||
use Gedmo\Mapping\Driver;
|
||||
use Gedmo\Exception\InvalidMappingException;
|
||||
|
||||
/**
|
||||
* This is a yaml mapping driver for Translatable
|
||||
* behavioral extension. Used for extraction of extended
|
||||
* metadata from yaml specifically for Translatable
|
||||
* extension.
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class Yaml extends File implements Driver
|
||||
{
|
||||
/**
|
||||
* File extension
|
||||
* @var string
|
||||
*/
|
||||
protected $_extension = '.dcm.yml';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function readExtendedMetadata($meta, array &$config)
|
||||
{
|
||||
$mapping = $this->_getMapping($meta->name);
|
||||
|
||||
if (isset($mapping['gedmo'])) {
|
||||
$classMapping = $mapping['gedmo'];
|
||||
if (isset($classMapping['translation']['entity'])) {
|
||||
$translationEntity = $classMapping['translation']['entity'];
|
||||
if (!$cl = $this->getRelatedClassName($meta, $translationEntity)) {
|
||||
throw new InvalidMappingException("Translation entity class: {$translationEntity} does not exist.");
|
||||
}
|
||||
$config['translationClass'] = $cl;
|
||||
}
|
||||
if (isset($classMapping['translation']['locale'])) {
|
||||
$config['locale'] = $classMapping['translation']['locale'];
|
||||
} elseif (isset($classMapping['translation']['language'])) {
|
||||
$config['locale'] = $classMapping['translation']['language'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($mapping['fields'])) {
|
||||
foreach ($mapping['fields'] as $field => $fieldMapping) {
|
||||
$this->buildFieldConfiguration($field, $fieldMapping, $config);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($mapping['attributeOverride'])) {
|
||||
foreach ($mapping['attributeOverride'] as $field => $overrideMapping) {
|
||||
$this->buildFieldConfiguration($field, $overrideMapping, $config);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$meta->isMappedSuperclass && $config) {
|
||||
if (is_array($meta->identifier) && count($meta->identifier) > 1) {
|
||||
throw new InvalidMappingException("Translatable does not support composite identifiers in class - {$meta->name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function _loadMappingFile($file)
|
||||
{
|
||||
return \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file));
|
||||
}
|
||||
|
||||
private function buildFieldConfiguration($field, $fieldMapping, array &$config)
|
||||
{
|
||||
if (is_array($fieldMapping) && isset($fieldMapping['gedmo'])) {
|
||||
if (in_array('translatable', $fieldMapping['gedmo']) || isset($fieldMapping['gedmo']['translatable'])) {
|
||||
// fields cannot be overrided and throws mapping exception
|
||||
$config['fields'][] = $field;
|
||||
if (isset($fieldMapping['gedmo']['translatable']['fallback'])) {
|
||||
$config['fallback'][$field] = $fieldMapping['gedmo']['translatable']['fallback'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
205
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/Adapter/ODM.php
vendored
Normal file
205
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/Adapter/ODM.php
vendored
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable\Mapping\Event\Adapter;
|
||||
|
||||
use Gedmo\Mapping\Event\Adapter\ODM as BaseAdapterODM;
|
||||
use Gedmo\Tool\Wrapper\AbstractWrapper;
|
||||
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo;
|
||||
use Doctrine\ODM\MongoDB\Cursor;
|
||||
use Gedmo\Translatable\Mapping\Event\TranslatableAdapter;
|
||||
|
||||
/**
|
||||
* Doctrine event adapter for ODM adapted
|
||||
* for Translatable behavior
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
final class ODM extends BaseAdapterODM implements TranslatableAdapter
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function usesPersonalTranslation($translationClassName)
|
||||
{
|
||||
return $this
|
||||
->getObjectManager()
|
||||
->getClassMetadata($translationClassName)
|
||||
->getReflectionClass()
|
||||
->isSubclassOf('Gedmo\Translatable\Document\MappedSuperclass\AbstractPersonalTranslation')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getDefaultTranslationClass()
|
||||
{
|
||||
return 'Gedmo\\Translatable\\Document\\Translation';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function loadTranslations($object, $translationClass, $locale, $objectClass)
|
||||
{
|
||||
$dm = $this->getObjectManager();
|
||||
$wrapped = AbstractWrapper::wrap($object, $dm);
|
||||
$result = array();
|
||||
|
||||
if ($this->usesPersonalTranslation($translationClass)) {
|
||||
// first try to load it using collection
|
||||
foreach ($wrapped->getMetadata()->fieldMappings as $mapping) {
|
||||
$isRightCollection = isset($mapping['association'])
|
||||
&& $mapping['association'] === ClassMetadataInfo::REFERENCE_MANY
|
||||
&& $mapping['targetDocument'] === $translationClass
|
||||
&& $mapping['mappedBy'] === 'object'
|
||||
;
|
||||
if ($isRightCollection) {
|
||||
$collection = $wrapped->getPropertyValue($mapping['fieldName']);
|
||||
foreach ($collection as $trans) {
|
||||
if ($trans->getLocale() === $locale) {
|
||||
$result[] = array(
|
||||
'field' => $trans->getField(),
|
||||
'content' => $trans->getContent(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
$q = $dm
|
||||
->createQueryBuilder($translationClass)
|
||||
->field('object.$id')->equals($wrapped->getIdentifier())
|
||||
->field('locale')->equals($locale)
|
||||
->getQuery()
|
||||
;
|
||||
} else {
|
||||
// load translated content for all translatable fields
|
||||
// construct query
|
||||
$q = $dm
|
||||
->createQueryBuilder($translationClass)
|
||||
->field('foreignKey')->equals($wrapped->getIdentifier())
|
||||
->field('locale')->equals($locale)
|
||||
->field('objectClass')->equals($objectClass)
|
||||
->getQuery()
|
||||
;
|
||||
}
|
||||
$q->setHydrate(false);
|
||||
$result = $q->execute();
|
||||
if ($result instanceof Cursor) {
|
||||
$result = $result->toArray();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function findTranslation(AbstractWrapper $wrapped, $locale, $field, $translationClass, $objectClass)
|
||||
{
|
||||
$dm = $this->getObjectManager();
|
||||
$qb = $dm
|
||||
->createQueryBuilder($translationClass)
|
||||
->field('locale')->equals($locale)
|
||||
->field('field')->equals($field)
|
||||
->limit(1)
|
||||
;
|
||||
if ($this->usesPersonalTranslation($translationClass)) {
|
||||
$qb->field('object.$id')->equals($wrapped->getIdentifier());
|
||||
} else {
|
||||
$qb->field('foreignKey')->equals($wrapped->getIdentifier());
|
||||
$qb->field('objectClass')->equals($objectClass);
|
||||
}
|
||||
$q = $qb->getQuery();
|
||||
$result = $q->execute();
|
||||
if ($result instanceof Cursor) {
|
||||
$result = current($result->toArray());
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function removeAssociatedTranslations(AbstractWrapper $wrapped, $transClass, $objectClass)
|
||||
{
|
||||
$dm = $this->getObjectManager();
|
||||
$qb = $dm
|
||||
->createQueryBuilder($transClass)
|
||||
->remove()
|
||||
;
|
||||
if ($this->usesPersonalTranslation($transClass)) {
|
||||
$qb->field('object.$id')->equals($wrapped->getIdentifier());
|
||||
} else {
|
||||
$qb->field('foreignKey')->equals($wrapped->getIdentifier());
|
||||
$qb->field('objectClass')->equals($objectClass);
|
||||
}
|
||||
$q = $qb->getQuery();
|
||||
|
||||
return $q->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function insertTranslationRecord($translation)
|
||||
{
|
||||
$dm = $this->getObjectManager();
|
||||
$meta = $dm->getClassMetadata(get_class($translation));
|
||||
$collection = $dm->getDocumentCollection($meta->name);
|
||||
$data = array();
|
||||
|
||||
foreach ($meta->getReflectionProperties() as $fieldName => $reflProp) {
|
||||
if (!$meta->isIdentifier($fieldName)) {
|
||||
$data[$meta->fieldMappings[$fieldName]['name']] = $reflProp->getValue($translation);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$collection->insert($data)) {
|
||||
throw new \Gedmo\Exception\RuntimeException('Failed to insert new Translation record');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getTranslationValue($object, $field, $value = false)
|
||||
{
|
||||
$dm = $this->getObjectManager();
|
||||
$wrapped = AbstractWrapper::wrap($object, $dm);
|
||||
$meta = $wrapped->getMetadata();
|
||||
$mapping = $meta->getFieldMapping($field);
|
||||
$type = $this->getType($mapping['type']);
|
||||
if ($value === false) {
|
||||
$value = $wrapped->getPropertyValue($field);
|
||||
}
|
||||
|
||||
return $type->convertToDatabaseValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setTranslationValue($object, $field, $value)
|
||||
{
|
||||
$dm = $this->getObjectManager();
|
||||
$wrapped = AbstractWrapper::wrap($object, $dm);
|
||||
$meta = $wrapped->getMetadata();
|
||||
$mapping = $meta->getFieldMapping($field);
|
||||
$type = $this->getType($mapping['type']);
|
||||
|
||||
$value = $type->convertToPHPValue($value);
|
||||
$wrapped->setPropertyValue($field, $value);
|
||||
}
|
||||
|
||||
private function getType($type)
|
||||
{
|
||||
// due to change in ODM beta 9
|
||||
return class_exists('Doctrine\ODM\MongoDB\Types\Type') ? \Doctrine\ODM\MongoDB\Types\Type::getType($type)
|
||||
: \Doctrine\ODM\MongoDB\Mapping\Types\Type::getType($type);
|
||||
}
|
||||
}
|
||||
260
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/Adapter/ORM.php
vendored
Normal file
260
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/Adapter/ORM.php
vendored
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable\Mapping\Event\Adapter;
|
||||
|
||||
use Doctrine\Common\Proxy\Proxy;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
use Gedmo\Mapping\Event\Adapter\ORM as BaseAdapterORM;
|
||||
use Gedmo\Translatable\Mapping\Event\TranslatableAdapter;
|
||||
use Gedmo\Tool\Wrapper\AbstractWrapper;
|
||||
|
||||
/**
|
||||
* Doctrine event adapter for ORM adapted
|
||||
* for Translatable behavior
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
final class ORM extends BaseAdapterORM implements TranslatableAdapter
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function usesPersonalTranslation($translationClassName)
|
||||
{
|
||||
return $this
|
||||
->getObjectManager()
|
||||
->getClassMetadata($translationClassName)
|
||||
->getReflectionClass()
|
||||
->isSubclassOf('Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getDefaultTranslationClass()
|
||||
{
|
||||
return 'Gedmo\\Translatable\\Entity\\Translation';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function loadTranslations($object, $translationClass, $locale, $objectClass)
|
||||
{
|
||||
$em = $this->getObjectManager();
|
||||
$wrapped = AbstractWrapper::wrap($object, $em);
|
||||
$result = array();
|
||||
if ($this->usesPersonalTranslation($translationClass)) {
|
||||
// first try to load it using collection
|
||||
$found = false;
|
||||
foreach ($wrapped->getMetadata()->associationMappings as $assoc) {
|
||||
$isRightCollection = $assoc['targetEntity'] === $translationClass
|
||||
&& $assoc['mappedBy'] === 'object'
|
||||
&& $assoc['type'] === ClassMetadataInfo::ONE_TO_MANY
|
||||
;
|
||||
if ($isRightCollection) {
|
||||
$collection = $wrapped->getPropertyValue($assoc['fieldName']);
|
||||
foreach ($collection as $trans) {
|
||||
if ($trans->getLocale() === $locale) {
|
||||
$result[] = array(
|
||||
'field' => $trans->getField(),
|
||||
'content' => $trans->getContent(),
|
||||
);
|
||||
}
|
||||
}
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if collection is not set, fetch it through relation
|
||||
if (!$found) {
|
||||
$dql = 'SELECT t.content, t.field FROM '.$translationClass.' t';
|
||||
$dql .= ' WHERE t.locale = :locale';
|
||||
$dql .= ' AND t.object = :object';
|
||||
|
||||
$q = $em->createQuery($dql);
|
||||
$q->setParameters(compact('object', 'locale'));
|
||||
$result = $q->getArrayResult();
|
||||
}
|
||||
} else {
|
||||
// load translated content for all translatable fields
|
||||
$objectId = $this->foreignKey($wrapped->getIdentifier(), $translationClass);
|
||||
// construct query
|
||||
$dql = 'SELECT t.content, t.field FROM '.$translationClass.' t';
|
||||
$dql .= ' WHERE t.foreignKey = :objectId';
|
||||
$dql .= ' AND t.locale = :locale';
|
||||
$dql .= ' AND t.objectClass = :objectClass';
|
||||
// fetch results
|
||||
$q = $em->createQuery($dql);
|
||||
$q->setParameters(compact('objectId', 'locale', 'objectClass'));
|
||||
$result = $q->getArrayResult();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms foreigh key of translation to appropriate PHP value
|
||||
* to prevent database level cast
|
||||
*
|
||||
* @param $key - foreign key value
|
||||
* @param $className - translation class name
|
||||
* @return transformed foreign key
|
||||
*/
|
||||
private function foreignKey($key, $className)
|
||||
{
|
||||
$em = $this->getObjectManager();
|
||||
$meta = $em->getClassMetadata($className);
|
||||
$type = Type::getType($meta->getTypeOfField('foreignKey'));
|
||||
switch ($type->getName()) {
|
||||
case Type::BIGINT:
|
||||
case Type::INTEGER:
|
||||
case Type::SMALLINT:
|
||||
return intval($key);
|
||||
default:
|
||||
return (string)$key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function findTranslation(AbstractWrapper $wrapped, $locale, $field, $translationClass, $objectClass)
|
||||
{
|
||||
$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) {
|
||||
$isRequestedTranslation = !$trans instanceof Proxy
|
||||
&& $trans->getLocale() === $locale
|
||||
&& $trans->getField() === $field
|
||||
;
|
||||
if ($isRequestedTranslation) {
|
||||
if ($this->usesPersonalTranslation($translationClass)) {
|
||||
$isRequestedTranslation = $trans->getObject() === $wrapped->getObject();
|
||||
} else {
|
||||
$objectId = $this->foreignKey($wrapped->getIdentifier(), $translationClass);
|
||||
$isRequestedTranslation = $trans->getForeignKey() === $objectId
|
||||
&& $trans->getObjectClass() === $wrapped->getMetadata()->name
|
||||
;
|
||||
}
|
||||
}
|
||||
if ($isRequestedTranslation) {
|
||||
return $trans;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$qb = $em->createQueryBuilder();
|
||||
$qb->select('trans')
|
||||
->from($translationClass, 'trans')
|
||||
->where(
|
||||
'trans.locale = :locale',
|
||||
'trans.field = :field'
|
||||
)
|
||||
;
|
||||
$qb->setParameters(compact('locale', 'field'));
|
||||
if ($this->usesPersonalTranslation($translationClass)) {
|
||||
$qb->andWhere('trans.object = :object');
|
||||
if ($wrapped->getIdentifier()) {
|
||||
$qb->setParameter('object', $wrapped->getObject());
|
||||
} else {
|
||||
$qb->setParameter('object', null);
|
||||
}
|
||||
} else {
|
||||
$qb->andWhere('trans.foreignKey = :objectId');
|
||||
$qb->andWhere('trans.objectClass = :objectClass');
|
||||
$qb->setParameter('objectId', $this->foreignKey($wrapped->getIdentifier(), $translationClass));
|
||||
$qb->setParameter('objectClass', $objectClass);
|
||||
}
|
||||
$q = $qb->getQuery();
|
||||
$q->setMaxResults(1);
|
||||
$result = $q->getResult();
|
||||
|
||||
if ($result) {
|
||||
return array_shift($result);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function removeAssociatedTranslations(AbstractWrapper $wrapped, $transClass, $objectClass)
|
||||
{
|
||||
$qb = $this
|
||||
->getObjectManager()
|
||||
->createQueryBuilder()
|
||||
->delete($transClass, 'trans')
|
||||
;
|
||||
if ($this->usesPersonalTranslation($transClass)) {
|
||||
$qb->where('trans.object = :object');
|
||||
$qb->setParameter('object', $wrapped->getObject());
|
||||
} else {
|
||||
$qb->where(
|
||||
'trans.foreignKey = :objectId',
|
||||
'trans.objectClass = :class'
|
||||
);
|
||||
$qb->setParameter('objectId', $this->foreignKey($wrapped->getIdentifier(), $transClass));
|
||||
$qb->setParameter('class', $objectClass);
|
||||
}
|
||||
|
||||
return $qb->getQuery()->getSingleScalarResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function insertTranslationRecord($translation)
|
||||
{
|
||||
$em = $this->getObjectManager();
|
||||
$meta = $em->getClassMetadata(get_class($translation));
|
||||
$data = array();
|
||||
|
||||
foreach ($meta->getReflectionProperties() as $fieldName => $reflProp) {
|
||||
if (!$meta->isIdentifier($fieldName)) {
|
||||
$data[$meta->getColumnName($fieldName)] = $reflProp->getValue($translation);
|
||||
}
|
||||
}
|
||||
|
||||
$table = $meta->getTableName();
|
||||
if (!$em->getConnection()->insert($table, $data)) {
|
||||
throw new \Gedmo\Exception\RuntimeException('Failed to insert new Translation record');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getTranslationValue($object, $field, $value = false)
|
||||
{
|
||||
$em = $this->getObjectManager();
|
||||
$wrapped = AbstractWrapper::wrap($object, $em);
|
||||
$meta = $wrapped->getMetadata();
|
||||
$type = Type::getType($meta->getTypeOfField($field));
|
||||
if ($value === false) {
|
||||
$value = $wrapped->getPropertyValue($field);
|
||||
}
|
||||
|
||||
return $type->convertToDatabaseValue($value, $em->getConnection()->getDatabasePlatform());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setTranslationValue($object, $field, $value)
|
||||
{
|
||||
$em = $this->getObjectManager();
|
||||
$wrapped = AbstractWrapper::wrap($object, $em);
|
||||
$meta = $wrapped->getMetadata();
|
||||
$type = Type::getType($meta->getTypeOfField($field));
|
||||
$value = $type->convertToPHPValue($value, $em->getConnection()->getDatabasePlatform());
|
||||
$wrapped->setPropertyValue($field, $value);
|
||||
}
|
||||
}
|
||||
96
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/TranslatableAdapter.php
vendored
Normal file
96
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/TranslatableAdapter.php
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable\Mapping\Event;
|
||||
|
||||
use Gedmo\Mapping\Event\AdapterInterface;
|
||||
use Gedmo\Tool\Wrapper\AbstractWrapper;
|
||||
|
||||
/**
|
||||
* Doctrine event adapter interface
|
||||
* for Translatable behavior
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
interface TranslatableAdapter extends AdapterInterface
|
||||
{
|
||||
/**
|
||||
* Checks if $translationClassName is a subclass
|
||||
* of personal translation
|
||||
*
|
||||
* @param string $translationClassName
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function usesPersonalTranslation($translationClassName);
|
||||
|
||||
/**
|
||||
* Get default LogEntry class used to store the logs
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultTranslationClass();
|
||||
|
||||
/**
|
||||
* Load the translations for a given object
|
||||
*
|
||||
* @param object $object
|
||||
* @param string $translationClass
|
||||
* @param string $locale
|
||||
* @param string $objectClass
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function loadTranslations($object, $translationClass, $locale, $objectClass);
|
||||
|
||||
/**
|
||||
* Search for existing translation record
|
||||
*
|
||||
* @param AbstractWrapper $wrapped
|
||||
* @param string $locale
|
||||
* @param string $field
|
||||
* @param string $translationClass
|
||||
* @param string $objectClass
|
||||
*
|
||||
* @return mixed - null if nothing is found, Translation otherwise
|
||||
*/
|
||||
public function findTranslation(AbstractWrapper $wrapped, $locale, $field, $translationClass, $objectClass);
|
||||
|
||||
/**
|
||||
* Removes all associated translations for given object
|
||||
*
|
||||
* @param AbstractWrapper $wrapped
|
||||
* @param string $transClass
|
||||
* @param string $objectClass
|
||||
*/
|
||||
public function removeAssociatedTranslations(AbstractWrapper $wrapped, $transClass, $objectClass);
|
||||
|
||||
/**
|
||||
* Inserts the translation record
|
||||
*
|
||||
* @param object $translation
|
||||
*/
|
||||
public function insertTranslationRecord($translation);
|
||||
|
||||
/**
|
||||
* Get the transformed value for translation
|
||||
* storage
|
||||
*
|
||||
* @param object $object
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getTranslationValue($object, $field, $value = false);
|
||||
|
||||
/**
|
||||
* Transform the value from database
|
||||
* for translation
|
||||
*
|
||||
* @param object $object
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setTranslationValue($object, $field, $value);
|
||||
}
|
||||
472
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Query/TreeWalker/TranslationWalker.php
vendored
Normal file
472
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Query/TreeWalker/TranslationWalker.php
vendored
Normal file
|
|
@ -0,0 +1,472 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable\Query\TreeWalker;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Gedmo\Translatable\Mapping\Event\Adapter\ORM as TranslatableEventAdapter;
|
||||
use Gedmo\Translatable\TranslatableListener;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\AST\SelectStatement;
|
||||
use Doctrine\ORM\Query\Exec\SingleSelectExecutor;
|
||||
use Doctrine\ORM\Query\AST\RangeVariableDeclaration;
|
||||
use Doctrine\ORM\Query\AST\Join;
|
||||
use Doctrine\DBAL\Platforms\MySqlPlatform;
|
||||
use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
|
||||
|
||||
/**
|
||||
* 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 TranslatableListener
|
||||
* and skip postLoad event which would cause automatic retranslation
|
||||
* of the fields.
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class TranslationWalker extends SqlWalker
|
||||
{
|
||||
/**
|
||||
* Name for translation fallback hint
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
const HINT_TRANSLATION_FALLBACKS = '__gedmo.translatable.stored.fallbacks';
|
||||
|
||||
/**
|
||||
* Customized object hydrator name
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
const HYDRATE_OBJECT_TRANSLATION = '__gedmo.translatable.object.hydrator';
|
||||
|
||||
/**
|
||||
* Customized object hydrator name
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
const HYDRATE_SIMPLE_OBJECT_TRANSLATION = '__gedmo.translatable.simple_object.hydrator';
|
||||
|
||||
/**
|
||||
* Stores all component references from select clause
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $translatedComponents = array();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* List of joins for translated components in query
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $components = array();
|
||||
|
||||
/**
|
||||
* {@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->getTranslatableListener();
|
||||
$this->extractTranslatedComponents($queryComponents);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getExecutor($AST)
|
||||
{
|
||||
// If it's not a Select, the TreeWalker ought to skip it, and just return the parent.
|
||||
// @see https://github.com/Atlantic18/DoctrineExtensions/issues/2013
|
||||
if (!$AST instanceof SelectStatement) {
|
||||
return parent::getExecutor($AST);
|
||||
}
|
||||
$this->prepareTranslatedComponents();
|
||||
|
||||
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 ($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);
|
||||
$result = $this->replace($this->replacements, $result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function walkFromClause($fromClause)
|
||||
{
|
||||
$result = parent::walkFromClause($fromClause);
|
||||
$result .= $this->joinTranslations($fromClause);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function walkWhereClause($whereClause)
|
||||
{
|
||||
$result = parent::walkWhereClause($whereClause);
|
||||
|
||||
return $this->replace($this->replacements, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function walkHavingClause($havingClause)
|
||||
{
|
||||
$result = parent::walkHavingClause($havingClause);
|
||||
|
||||
return $this->replace($this->replacements, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function walkOrderByClause($orderByClause)
|
||||
{
|
||||
$result = parent::walkOrderByClause($orderByClause);
|
||||
|
||||
return $this->replace($this->replacements, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function walkSubselect($subselect)
|
||||
{
|
||||
$result = parent::walkSubselect($subselect);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function walkSubselectFromClause($subselectFromClause)
|
||||
{
|
||||
$result = parent::walkSubselectFromClause($subselectFromClause);
|
||||
$result .= $this->joinTranslations($subselectFromClause);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function walkSimpleSelectClause($simpleSelectClause)
|
||||
{
|
||||
$result = parent::walkSimpleSelectClause($simpleSelectClause);
|
||||
|
||||
return $this->replace($this->replacements, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function walkGroupByClause($groupByClause)
|
||||
{
|
||||
$result = parent::walkGroupByClause($groupByClause);
|
||||
|
||||
return $this->replace($this->replacements, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks from clause, and creates translation joins
|
||||
* for the translated components
|
||||
*
|
||||
* @param \Doctrine\ORM\Query\AST\FromClause $from
|
||||
* @return string
|
||||
*/
|
||||
private function joinTranslations($from)
|
||||
{
|
||||
$result = '';
|
||||
foreach ($from->identificationVariableDeclarations as $decl) {
|
||||
if ($decl->rangeVariableDeclaration instanceof RangeVariableDeclaration) {
|
||||
if (isset($this->components[$decl->rangeVariableDeclaration->aliasIdentificationVariable])) {
|
||||
$result .= $this->components[$decl->rangeVariableDeclaration->aliasIdentificationVariable];
|
||||
}
|
||||
}
|
||||
if (isset($decl->joinVariableDeclarations)) {
|
||||
foreach ($decl->joinVariableDeclarations as $joinDecl) {
|
||||
if ($joinDecl->join instanceof Join) {
|
||||
if (isset($this->components[$joinDecl->join->aliasIdentificationVariable])) {
|
||||
$result .= $this->components[$joinDecl->join->aliasIdentificationVariable];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// based on new changes
|
||||
foreach ($decl->joins as $join) {
|
||||
if ($join instanceof Join) {
|
||||
if (isset($this->components[$join->joinAssociationDeclaration->aliasIdentificationVariable])) {
|
||||
$result .= $this->components[$join->joinAssociationDeclaration->aliasIdentificationVariable];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a left join list for translations
|
||||
* on used query components
|
||||
*
|
||||
* @todo: make it cleaner
|
||||
* @return string
|
||||
*/
|
||||
private function prepareTranslatedComponents()
|
||||
{
|
||||
$q = $this->getQuery();
|
||||
$locale = $q->getHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE);
|
||||
if (!$locale) {
|
||||
// use from listener
|
||||
$locale = $this->listener->getListenerLocale();
|
||||
}
|
||||
$defaultLocale = $this->listener->getDefaultLocale();
|
||||
if ($locale === $defaultLocale && !$this->listener->getPersistDefaultLocaleTranslation()) {
|
||||
// Skip preparation as there's no need to translate anything
|
||||
return;
|
||||
}
|
||||
$em = $this->getEntityManager();
|
||||
$ea = new TranslatableEventAdapter();
|
||||
$ea->setEntityManager($em);
|
||||
$quoteStrategy = $em->getConfiguration()->getQuoteStrategy();
|
||||
$joinStrategy = $q->getHint(TranslatableListener::HINT_INNER_JOIN) ? 'INNER' : 'LEFT';
|
||||
|
||||
foreach ($this->translatedComponents as $dqlAlias => $comp) {
|
||||
/** @var ClassMetadata $meta */
|
||||
$meta = $comp['metadata'];
|
||||
$config = $this->listener->getConfiguration($em, $meta->name);
|
||||
$transClass = $this->listener->getTranslationClass($ea, $meta->name);
|
||||
$transMeta = $em->getClassMetadata($transClass);
|
||||
$transTable = $quoteStrategy->getTableName($transMeta, $this->platform);
|
||||
foreach ($config['fields'] as $field) {
|
||||
$compTblAlias = $this->walkIdentificationVariable($dqlAlias, $field);
|
||||
$tblAlias = $this->getSQLTableAlias('trans'.$compTblAlias.$field);
|
||||
$sql = " {$joinStrategy} JOIN ".$transTable.' '.$tblAlias;
|
||||
$sql .= ' ON '.$tblAlias.'.'.$quoteStrategy->getColumnName('locale', $transMeta, $this->platform)
|
||||
.' = '.$this->conn->quote($locale);
|
||||
$sql .= ' AND '.$tblAlias.'.'.$quoteStrategy->getColumnName('field', $transMeta, $this->platform)
|
||||
.' = '.$this->conn->quote($field);
|
||||
$identifier = $meta->getSingleIdentifierFieldName();
|
||||
$idColName = $quoteStrategy->getColumnName($identifier, $meta, $this->platform);
|
||||
if ($ea->usesPersonalTranslation($transClass)) {
|
||||
$sql .= ' AND '.$tblAlias.'.'.$transMeta->getSingleAssociationJoinColumnName('object')
|
||||
.' = '.$compTblAlias.'.'.$idColName;
|
||||
} else {
|
||||
$sql .= ' AND '.$tblAlias.'.'.$quoteStrategy->getColumnName('objectClass', $transMeta, $this->platform)
|
||||
.' = '.$this->conn->quote($config['useObjectClass']);
|
||||
|
||||
$mappingFK = $transMeta->getFieldMapping('foreignKey');
|
||||
$mappingPK = $meta->getFieldMapping($identifier);
|
||||
$fkColName = $this->getCastedForeignKey($compTblAlias.'.'.$idColName, $mappingFK['type'], $mappingPK['type']);
|
||||
$sql .= ' AND '.$tblAlias.'.'.$quoteStrategy->getColumnName('foreignKey', $transMeta, $this->platform)
|
||||
.' = '.$fkColName;
|
||||
}
|
||||
isset($this->components[$dqlAlias]) ? $this->components[$dqlAlias] .= $sql : $this->components[$dqlAlias] = $sql;
|
||||
|
||||
$originalField = $compTblAlias.'.'.$quoteStrategy->getColumnName($field, $meta, $this->platform);
|
||||
$substituteField = $tblAlias.'.'.$quoteStrategy->getColumnName('content', $transMeta, $this->platform);
|
||||
|
||||
// Treat translation as original field type
|
||||
$fieldMapping = $meta->getFieldMapping($field);
|
||||
if ((($this->platform instanceof MySqlPlatform) &&
|
||||
in_array($fieldMapping["type"], array("decimal"))) ||
|
||||
(!($this->platform instanceof MySqlPlatform) &&
|
||||
!in_array($fieldMapping["type"], array("datetime", "datetimetz", "date", "time")))) {
|
||||
$type = Type::getType($fieldMapping["type"]);
|
||||
$substituteField = 'CAST('.$substituteField.' AS '.$type->getSQLDeclaration($fieldMapping, $this->platform).')';
|
||||
}
|
||||
|
||||
// Fallback to original if was asked for
|
||||
if (($this->needsFallback() && (!isset($config['fallback'][$field]) || $config['fallback'][$field]))
|
||||
|| (!$this->needsFallback() && isset($config['fallback'][$field]) && $config['fallback'][$field])
|
||||
) {
|
||||
$substituteField = 'COALESCE('.$substituteField.', '.$originalField.')';
|
||||
}
|
||||
|
||||
$this->replacements[$originalField] = $substituteField;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if translation fallbacks are needed
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function needsFallback()
|
||||
{
|
||||
$q = $this->getQuery();
|
||||
$fallback = $q->getHint(TranslatableListener::HINT_FALLBACK);
|
||||
if (false === $fallback) {
|
||||
// non overrided
|
||||
$fallback = $this->listener->getTranslationFallback();
|
||||
}
|
||||
|
||||
// applies fallbacks to scalar hydration as well
|
||||
return (bool) $fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for translated components in the select clause
|
||||
*
|
||||
* @param array $queryComponents
|
||||
*/
|
||||
private function extractTranslatedComponents(array $queryComponents)
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
foreach ($queryComponents as $alias => $comp) {
|
||||
if (!isset($comp['metadata'])) {
|
||||
continue;
|
||||
}
|
||||
$meta = $comp['metadata'];
|
||||
$config = $this->listener->getConfiguration($em, $meta->name);
|
||||
if ($config && isset($config['fields'])) {
|
||||
$this->translatedComponents[$alias] = $comp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently used TranslatableListener
|
||||
*
|
||||
* @throws \Gedmo\Exception\RuntimeException - if listener is not found
|
||||
*
|
||||
* @return TranslatableListener
|
||||
*/
|
||||
private function getTranslatableListener()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
foreach ($em->getEventManager()->getListeners() as $event => $listeners) {
|
||||
foreach ($listeners as $hash => $listener) {
|
||||
if ($listener instanceof TranslatableListener) {
|
||||
return $listener;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new \Gedmo\Exception\RuntimeException('The translation listener could not be found');
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces given sql $str with required
|
||||
* results
|
||||
*
|
||||
* @param array $repl
|
||||
* @param string $str
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function replace(array $repl, $str)
|
||||
{
|
||||
foreach ($repl as $target => $result) {
|
||||
$str = preg_replace_callback('/(\s|\()('.$target.')(,?)(\s|\)|$)/smi', function ($m) use ($result) {
|
||||
return $m[1].$result.$m[3].$m[4];
|
||||
}, $str);
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts a foreign key if needed
|
||||
* @NOTE: personal translations manages that for themselves.
|
||||
*
|
||||
* @param $component - a column with an alias to cast
|
||||
* @param $typeFK - translation table foreign key type
|
||||
* @param $typePK - primary key type which references translation table
|
||||
* @return string - modified $component if needed
|
||||
*/
|
||||
private function getCastedForeignKey($component, $typeFK, $typePK)
|
||||
{
|
||||
// the keys are of same type
|
||||
if ($typeFK === $typePK) {
|
||||
return $component;
|
||||
}
|
||||
|
||||
// try to look at postgres casting
|
||||
if ($this->platform instanceof PostgreSqlPlatform) {
|
||||
switch ($typeFK) {
|
||||
case 'string':
|
||||
case 'guid':
|
||||
// need to cast to VARCHAR
|
||||
$component = $component . '::VARCHAR';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// @TODO may add the same thing for MySQL for performance to match index
|
||||
|
||||
return $component;
|
||||
}
|
||||
}
|
||||
34
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Translatable.php
vendored
Normal file
34
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Translatable.php
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable;
|
||||
|
||||
/**
|
||||
* This interface is not necessary but can be implemented for
|
||||
* Entities which in some cases needs to be identified as
|
||||
* Translatable
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
interface Translatable
|
||||
{
|
||||
// use now annotations instead of predefined methods, this interface is not necessary
|
||||
|
||||
/**
|
||||
* @gedmo:TranslationEntity
|
||||
* to specify custom translation class use
|
||||
* class annotation @gedmo:TranslationEntity(class="your\class")
|
||||
*/
|
||||
|
||||
/**
|
||||
* @gedmo:Translatable
|
||||
* to mark the field as translatable,
|
||||
* these fields will be translated
|
||||
*/
|
||||
|
||||
/**
|
||||
* @gedmo:Locale OR @gedmo:Language
|
||||
* to mark the field as locale used to override global
|
||||
* locale settings from TranslatableListener
|
||||
*/
|
||||
}
|
||||
796
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/TranslatableListener.php
vendored
Normal file
796
vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/TranslatableListener.php
vendored
Normal file
|
|
@ -0,0 +1,796 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Translatable;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
use Doctrine\ODM\MongoDB\DocumentManager;
|
||||
use Doctrine\ORM\ORMInvalidArgumentException;
|
||||
use Gedmo\Tool\Wrapper\AbstractWrapper;
|
||||
use Gedmo\Mapping\MappedEventSubscriber;
|
||||
use Gedmo\Translatable\Mapping\Event\TranslatableAdapter;
|
||||
|
||||
/**
|
||||
* The translation listener handles the generation and
|
||||
* loading of translations for entities which implements
|
||||
* the Translatable interface.
|
||||
*
|
||||
* This behavior can impact the performance of your application
|
||||
* since it does an additional query for each field to translate.
|
||||
*
|
||||
* Nevertheless the annotation metadata is properly cached and
|
||||
* it is not a big overhead to lookup all entity annotations since
|
||||
* the caching is activated for metadata
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class TranslatableListener extends MappedEventSubscriber
|
||||
{
|
||||
/**
|
||||
* Query hint to override the fallback of translations
|
||||
* integer 1 for true, 0 false
|
||||
*/
|
||||
const HINT_FALLBACK = 'gedmo.translatable.fallback';
|
||||
|
||||
/**
|
||||
* Query hint to override the fallback locale
|
||||
*/
|
||||
const HINT_TRANSLATABLE_LOCALE = 'gedmo.translatable.locale';
|
||||
|
||||
/**
|
||||
* Query hint to use inner join strategy for translations
|
||||
*/
|
||||
const HINT_INNER_JOIN = 'gedmo.translatable.inner_join.translations';
|
||||
|
||||
/**
|
||||
* Locale which is set on this listener.
|
||||
* If Entity being translated has locale defined it
|
||||
* will override this one
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $locale = 'en_US';
|
||||
|
||||
/**
|
||||
* Default locale, this changes behavior
|
||||
* to not update the original record field if locale
|
||||
* which is used for updating is not default. This
|
||||
* will load the default translation in other locales
|
||||
* if record is not translated yet
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $defaultLocale = 'en_US';
|
||||
|
||||
/**
|
||||
* If this is set to false, when if entity does
|
||||
* not have a translation for requested locale
|
||||
* it will show a blank value
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $translationFallback = false;
|
||||
|
||||
/**
|
||||
* List of translations which do not have the foreign
|
||||
* key generated yet - MySQL case. These translations
|
||||
* will be updated with new keys on postPersist event
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* Tracks locale the objects currently translated in
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $translatedInLocale = array();
|
||||
|
||||
/**
|
||||
* Whether or not, to persist default locale
|
||||
* translation or keep it in original record
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $persistDefaultLocaleTranslation = false;
|
||||
|
||||
/**
|
||||
* Tracks translation object for default locale
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $translationInDefaultLocale = array();
|
||||
|
||||
/**
|
||||
* Specifies the list of events to listen
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'postLoad',
|
||||
'postPersist',
|
||||
'preFlush',
|
||||
'onFlush',
|
||||
'loadClassMetadata',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to skip or not onLoad event
|
||||
*
|
||||
* @param boolean $bool
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setSkipOnLoad($bool)
|
||||
{
|
||||
$this->skipOnLoad = (bool) $bool;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not, to persist default locale
|
||||
* translation or keep it in original record
|
||||
*
|
||||
* @param boolean $bool
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setPersistDefaultLocaleTranslation($bool)
|
||||
{
|
||||
$this->persistDefaultLocaleTranslation = (bool) $bool;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if should persist default locale
|
||||
* translation or keep it in original record
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getPersistDefaultLocaleTranslation()
|
||||
{
|
||||
return (bool) $this->persistDefaultLocaleTranslation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional $translation for pending $oid object
|
||||
* which is being inserted
|
||||
*
|
||||
* @param string $oid
|
||||
* @param object $translation
|
||||
*/
|
||||
public function addPendingTranslationInsert($oid, $translation)
|
||||
{
|
||||
$this->pendingTranslationInserts[$oid][] = $translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps additional metadata
|
||||
*
|
||||
* @param EventArgs $eventArgs
|
||||
*/
|
||||
public function loadClassMetadata(EventArgs $eventArgs)
|
||||
{
|
||||
$ea = $this->getEventAdapter($eventArgs);
|
||||
$this->loadMetadataForObjectClass($ea->getObjectManager(), $eventArgs->getClassMetadata());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the translation class to be used
|
||||
* for the object $class
|
||||
*
|
||||
* @param TranslatableAdapter $ea
|
||||
* @param string $class
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTranslationClass(TranslatableAdapter $ea, $class)
|
||||
{
|
||||
return isset(self::$configurations[$this->name][$class]['translationClass']) ?
|
||||
self::$configurations[$this->name][$class]['translationClass'] :
|
||||
$ea->getDefaultTranslationClass()
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable translation fallback
|
||||
* to original record value
|
||||
*
|
||||
* @param boolean $bool
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the locale to use for translation listener
|
||||
*
|
||||
* @param string $locale
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setTranslatableLocale($locale)
|
||||
{
|
||||
$this->validateLocale($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 static
|
||||
*/
|
||||
public function setDefaultLocale($locale)
|
||||
{
|
||||
$this->validateLocale($locale);
|
||||
$this->defaultLocale = $locale;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default locale
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultLocale()
|
||||
{
|
||||
return $this->defaultLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get currently set global locale, used
|
||||
* extensively during query execution
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getListenerLocale()
|
||||
{
|
||||
return $this->locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the locale to use for translation. Loads object
|
||||
* defined locale first..
|
||||
*
|
||||
* @param object $object
|
||||
* @param object $meta
|
||||
* @param object $om
|
||||
*
|
||||
* @throws \Gedmo\Exception\RuntimeException - if language or locale property is not
|
||||
* found in entity
|
||||
* @return string
|
||||
*/
|
||||
public function getTranslatableLocale($object, $meta, $om = null)
|
||||
{
|
||||
$locale = $this->locale;
|
||||
if (isset(self::$configurations[$this->name][$meta->name]['locale'])) {
|
||||
/** @var \ReflectionClass $class */
|
||||
$class = $meta->getReflectionClass();
|
||||
$reflectionProperty = $class->getProperty(self::$configurations[$this->name][$meta->name]['locale']);
|
||||
if (!$reflectionProperty) {
|
||||
$column = self::$configurations[$this->name][$meta->name]['locale'];
|
||||
throw new \Gedmo\Exception\RuntimeException("There is no locale or language property ({$column}) found on object: {$meta->name}");
|
||||
}
|
||||
$reflectionProperty->setAccessible(true);
|
||||
$value = $reflectionProperty->getValue($object);
|
||||
if (is_object($value) && method_exists($value, '__toString')) {
|
||||
$value = (string) $value;
|
||||
}
|
||||
if ($this->isValidLocale($value)) {
|
||||
$locale = $value;
|
||||
}
|
||||
} elseif ($om instanceof DocumentManager) {
|
||||
list($mapping, $parentObject) = $om->getUnitOfWork()->getParentAssociation($object);
|
||||
if ($parentObject != null) {
|
||||
$parentMeta = $om->getClassMetadata(get_class($parentObject));
|
||||
$locale = $this->getTranslatableLocale($parentObject, $parentMeta, $om);
|
||||
}
|
||||
}
|
||||
|
||||
return $locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle translation changes in default locale
|
||||
*
|
||||
* This has to be done in the preFlush because, when an entity has been loaded
|
||||
* in a different locale, no changes will be detected.
|
||||
*
|
||||
* @param EventArgs $args
|
||||
*/
|
||||
public function preFlush(EventArgs $args)
|
||||
{
|
||||
$ea = $this->getEventAdapter($args);
|
||||
$om = $ea->getObjectManager();
|
||||
$uow = $om->getUnitOfWork();
|
||||
|
||||
foreach ($this->translationInDefaultLocale as $oid => $fields) {
|
||||
$trans = reset($fields);
|
||||
if ($ea->usesPersonalTranslation(get_class($trans))) {
|
||||
$entity = $trans->getObject();
|
||||
} else {
|
||||
$entity = $uow->tryGetById($trans->getForeignKey(), $trans->getObjectClass());
|
||||
}
|
||||
|
||||
if (!$entity) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$uow->scheduleForUpdate($entity);
|
||||
} catch (ORMInvalidArgumentException $e) {
|
||||
foreach ($fields as $field => $trans) {
|
||||
$this->removeTranslationInDefaultLocale($oid, $field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for translatable objects being inserted or updated
|
||||
* for further processing
|
||||
*
|
||||
* @param EventArgs $args
|
||||
*/
|
||||
public function onFlush(EventArgs $args)
|
||||
{
|
||||
$ea = $this->getEventAdapter($args);
|
||||
$om = $ea->getObjectManager();
|
||||
$uow = $om->getUnitOfWork();
|
||||
// check all scheduled inserts for Translatable objects
|
||||
foreach ($ea->getScheduledObjectInsertions($uow) as $object) {
|
||||
$meta = $om->getClassMetadata(get_class($object));
|
||||
$config = $this->getConfiguration($om, $meta->name);
|
||||
if (isset($config['fields'])) {
|
||||
$this->handleTranslatableObjectUpdate($ea, $object, true);
|
||||
}
|
||||
}
|
||||
// check all scheduled updates for Translatable entities
|
||||
foreach ($ea->getScheduledObjectUpdates($uow) as $object) {
|
||||
$meta = $om->getClassMetadata(get_class($object));
|
||||
$config = $this->getConfiguration($om, $meta->name);
|
||||
if (isset($config['fields'])) {
|
||||
$this->handleTranslatableObjectUpdate($ea, $object, false);
|
||||
}
|
||||
}
|
||||
// check scheduled deletions for Translatable entities
|
||||
foreach ($ea->getScheduledObjectDeletions($uow) as $object) {
|
||||
$meta = $om->getClassMetadata(get_class($object));
|
||||
$config = $this->getConfiguration($om, $meta->name);
|
||||
if (isset($config['fields'])) {
|
||||
$wrapped = AbstractWrapper::wrap($object, $om);
|
||||
$transClass = $this->getTranslationClass($ea, $meta->name);
|
||||
$ea->removeAssociatedTranslations($wrapped, $transClass, $config['useObjectClass']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for inserted object to update their translation
|
||||
* foreign keys
|
||||
*
|
||||
* @param EventArgs $args
|
||||
*/
|
||||
public function postPersist(EventArgs $args)
|
||||
{
|
||||
$ea = $this->getEventAdapter($args);
|
||||
$om = $ea->getObjectManager();
|
||||
$object = $ea->getObject();
|
||||
$meta = $om->getClassMetadata(get_class($object));
|
||||
// check if entity is tracked by translatable and without foreign key
|
||||
if ($this->getConfiguration($om, $meta->name) && count($this->pendingTranslationInserts)) {
|
||||
$oid = spl_object_hash($object);
|
||||
if (array_key_exists($oid, $this->pendingTranslationInserts)) {
|
||||
// load the pending translations without key
|
||||
$wrapped = AbstractWrapper::wrap($object, $om);
|
||||
$objectId = $wrapped->getIdentifier();
|
||||
$translationClass = $this->getTranslationClass($ea, get_class($object));
|
||||
foreach ($this->pendingTranslationInserts[$oid] as $translation) {
|
||||
if ($ea->usesPersonalTranslation($translationClass)) {
|
||||
$translation->setObject($objectId);
|
||||
} else {
|
||||
$translation->setForeignKey($objectId);
|
||||
}
|
||||
$ea->insertTranslationRecord($translation);
|
||||
}
|
||||
unset($this->pendingTranslationInserts[$oid]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* After object is loaded, listener updates the translations
|
||||
* by currently used locale
|
||||
*
|
||||
* @param EventArgs $args
|
||||
*/
|
||||
public function postLoad(EventArgs $args)
|
||||
{
|
||||
$ea = $this->getEventAdapter($args);
|
||||
$om = $ea->getObjectManager();
|
||||
$object = $ea->getObject();
|
||||
$meta = $om->getClassMetadata(get_class($object));
|
||||
$config = $this->getConfiguration($om, $meta->name);
|
||||
if (isset($config['fields'])) {
|
||||
$locale = $this->getTranslatableLocale($object, $meta, $om);
|
||||
$oid = spl_object_hash($object);
|
||||
$this->translatedInLocale[$oid] = $locale;
|
||||
}
|
||||
|
||||
if ($this->skipOnLoad) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($config['fields']) && ($locale !== $this->defaultLocale || $this->persistDefaultLocaleTranslation)) {
|
||||
// fetch translations
|
||||
$translationClass = $this->getTranslationClass($ea, $config['useObjectClass']);
|
||||
$result = $ea->loadTranslations(
|
||||
$object,
|
||||
$translationClass,
|
||||
$locale,
|
||||
$config['useObjectClass']
|
||||
);
|
||||
// translate object's translatable properties
|
||||
foreach ($config['fields'] as $field) {
|
||||
$translated = '';
|
||||
$is_translated = false;
|
||||
foreach ((array) $result as $entry) {
|
||||
if ($entry['field'] == $field) {
|
||||
$translated = isset($entry['content']) ? $entry['content'] : null;
|
||||
$is_translated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// update translation
|
||||
if ($is_translated
|
||||
|| (!$this->translationFallback && (!isset($config['fallback'][$field]) || !$config['fallback'][$field]))
|
||||
|| ($this->translationFallback && isset($config['fallback'][$field]) && !$config['fallback'][$field])
|
||||
) {
|
||||
$ea->setTranslationValue($object, $field, $translated);
|
||||
// ensure clean changeset
|
||||
$ea->setOriginalObjectProperty(
|
||||
$om->getUnitOfWork(),
|
||||
$oid,
|
||||
$field,
|
||||
$meta->getReflectionProperty($field)->getValue($object)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function getNamespace()
|
||||
{
|
||||
return __NAMESPACE__;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the given locale
|
||||
*
|
||||
* @param string $locale - locale to validate
|
||||
*
|
||||
* @throws \Gedmo\Exception\InvalidArgumentException if locale is not valid
|
||||
*/
|
||||
protected function validateLocale($locale)
|
||||
{
|
||||
if (!$this->isValidLocale($locale)) {
|
||||
throw new \Gedmo\Exception\InvalidArgumentException('Locale or language cannot be empty and must be set through Listener or Entity');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given locale is valid
|
||||
*
|
||||
* @param string $locale - locale to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isValidlocale($locale)
|
||||
{
|
||||
return is_string($locale) && strlen($locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the translation for object being flushed
|
||||
*
|
||||
* @param TranslatableAdapter $ea
|
||||
* @param object $object
|
||||
* @param boolean $isInsert
|
||||
*
|
||||
* @throws \UnexpectedValueException - if locale is not valid, or
|
||||
* primary key is composite, missing or invalid
|
||||
*/
|
||||
private function handleTranslatableObjectUpdate(TranslatableAdapter $ea, $object, $isInsert)
|
||||
{
|
||||
$om = $ea->getObjectManager();
|
||||
$wrapped = AbstractWrapper::wrap($object, $om);
|
||||
$meta = $wrapped->getMetadata();
|
||||
$config = $this->getConfiguration($om, $meta->name);
|
||||
// no need cache, metadata is loaded only once in MetadataFactoryClass
|
||||
$translationClass = $this->getTranslationClass($ea, $config['useObjectClass']);
|
||||
$translationMetadata = $om->getClassMetadata($translationClass);
|
||||
|
||||
// check for the availability of the primary key
|
||||
$objectId = $wrapped->getIdentifier();
|
||||
// load the currently used locale
|
||||
$locale = $this->getTranslatableLocale($object, $meta, $om);
|
||||
|
||||
$uow = $om->getUnitOfWork();
|
||||
$oid = spl_object_hash($object);
|
||||
$changeSet = $ea->getObjectChangeSet($uow, $object);
|
||||
$translatableFields = $config['fields'];
|
||||
foreach ($translatableFields as $field) {
|
||||
$wasPersistedSeparetely = false;
|
||||
$skip = isset($this->translatedInLocale[$oid]) && $locale === $this->translatedInLocale[$oid];
|
||||
$skip = $skip && !isset($changeSet[$field]) && !$this->getTranslationInDefaultLocale($oid, $field);
|
||||
if ($skip) {
|
||||
continue; // locale is same and nothing changed
|
||||
}
|
||||
$translation = null;
|
||||
foreach ($ea->getScheduledObjectInsertions($uow) as $trans) {
|
||||
if ($locale !== $this->defaultLocale
|
||||
&& get_class($trans) === $translationClass
|
||||
&& $trans->getLocale() === $this->defaultLocale
|
||||
&& $trans->getField() === $field
|
||||
&& $this->belongsToObject($ea, $trans, $object)) {
|
||||
$this->setTranslationInDefaultLocale($oid, $field, $trans);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// lookup persisted translations
|
||||
foreach ($ea->getScheduledObjectInsertions($uow) as $trans) {
|
||||
if (get_class($trans) !== $translationClass
|
||||
|| $trans->getLocale() !== $locale
|
||||
|| $trans->getField() !== $field) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($ea->usesPersonalTranslation($translationClass)) {
|
||||
$wasPersistedSeparetely = $trans->getObject() === $object;
|
||||
} else {
|
||||
$wasPersistedSeparetely = $trans->getObjectClass() === $config['useObjectClass']
|
||||
&& $trans->getForeignKey() === $objectId;
|
||||
}
|
||||
|
||||
if ($wasPersistedSeparetely) {
|
||||
$translation = $trans;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check if translation already is created
|
||||
if (!$isInsert && !$translation) {
|
||||
$translation = $ea->findTranslation(
|
||||
$wrapped,
|
||||
$locale,
|
||||
$field,
|
||||
$translationClass,
|
||||
$config['useObjectClass']
|
||||
);
|
||||
}
|
||||
|
||||
// create new translation if translation not already created and locale is different from default locale, otherwise, we have the date in the original record
|
||||
$persistNewTranslation = !$translation
|
||||
&& ($locale !== $this->defaultLocale || $this->persistDefaultLocaleTranslation)
|
||||
;
|
||||
if ($persistNewTranslation) {
|
||||
$translation = $translationMetadata->newInstance();
|
||||
$translation->setLocale($locale);
|
||||
$translation->setField($field);
|
||||
if ($ea->usesPersonalTranslation($translationClass)) {
|
||||
$translation->setObject($object);
|
||||
} else {
|
||||
$translation->setObjectClass($config['useObjectClass']);
|
||||
$translation->setForeignKey($objectId);
|
||||
}
|
||||
}
|
||||
|
||||
if ($translation) {
|
||||
// set the translated field, take value using reflection
|
||||
$content = $ea->getTranslationValue($object, $field);
|
||||
$translation->setContent($content);
|
||||
// check if need to update in database
|
||||
$transWrapper = AbstractWrapper::wrap($translation, $om);
|
||||
if (((is_null($content) && !$isInsert) || is_bool($content) || is_int($content) || is_string($content) || !empty($content)) && ($isInsert || !$transWrapper->getIdentifier() || isset($changeSet[$field]))) {
|
||||
if ($isInsert && !$objectId && !$ea->usesPersonalTranslation($translationClass)) {
|
||||
// if we do not have the primary key yet available
|
||||
// keep this translation in memory to insert it later with foreign key
|
||||
$this->pendingTranslationInserts[spl_object_hash($object)][] = $translation;
|
||||
} else {
|
||||
// persist and compute change set for translation
|
||||
if ($wasPersistedSeparetely) {
|
||||
$ea->recomputeSingleObjectChangeset($uow, $translationMetadata, $translation);
|
||||
} else {
|
||||
$om->persist($translation);
|
||||
$uow->computeChangeSet($translationMetadata, $translation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($isInsert && $this->getTranslationInDefaultLocale($oid, $field) !== null) {
|
||||
// We can't rely on object field value which is created in non-default locale.
|
||||
// If we provide translation for default locale as well, the latter is considered to be trusted
|
||||
// and object content should be overridden.
|
||||
$wrapped->setPropertyValue($field, $this->getTranslationInDefaultLocale($oid, $field)->getContent());
|
||||
$ea->recomputeSingleObjectChangeset($uow, $meta, $object);
|
||||
$this->removeTranslationInDefaultLocale($oid, $field);
|
||||
}
|
||||
}
|
||||
$this->translatedInLocale[$oid] = $locale;
|
||||
// check if we have default translation and need to reset the translation
|
||||
if (!$isInsert && strlen($this->defaultLocale)) {
|
||||
$this->validateLocale($this->defaultLocale);
|
||||
$modifiedChangeSet = $changeSet;
|
||||
foreach ($changeSet as $field => $changes) {
|
||||
if (in_array($field, $translatableFields)) {
|
||||
if ($locale !== $this->defaultLocale) {
|
||||
$ea->setOriginalObjectProperty($uow, $oid, $field, $changes[0]);
|
||||
unset($modifiedChangeSet[$field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$ea->recomputeSingleObjectChangeset($uow, $meta, $object);
|
||||
// cleanup current changeset only if working in a another locale different than de default one, otherwise the changeset will always be reverted
|
||||
if ($locale !== $this->defaultLocale) {
|
||||
$ea->clearObjectChangeSet($uow, $oid);
|
||||
// recompute changeset only if there are changes other than reverted translations
|
||||
if ($modifiedChangeSet || $this->hasTranslationsInDefaultLocale($oid)) {
|
||||
foreach ($modifiedChangeSet as $field => $changes) {
|
||||
$ea->setOriginalObjectProperty($uow, $oid, $field, $changes[0]);
|
||||
}
|
||||
foreach ($translatableFields as $field) {
|
||||
if ($this->getTranslationInDefaultLocale($oid, $field) !== null) {
|
||||
$wrapped->setPropertyValue($field, $this->getTranslationInDefaultLocale($oid, $field)->getContent());
|
||||
$this->removeTranslationInDefaultLocale($oid, $field);
|
||||
}
|
||||
}
|
||||
$ea->recomputeSingleObjectChangeset($uow, $meta, $object);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets translation object which represents translation in default language.
|
||||
*
|
||||
* @param string $oid hash of basic entity
|
||||
* @param string $field field of basic entity
|
||||
* @param mixed $trans Translation object
|
||||
*/
|
||||
public function setTranslationInDefaultLocale($oid, $field, $trans)
|
||||
{
|
||||
if (!isset($this->translationInDefaultLocale[$oid])) {
|
||||
$this->translationInDefaultLocale[$oid] = array();
|
||||
}
|
||||
$this->translationInDefaultLocale[$oid][$field] = $trans;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function isSkipOnLoad()
|
||||
{
|
||||
return $this->skipOnLoad;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes translation object which represents translation in default language.
|
||||
* This is for internal use only.
|
||||
*
|
||||
* @param string $oid hash of the basic entity
|
||||
* @param string $field field of basic entity
|
||||
*/
|
||||
private function removeTranslationInDefaultLocale($oid, $field)
|
||||
{
|
||||
if (isset($this->translationInDefaultLocale[$oid])) {
|
||||
if (isset($this->translationInDefaultLocale[$oid][$field])) {
|
||||
unset($this->translationInDefaultLocale[$oid][$field]);
|
||||
}
|
||||
if (! $this->translationInDefaultLocale[$oid]) {
|
||||
// We removed the final remaining elements from the
|
||||
// translationInDefaultLocale[$oid] array, so we might as well
|
||||
// completely remove the entry at $oid.
|
||||
unset($this->translationInDefaultLocale[$oid]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets translation object which represents translation in default language.
|
||||
* This is for internal use only.
|
||||
*
|
||||
* @param string $oid hash of the basic entity
|
||||
* @param string $field field of basic entity
|
||||
*
|
||||
* @return mixed Returns translation object if it exists or NULL otherwise
|
||||
*/
|
||||
private function getTranslationInDefaultLocale($oid, $field)
|
||||
{
|
||||
if (array_key_exists($oid, $this->translationInDefaultLocale)) {
|
||||
if (array_key_exists($field, $this->translationInDefaultLocale[$oid])) {
|
||||
$ret = $this->translationInDefaultLocale[$oid][$field];
|
||||
} else {
|
||||
$ret = null;
|
||||
}
|
||||
} else {
|
||||
$ret = null;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if object has any translation object which represents translation in default language.
|
||||
* This is for internal use only.
|
||||
*
|
||||
* @param string $oid hash of the basic entity
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasTranslationsInDefaultLocale($oid)
|
||||
{
|
||||
return array_key_exists($oid, $this->translationInDefaultLocale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the translation entity belongs to the object in question
|
||||
*
|
||||
* @param TranslatableAdapter $ea
|
||||
* @param object $trans
|
||||
* @param object $object
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function belongsToObject(TranslatableAdapter $ea, $trans, $object)
|
||||
{
|
||||
if ($ea->usesPersonalTranslation(get_class($trans))) {
|
||||
return $trans->getObject() === $object;
|
||||
}
|
||||
|
||||
return ($trans->getForeignKey() === $object->getId()
|
||||
&& ($trans->getObjectClass() === get_class($object)));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue