CakePHP 3.0.8 翻译行为和数据验证 (requirePresence, notEmpty) [英] CakePHP 3.0.8 translate behavior and data validation (requirePresence, notEmpty)

查看:37
本文介绍了CakePHP 3.0.8 翻译行为和数据验证 (requirePresence, notEmpty)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题很简单,但我不知道如何解决.

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 wants, while requiring the inputs of his language (depending on his locale).

问题是,根据 CakePHP 关于翻译的约定,所有输入都必须以字段名称结尾,无论是什么语言.因此,所有字段对同一字段具有相同的规则.我不能让一个名字"成为必需,而另一个语言则不需要.

Problem is, with CakePHP's conventions about translation, all the inputs must end with the field's name, no matter what language. So all the fields has the same rule for the same field. I can't make one "name" required while another in another language not required.

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

For example, the default language's input would be:

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

在此之下,同一字段的另一种语言输入:

And below that, another language's input for the same field:

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

由于这些规则,必需"属性会自动添加到两者:

The "required" attribute is automatically added to both because of these rules:

$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 表中).

Note: I have to change the locale to the default (en_US) when I save to be able to save in multiple languages + the default language (otherwise the default inputs are saved in the default table AND in the i18n table).

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

这是我保存时的完整代码(IngredientsController.php)

So here's the complete piece of code when I save (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

I set the default locale is the 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 中确定用户的语言环境

I determine the user's locale in the 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 表中

So if I save while being in a different locale than the default, the same default inputs will be saved in the ingredients table AND in the i18n table in fr_CA

推荐答案

翻译表中保存的默认值

如果默认语言环境已更改,默认语言的输入将存储在翻译表中,这似乎是预期的行为,就像在读取数据时它将检索与当前语言环境,保存数据时也是如此.

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.

食谱 > 数据库访问&ORM > 行为 > 翻译 > 以另一种语言保存

将语言环境更改为默认设置是一种解决方法,但它可能有点过于侵入性,因为它会干扰使用该值检查当前语言环境的任何代码.最好直接在table上设置想要的locale

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.

虽然我可以看到为什么表单助手和实体上下文会选择错误"字段的验证规则,即 xyz.name 字段会选择 name 字段,我不知道这是否是它的工作方式.

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.

由于它不会拾取嵌套错误,我想这是预期的行为,但我不确定,所以我建议 在 GitHub 上创建一个问题 进行澄清.无论如何,有多种方法可以解决这个问题,例如通过重命名字段,或将 required 选项设置为 false.

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.

为了完整起见,让我们也介绍一下验证/应用程序规则.

For the sake of completion, let's cover validation/application rules too.

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

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.

这是一个例子.

src/Model/Table/IngredientsI18nTable.php

namespace AppModelTable;

use CakeDatasourceEntityInterface;
use CakeORMRulesChecker;
use CakeORMTable;
use CakeValidationValidator;

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

成分表

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 属性已在外部设置的情况!

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!

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

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

这篇关于CakePHP 3.0.8 翻译行为和数据验证 (requirePresence, notEmpty)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆