CakePHP 3.0.8翻译行为和数据验证(requirePresence,notEmpty)

时间:2015-08-09 05:15:10

标签: php validation cakephp internationalization cakephp-3.0

我的问题很简单,但我无法弄清楚如何解决它。

我的网站是多语言的。我希望用户能够根据需要添加多种语言的文章,同时需要输入他的语言(取决于他的语言环境)。

问题是,对于CakePHP关于翻译的惯例,所有输入必须以字段的名称结尾,无论使用何种语言。因此,所有字段对同一字段具有相同的规则。我不能说一个名字"需要另一种语言时不需要另一种语言。

例如,默认语言的输入将是:

<input type="text" name="name" required="required" maxlength="45" id="name">

在此之下,同一领域的另一种语言输入:

<input type="text" name="locales[fr_CA][name]" required="required" maxlength="45" id="locales-fr-ca-name">

&#34; required&#34;由于以下规则,属性会自动添加到两者:

$validator
    ->requirePresence('name', 'create')
    ->notEmpty('name')
    ->add('name', [
        'length' => [
            'rule' => ['minLength', 10],
            'message' => 'The title needs to be at least 10 characters long.',
        ]
    ]);

注意:当我保存时,我必须将语言环境更改为默认语言(en_US),以便能够以多种语言保存+默认语言(否则默认输入将保存在默认表格和i18n表格中)。

if ($this->request->is('post')) {
    I18n::locale('en_US');
    // ......

编辑:所以这是我保存时的完整代码(IngredientsController.php)

public function add() {
    $ingredient = $this->Ingredients->newEntity();
    if ($this->request->is('post')) {
        $ingredient = $this->Ingredients->patchEntity($ingredient, $this->request->data);

        if(isset($this->request->data['locales'])) {
            foreach ($this->request->data['locales'] as $lang => $data) {
                $ingredient->translation($lang)->set($data, ['guard' => false]);
            }
        }

        $locale = I18n::locale(); // At this point the locale is fr_CA (not de default)
        I18n::locale('en_US'); // Change the locale to the default

        if ($this->Ingredients->save($ingredient)) {
            $this->Flash->success(__('The ingredient has been saved.'));
            I18n::locale($locale); // Put the locale back to the user's locale
            return $this->redirect(['action' => 'index']);
        } else {
            I18n::locale($locale);
            $this->Flash->error(__('The ingredient could not be saved. Please, try again.'));
        }
    }

    $this->set(compact('ingredient'));
    $this->set('_serialize', ['ingredient']);
}

我设置的默认语言环境是bootstrap.php

/**
 * Set the default locale. This controls how dates, number and currency is
 * formatted and sets the default language to use for translations.
 */
ini_set('intl.default_locale', 'en_US');
Configure::write('Config.locales', ['fr_CA']);

我在AppController.php中确定了用户的语言环境

public function beforeFilter(Event $event)
{
    $locales = Configure::read('Config.locales');
    $boom = explode(',', str_replace('-', '_', $_SERVER['HTTP_ACCEPT_LANGUAGE']));
    $user_lang = substr($boom[0], 0, 2);

    // This piece of code is only to change the locale to fr_CA even if the user's language is just fr or fr_FR
    if(in_array($user_lang, Configure::read('Config.langs'))) {
        if(in_array($boom[0], $locales)) {
            I18n::locale($boom[0]);
        } else {
            foreach ($locales as $locale) {
                if(substr($locale, 0, 2) == $user_lang) {
                    I18n::locale($locale);
                }
            }
        }
    }

    $this->set('locales', $locales);
    $this->set('locale', I18n::locale());
}

因此,如果我在与默认语言环境不同的语言环境中保存,则相同的默认输入将保存在成分表和fr_CA中的i18n表中

1 个答案:

答案 0 :(得分:1)

默认值保存在转换表

如果默认语言环境已被更改,默认语言的输入存储在转换表中这一事实似乎是预期的行为,就像读取数据一样,它将检索与数据相关的数据。当前区域设置,保存数据时同样适用。

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

将语言环境更改为默认语言是一种解决方法,但它可能有点过于干扰,因为它会干扰使用该值检查当前语言环境的任何代码。最好直接在表格上设置所需的区域设置

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

或者,主要实体上的侵入性最小的选项,而不是

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

前者是链接的文档序列描述的内容,但实际上没有显示,需要修复。

字段选择“错误”验证规则

虽然我可以看到为什么表单助手(分别是实体上下文)为“错误”字段选择验证规则,即xyz.name字段为name字段选择那些,我可以'告诉我这是否是如何工作的。

由于它不会发现嵌套错误,我猜这是预期的行为,但我不确定,所以我建议create an issue over at GitHub进行澄清。在任何情况下,有多种方法可以解决此问题,例如重命名字段,或将required选项设置为false

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

在您的示例中,这几乎只是一个前端问题,因为字段不会在服务器端实际验证。

另一种选择是使用自定义翻译表类,具有特定于翻译的验证,实际应用于使用的字段,但是这可能不是 可取的,除非您实际想要应用任何完全验证。

将验证/应用程序规则应用于已翻译的列

为了完成,我们也将介绍验证/应用规则。

为了实际应用验证和/或应用程序规则,并在表单中识别它们,您将使用包含规则的自定义转换表类,并且您必须使用转换行为使用的实际属性名称对于hasMany关联的翻译表,即_i18n

这是一个例子。

<强>的src /型号/表/ 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'
    ]);
}

查看模板

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

// ...

现在字段将选择正确的验证器,因此不会被标记为必需。在创建/修补实体时也会应用验证,最后也会应用应用程序规则。但是我无法保证这没有任何副作用,因为内部的翻译行为似乎没有考虑到_i18n属性已在外部设置的情况!

此外,您仍然需要使用translations()在实体上设置翻译,以便正确保存翻译!

foreach ($this->request->data['_i18n'] as $translation) {
    $ingredient->translation($translation['locale'])->set('name', $translation['name']);
}