CakePHP 3.1: Validation for translate behaviour fields (i18n)

£可爱£侵袭症+ 提交于 2019-12-13 08:44:08

问题


I'm trying to add an item including multiple translations in one form with the CakePHP translate behaviour.

How can I validate the translation fields? E.g. make specific languages required?

Let's assume you have a simple items table with a separate translations table items_i18n, set up as described in the book. As simple example the items table has only one field title to translate and I want to save the title in five languages. So I make a form like this (in add view template):

echo $this->Form->create($item, ['controller' => 'Items', 'action' => 'add']);
echo $this->Form->input('title', ['label' => __('English')]);
echo $this->Form->input('_translations.es.title', ['label' => __('Spanish')]);
echo $this->Form->input('_translations.fr.title', ['label' => __('French')]);
echo $this->Form->input('_translations.de.title', ['label' => __('German')]);
echo $this->Form->input('_translations.it.title', ['label' => __('Italian')]);
echo $this->Form->button(__('Save'), ['type' => 'submit']);
echo $this->Form->end();

And saving in the controller (add action/function) like this:

$item = $this->Items->newEntity();
if ($this->request->is('post')) {
  $translations = [
    'es' => ['title' => $this->request->data['_translations']['es']['title']],
    'fr' => ['title' => $this->request->data['_translations']['fr']['title']],
    'de' => ['title' => $this->request->data['_translations']['de']['title']],
    'it' => ['title' => $this->request->data['_translations']['it']['title']],
  ];
  foreach ($translations as $lang => $data) {
    $item->translation($lang)->set($data, ['guard' => false]);
  }
  $item = $this->Items->patchEntity($item, $this->request->data, ['validate' => 'default'] );
  if ( $this->Items->save($item) ) { $this->Flash->success(__('Saved.')); }
  else { $this->Flash->error(__('Not saved.')); }
}
$this->set('item', $item);

This is working without validation or if I only have validation rules for the "native" title field (well it should, I simplified the code for stackoverflow and renamed some parts for the example, so maybe there are some typos, but you should get the idea...).

Now let's further assume the languages English (default) and Spanish are required, other language fields are optional. How can I achieve that?

In the ItemsTable I tried something like this for validation:

class ItemsTable extends Table {
  public function validationDefault(Validator $validator) {
    $validator
      // Title English (default field)
      ->requirePresence('title')
      ->notEmpty('title', __('Required field'))
      // Title Spanish (translate behaviour field)
      ->requirePresence('_translations.es.title')
      ->notEmpty('_translations.es.title', __('Required field'))
      ;
    return $validator;
  }
}

But this allways brings a validation error "This field is required" because patchEntity($item, $this->request->data); results in the translations being thrown away. I know this by an open issue on GitHub about the saving workflow (btw +1 for this request :).

So currently I'm not sure if there is a way to define validation rules for translation fields when using the CakePHP translation behaviour... Required language fields is only an example, the same problem occurs if you want to validate e.g. the min/max lenght of a input field for a foreign language...


回答1:


Ok, I think I found a solution. At least temporarily, because I also discovered an issue with NestedValidator and FormHelper.

Currently the validation is still applied to all additional languages. So this is not exactly what I wanted and not the final answer. If you have an idea how I can apply the validation to single languages please leave a comment or answer.

So this is my current intermediate solution with CakePHP 3.1.1:

In the table class add a nested validator for the i18n translation fields.

These nested validation rules will apply to all additional language fields, because they are grouped together in $this->request->data['_translations']:

class ItemsTable extends Table {
  public function validationDefault(Validator $validator) {
    $validator
      // Title English (default language)
      ->requirePresence('title')
      ->notEmpty('title')
      ->add('title', [
          'minLength'=>['rule'=>['minLength', 2], 'message' => __('MinLength 2')],
          'maxLength'=>['rule'=>['maxLength', 255], 'message' => __('MaxLength 255')],
          ])
      ;
      // Nested Validation for i18n fields (Translate Behaviour)
      // These rules will apply to all 'title' fields in all additional languages
      $translationValidator = new Validator();
      $translationValidator
        ->requirePresence('title', 'false') // I want translation to be optional
        ->allowEmpty('title') // I want translation to be optional
        ->add('title', [
          'minLength'=>['rule'=>['minLength', 5], 'message' => __('MinLength 5')],
          'maxLength'=>['rule'=>['maxLength', 255], 'message' => __('MaxLength 255')],
          ])
      ;
      // Now apply the nested validator to the "main" validation
      // ('_translations' is containing the translated input data)
      $validator
        ->addNestedMany('_translations', $translationValidator)
        // To prevent "field is required" for the "_translations" data
        ->requirePresence('_translations', 'false')
        ->allowEmpty('_translations')
        ;
    return $validator;
  }
}

In my test setup I want the translation fields to be optional and have other minLength as the default language. As you can see in the code above, I added allowEmpty and set requirePresence to false for the translation fields. Currently the TranslateBehaviour is still forcing the translation title fields to be required. So I added additionally 'required' => false to the translation input fields in add/edit form:

echo $this->Form->input('_translations.es.title', ['required' => false]);

The separate validation rules are now applied to the translation fields, as shown in the debug result (added temporarily in the controller while testing):

$item = $this->Items->patchEntity($item, $this->request->data);
debug($item);

When you enter only one character in the input fields, the minLength error messages are available in the debug error array.

But currently the FormHelper does not support nested error messages. I reported this issue on GitHub. A temporarily solution to show an error in the form is by checking the error array manually. To do so, add in the Controller:

$item = $this->Items->patchEntity($item, $this->request->data);
if ( !$item->errors() ) {
  foreach ($this->request->data['_translations'] as $lang => $data) {
    $item->translation($lang)->set($data, ['guard' => false]);  
  }
}
// Temp workaround for issue#7532:
else {
  $this->set('formerrors', $language->errors()); 
}

And in the add/edit view you can check and use the additional $formerrors array:

if ( isset($formerrors['_translations']['es']['title']) ) { ... }

Another interesting approach is shown in the answer to this question.



来源:https://stackoverflow.com/questions/32915248/cakephp-3-1-validation-for-translate-behaviour-fields-i18n

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!