CakePHP 3.0.8 translate behavior and data validation (requirePresence, notEmpty)

扶醉桌前 提交于 2019-12-01 13:23:04

Defaults saved in translation table

The fact that the input for the default language is being stored in the translation table in case the default locale has been changed, seems to be the expected behavior, just like when reading data where it will retrieve the data with respect to the current locale, the same applies when saving data.

Cookbook > Database Access & ORM > Behaviours > Translate > Saving in Another Language

Changing the locale to the default is a workaround, but it might be a little too invasive, as it will interfer with any code that uses that value to check the current locale. It's better to directly set the desired locale on the table

$Ingredients->locale(I18n::defaultLocale());

or, which is the least invasive option, on the main entity instead

$ingredient->_locale = I18n::defaultLocale();

Also the former is what the linked docs sesction is describing, but not actually showing, that needs to be fixed.

Fields picking up "wrong" validation rules

While I can see why the form helper, respectively the entity context, picks up validation rules for the "wrong" fields, ie xyz.name fields pick up those for the name field, I can't tell whether this is how it is ment to work.

Since it wouldn't pick up nested errors, I guess this is the expected behavior, but I'm not sure, so I'd suggest to create an issue over at GitHub for clarification. In any case, there are various ways to work around this, for example by renaming the fields, or by setting the required option to false.

echo $this->Form->input('locales.fr_CA.name', [
    // ...
    'required' => false
]);

In your example this is pretty much just a frontend issue, as the fields are not going to be actually validated on the server side.

Another option would be to use a custom translation table class, with validation specific to translations, that actually apply to the used fields, however this is probably not that advisable unless you actually want to apply any validation at all.

Apply validation/application rules to translated columns

For the sake of completion, let's cover validation/application rules too.

In order to actually apply validation and/or application rules, and have them recognized in forms, you'll have use a custom translation table class that holds the rules, and you must use the actual property name that the translate behavior uses for the hasMany associated translation table, which is _i18n.

Here's an example.

src/Model/Table/IngredientsI18nTable.php

namespace App\Model\Table;

use Cake\Datasource\EntityInterface;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

class IngredientsI18nTable extends Table
{
    public function initialize(array $config) {
        $this->entityClass('Ingredient');
        $this->table('i18n');
        $this->displayField('id');
        $this->primaryKey('id');
    }

    public function validationDefault(Validator $validator) {

        $validator
            ->allowEmpty('name')
            ->add('name', 'valid', [
                'rule' => function ($value, $context) {
                    return false;
                }
            ]);
        return $validator;
    }

    public function buildRules(RulesChecker $rules)
    {
        $rules->add(
            function (EntityInterface $entity, $options) {
                return false;
            },
            'i18nName',
            [
                'errorField' => 'name'
            ]
        );

        return $rules;
    }
}

IngredientsTable

public function initialize(array $config) {
    // ...

    $this->addBehavior('Translate', [
        // ...
        'translationTable' => 'IngredientsI18n'
    ]);
}

View template

echo $this->Form->hidden('_i18n.0.locale', ['value' => 'fr_FR']);
echo $this->Form->input('_i18n.0.name');

echo $this->Form->hidden('_i18n.1.locale', ['value' => 'da_DK']);
echo $this->Form->input('_i18n.1.name');

// ...

Now the fields will pick up the correct validator, and thus are not being marked as required. Also validation will be applied when creating/patching entities, and finally application rules are being applied too. However I can't guarantee that this doesn't have any side effects, as the Translate behavior internally doesn't seem to account for the situation that the _i18n property has been set externally!

Also you'll still have to set the translations on the entity using translations() in order for the translations to be saved correctly!

foreach ($this->request->data['_i18n'] as $translation) {
    $ingredient->translation($translation['locale'])->set('name', $translation['name']);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!