问题
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