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

后端 未结 1 480
北荒
北荒 2021-01-16 09:42

My problem is simple, yet I can\'t figure out how to solve it.

My website is multilanguage. I want the user to be able to add an article in multiple language if he w

1条回答
  •  傲寒
    傲寒 (楼主)
    2021-01-16 09:59

    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.

    • https://github.com/cakephp/cakephp/blob/3.0.10/src/View/Form/EntityContext.php#L394
    • https://github.com/cakephp/cakephp/blob/3.0.10/src/View/Form/EntityContext.php#L439

    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']);
    }
    

    0 讨论(0)
提交回复
热议问题