Skip to content

Instantly share code, notes, and snippets.

@Tharos
Last active December 21, 2015 06:39
Show Gist options
  • Save Tharos/6266039 to your computer and use it in GitHub Desktop.
Save Tharos/6266039 to your computer and use it in GitHub Desktop.
Translations in Lean Mapper. See http://www.leanmapper.com/samples/translations.zip for working example.
<?php
$connection->registerFilter('translate', array($translator, 'translate'), Connection::WIRE_ENTITY | Connection::WIRE_PROPERTY);
$connection->registerFilter('translateByTable', array($translator, 'translateByTable'));
<?php
namespace Model;
class Mapper extends \LeanMapper\DefaultMapper implements ITranslatingMapper
{
/**
* @return string
*/
public function getLangTable()
{
return 'lang';
}
/**
* @param string $table
* @return string
*/
public function getTranslationTable($table)
{
return $table . '_translation';
}
}
<?php
namespace Model\Entity;
/**
* @property Lang $lang m:hasOne m:translatable
*/
abstract class TranslatableEntity extends \LeanMapper\Entity
{
/**
* @return array
*/
public function getTranslatableColumns()
{
$columns = array();
foreach ($this->getCurrentReflection()->getEntityProperties() as $property) {
if ($property->hasCustomFlag('translatable')) {
$columns[] = $property->getColumn();
}
}
return $columns;
}
}
<?php
namespace Model\Repository;
use Exception;
use LeanMapper\Connection;
use LeanMapper\Entity;
use LeanMapper\Events;
use Model\Entity\Lang;
use Model\Entity\TranslatableEntity;
use Model\ITranslatingMapper;
abstract class TranslatingRepository extends \LeanMapper\Repository
{
/** @var LangRepository */
private $langRepository;
/**
* @param Connection $connection
* @param ITranslatingMapper $mapper
* @param LangRepository $langRepository
*/
public function __construct(Connection $connection, ITranslatingMapper $mapper, LangRepository $langRepository)
{
parent::__construct($connection, $mapper);
$this->langRepository = $langRepository;
}
/**
* @param mixed $id
* @param Lang $lang
* @return Entity
* @throws Exception
*/
public function find($id, Lang $lang)
{
$table = $this->getTable();
$row = $this->connection->select('%n.*', $table)
->from($table)
->where('%n.%n = %i', $table, $this->mapper->getPrimaryKey($table), $id)
->applyFilter('translateByTable', $table, $lang)
->fetch();
if ($row === false) {
throw new Exception('Entity was not found.');
}
return $this->createEntity($row);
}
/**
* @param Lang $lang
* @return Entity[]
*/
public function findAll(Lang $lang)
{
$table = $this->getTable();
return $this->createEntities(
$this->connection->select('%n.*', $table)
->from($table)
->applyFilter('translateByTable', $table, $lang)
->fetchAll()
);
}
/**
* @param Entity $entity
* @return mixed
*/
protected function insertIntoDatabase(Entity $entity)
{
$table = $this->getTable();
$mapper = $this->mapper;
$primaryKey = $mapper->getPrimaryKey($table);
$translationTable = $mapper->getTranslationTable($table);
$relationshipColumn = $mapper->getRelationshipColumn($translationTable, $table);
$langRelationshipColumn = $mapper->getRelationshipColumn($translationTable, $mapper->getLangTable());
list($primaryValues, $translatedValues) = $this->getSeparatedModifierRowData($entity);
$this->connection->query(
'INSERT INTO %n %v', $table, $primaryValues
);
$id = isset($primaryValues[$primaryKey]) ? $primaryValues[$primaryKey] : $this->connection->getInsertId();
$translatedValues[$relationshipColumn] = $id;
foreach ($this->langRepository->findAll() as $lang) {
$values = $translatedValues;
if ($translatedValues[$langRelationshipColumn] !== $lang->id) {
$values = array(
$relationshipColumn => $id,
$langRelationshipColumn => $lang->id
);
}
$this->connection->query(
'INSERT INTO %n %v', $translationTable, $values
);
}
return $id;
}
/**
* @param Entity $entity
* @return mixed
*/
protected function updateInDatabase(Entity $entity)
{
$table = $this->getTable();
$mapper = $this->mapper;
$primaryKey = $mapper->getPrimaryKey($table);
$idField = $mapper->getEntityField($table, $primaryKey);
$translationTable = $mapper->getTranslationTable($table);
$relationshipColumn = $mapper->getRelationshipColumn($translationTable, $table);
$langRelationshipColumn = $mapper->getRelationshipColumn($translationTable, $mapper->getLangTable());
list($primaryValues, $translatedValues) = $this->getSeparatedModifierRowData($entity);
if (!empty($primaryValues)) {
$this->connection->query(
'UPDATE %n SET %a WHERE %n = ?', $table, $primaryValues, $primaryKey, $entity->$idField
);
}
if (!empty($translatedValues)) {
$this->connection->query(
'UPDATE %n SET %a', $translationTable, $translatedValues,
'WHERE %n = %i AND %n = %s', $relationshipColumn, $entity->$idField, $langRelationshipColumn, $entity->lang->id
);
}
}
////////////////////
////////////////////
/**
* @param TranslatableEntity $entity
* @return array
*/
private function getSeparatedModifierRowData(TranslatableEntity $entity)
{
$translatableColumns = array_flip($entity->getTranslatableColumns());
$primaryValues = array();
$translatedValues = array();
foreach ($entity->getModifiedRowData() as $column => $value) {
if (isset($translatableColumns[$column])) {
$translatedValues[$column] = $value;
} else {
$primaryValues[$column] = $value;
}
}
return array($primaryValues, $translatedValues);
}
}
<?php
namespace Model\Filter;
use LeanMapper\Fluent;
use LeanMapper\Reflection\Property;
use Model\Entity\Lang;
use Model\Entity\TranslatableEntity;
use Model\ITranslatingMapper;
class Translator
{
/** @var ITranslatingMapper */
private $mapper;
/**
* @param ITranslatingMapper $mapper
*/
public function __construct(ITranslatingMapper $mapper)
{
$this->mapper = $mapper;
}
/**
* @param Fluent $statement
* @param TranslatableEntity $entity
* @param Property $property
* @param Lang $lang
*/
public function translate(Fluent $statement, TranslatableEntity $entity, Property $property, Lang $lang = null)
{
if ($lang === null) {
$lang = $entity->lang;
}
$targetTable = $property->getRelationship()->getTargetTable();
$this->translateByTable($statement, $targetTable, $lang);
}
/**
* @param Fluent $statement
* @param string $targetTable
* @param Lang $lang
*/
public function translateByTable(Fluent $statement, $targetTable, Lang $lang)
{
$translationTable = $this->mapper->getTranslationTable($targetTable);
$statement->removeclause('select')
->select('%n.*, %n.*', $translationTable, $targetTable)
->leftJoin('%n', $translationTable)->on(
'%n.%n = %n.%n',
$translationTable,
$this->mapper->getRelationshipColumn($translationTable, $targetTable),
$targetTable,
$this->mapper->getPrimaryKey($targetTable)
)
->where('%n = %s', $this->mapper->getRelationshipColumn($translationTable, $this->mapper->getLangTable()), $lang->id);
}
}
@VojtaSim
Copy link

It would be great to use DefaultMapper::$defaultEntityNamespace as a prefix for Model\Entity\TranslatableEntity in Mapper::getImplicitFilters()

Somehow like this...

public function getImplicitFilters($entityClass, Caller $caller = null)
{
    if (is_subclass_of($entityClass, $this->defaultEntityNamespace . '\TranslatableEntity')) {
        if ($caller instanceof Entity) {
            return array('translateFromEntity');
        } else {
            return new ImplicitFilters(array('translate'), array(
                'translate' => array($this->getTable($entityClass)),
            ));
        }
    }
    return parent::getImplicitFilters($entityClass, $caller);
}

Found this function in the ZIP but in this Gist it's not mentioned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment