CakePHP 3.1:验证翻译行为字段(i18n)

时间:2015-10-02 20:20:27

标签: validation cakephp internationalization multilingual cakephp-3.1

我正在尝试使用CakePHP translate behaviour添加包含多个翻译以一种形式的项目。

如何验证翻译字段?例如需要特定的语言吗?

我们假设您有一个简单的items表格,其中separate translations table items_i18n的设置如书中所述。举个简单的例子,items表只有一个要翻译的字段title,我想用五种语言保存title。所以我创建了一个这样的表单(在add视图模板中):

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();

并保存在控制器(add动作/功能)中,如下所示:

$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);

这是在没有验证的情况下工作,或者我只有“native”title字段的验证规则(它应该是,我简化了stackoverflow的代码并为示例重命名了一些部分,所以也许有一些错别字,但你应该明白......)。

现在让我们进一步假设语言英语(默认)和西班牙语 必需,其他语言字段是可选的。我怎样才能做到这一点?

ItemsTable我尝试了类似这样的验证:

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;
  }
}

但是这总是带来验证错误“此字段是必需的”,因为patchEntity($item, $this->request->data);导致翻译被丢弃。我通过开放issue on GitHub了解保存工作流程(此请求的btw +1)。

所以目前我不确定在使用CakePHP翻译行为时是否有办法为翻译字段定义验证规则... 必需的语言字段只是一个例子,同样的问题发生如果你想验证,例如外语输入字段的最小/最大长度......

1 个答案:

答案 0 :(得分:1)

好的,我想我找到了解决方案。至少是暂时的,因为我还发现issue带有 NestedValidator FormHelper

目前,验证仍适用于所有其他语言。所以这不是我想要的,而不是最终答案。如果您知道如何将验证应用于单一语言,请发表评论或回答。

所以这是我目前使用CakePHP 3.1.1的中间解决方案:

在表格类中为i18n翻译字段添加nested validator

这些嵌套验证规则将适用于所有其他语言字段,因为它们在$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;
  }
}

在我的测试设置中,我希望翻译字段是可选的,并将其他minLength作为默认语言。正如您在上面的代码中看到的,我添加了allowEmpty并将requirePresence设置为false以获取翻译字段。目前,TranslateBehaviour仍然强制要求翻译title字段。所以我在添加/编辑表单中添加了'required' => false到翻译输入字段:

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

单独的验证规则现在应用于转换字段,如调试结果中所示(在测试时临时添加到控制器中):

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

如果在输入字段中只输入一个字符,则调试错误数组中会出现minLength错误消息。

但目前FormHelper不支持嵌套错误消息。我报告此issue on GitHub。在表单中显示错误的临时解决方案是手动检查error数组。为此,请添加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()); 
}

在添加/编辑视图中,您可以检查并使用其他$formerrors数组:

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

另一个有趣的方法显示在answer to this question