Skip to content

Commit

Permalink
extend Marshaller and TranslateBehavior to patch translations fields
Browse files Browse the repository at this point in the history
  • Loading branch information
Albert Cansado Solà authored and markstory committed Aug 7, 2016
1 parent e65f13f commit 785c2c6
Show file tree
Hide file tree
Showing 4 changed files with 794 additions and 3 deletions.
47 changes: 45 additions & 2 deletions src/ORM/Behavior/TranslateBehavior.php
Expand Up @@ -74,15 +74,16 @@ class TranslateBehavior extends Behavior
*/
protected $_defaultConfig = [
'implementedFinders' => ['translations' => 'findTranslations'],
'implementedMethods' => ['locale' => 'locale'],
'implementedMethods' => ['locale' => 'locale', 'mergeTranslations' => 'mergeTranslations'],
'fields' => [],
'translationTable' => 'I18n',
'defaultLocale' => '',
'referenceName' => '',
'allowEmptyTranslations' => true,
'onlyTranslated' => false,
'strategy' => 'subquery',
'tableLocator' => null
'tableLocator' => null,
'validator' => false
];

/**
Expand Down Expand Up @@ -326,6 +327,48 @@ public function afterSave(Event $event, EntityInterface $entity)
$entity->unsetProperty('_i18n');
}

/**
* Merges `$data` into `$original` entity recursively using Marshaller merge method, if
* original entity is null, new one will be created.
* The translated entity may only contain the fields defined in the
* behavior configuration (`fields`), you can use `fieldList` option as a
* whitelist of fields to be assigned.
*
* The result will be and array [entities, errors]:
* - entities indexed by locale name
* - errors indexed by locale name
* or null if there are no fields to merge.
*
* ## Note: Translated entity data not will be validated during merge.
*
* @param \Cake\Datasource\EntityInterface $original The original entity
* @param array $data key value list of languages with fields to be merged into the translate entity
* @param \Cake\ORM\Marshaller $marshaller Marshaller
* @param array $options list of options for Marshaller
* @return array|null
*/
public function mergeTranslations($original, array $data, \Cake\ORM\Marshaller $marshaller, array $options = [])
{
$options['fieldList'] = (isset($options['fieldList'])) ? array_intersect($this->_config['fields'], $options['fieldList']) : $this->_config['fields'];
if (empty($options['fieldList'])) {
return null;
}

$options['validate'] = $this->_config['validator'];
$errors = [];
foreach ($data as $language => $fields) {
if (!isset($original[$language])) {
$original[$language] = $this->_table->newEntity();
}
$marshaller->merge($original[$language], $fields, $options);
if ((bool)$original[$language]->errors()) {
$errors[$language] = $original[$language]->errors();
}
}

return [$original, $errors];
}

/**
* Sets all future finds for the bound table to also fetch translated fields for
* the passed locale. If no value is passed, it returns the currently configured
Expand Down
55 changes: 54 additions & 1 deletion src/ORM/Marshaller.php
Expand Up @@ -147,6 +147,8 @@ public function one(array $data, array $options = [])
$marshallOptions['forceNew'] = $options['forceNew'];
}

$hasTranslations = $this->_hasTranslations($options);

$errors = $this->_validate($data, $options, true);
$properties = [];
foreach ($data as $key => $value) {
Expand All @@ -166,6 +168,8 @@ public function one(array $data, array $options = [])
} elseif ($columnType) {
$converter = Type::build($columnType);
$value = $converter->marshal($value);
} elseif ($key === '_translations' && $hasTranslations) {
$value = $this->_mergeTranslations(null, $value, $errors, $options);
}
$properties[$key] = $value;
}
Expand Down Expand Up @@ -226,7 +230,7 @@ protected function _validate($data, $options, $isNew)
*/
protected function _prepareDataAndOptions($data, $options)
{
$options += ['validate' => true];
$options += ['validate' => true, 'translations' => true];

$tableName = $this->_table->alias();
if (isset($data[$tableName])) {
Expand Down Expand Up @@ -449,6 +453,51 @@ protected function _loadBelongsToMany($assoc, $ids)
return $this->_loadAssociatedByIds($assoc, $ids);
}

/**
* Call translation merge. Validations errors during merge will be added to `$errors` param
*
* @param \Cake\Datasource\EntityInterface $original The original entity
* @param array $data key value list of languages with fields to be merged into the translate entity
* @param array $errors array with entity errors
* @param array $options list of options
* @return array|null
*/
protected function _mergeTranslations($original, array $data, array &$errors, array $options = [])
{
$result = $this->_table->mergeTranslations($original, $data, $this, $options);

if (is_array($result)) {
if ((bool)$result[1]) {
$errors['_translations'] = $result[1];
}
$result = $result[0];
}

return $result;
}

/**
* Return if table contains translate behavior or we specificate to use via `translations` options.
*
* In case that $options has `fieldList` option and `_translations` field is not present inside it, it will include
*
* ### Options:
*
* - translations: Set to false to disable translations
*
* @param array $options List of options
* @return bool
*/
protected function _hasTranslations(array &$options = [])
{
$hasTranslations = ($this->_table->behaviors()->hasMethod('mergeTranslations') && (bool)$options['translations']);
if ($hasTranslations && !empty($options['fieldList']) && !in_array('_translations', $options['fieldList'])) {
array_push($options['fieldList'], '_translations');
}

return $hasTranslations;
}

/**
* Merges `$data` into `$entity` and recursively does the same for each one of
* the association names passed in `$options`. When merging associations, if an
Expand Down Expand Up @@ -503,6 +552,8 @@ public function merge(EntityInterface $entity, array $data, array $options = [])
}
}

$hasTranslations = $this->_hasTranslations($options);

$errors = $this->_validate($data + $keys, $options, $isNew);
$schema = $this->_table->schema();
$properties = $marshalledAssocs = [];
Expand Down Expand Up @@ -530,6 +581,8 @@ public function merge(EntityInterface $entity, array $data, array $options = [])
) {
continue;
}
} elseif ($key === '_translations' && $hasTranslations) {
$value = $this->_mergeTranslations($original, $value, $errors, $options);
}

$properties[$key] = $value;
Expand Down

0 comments on commit 785c2c6

Please sign in to comment.