Sorting CakePHP3:Paginator按语言排序(具有i18n翻译行为)

Sorting CakePHP3:Paginator按语言排序(具有i18n翻译行为),sorting,cakephp,pagination,cakephp-3.1,cakephp-3.x,Sorting,Cakephp,Pagination,Cakephp 3.1,Cakephp 3.x,我有一个包含所有条目的表,包括所有多语言的翻译:如何在翻译字段上创建分页排序链接?(第3.1.6节) 摘要:这不起作用,我无法按翻译进行排序: $this->Paginator->sort('_translations.es.title', 'Spanish') 长版本: 我正在使用带有的i18n表 我在一个表中列出了所有的翻译(多种语言) 现在我想在翻译中使用(按语言排序) |标题英语|标题西班牙语|标题德语|=分页排序链接 | ---------------- | ----

我有一个包含所有条目的表,包括所有多语言的翻译:如何在翻译字段上创建分页排序链接?(第3.1.6节)

摘要:这不起作用,我无法按翻译进行排序:

$this->Paginator->sort('_translations.es.title', 'Spanish')
长版本:

  • 我正在使用带有i18n表
  • 我在一个表中列出了所有的翻译(多种语言)
  • 现在我想在翻译中使用(按语言排序)
|标题英语|标题西班牙语|标题德语|=分页排序链接
| ---------------- | ---------------- | --------------- |
|圣诞节|纳维达德|威纳克斯登|
|春天|普里马维拉|弗吕林|
| ...
下面是我的简化测试设置:

文章只有一个字段
标题
需要翻译。
i18n表是默认设置

烘焙表格
/src/Model/Table/ArticlesTable.php
,添加了翻译行为

公共函数初始化(数组$config){
//…默认配置(在本文中删除以简化代码)
$this->addBehavior('Translate',['fields'=>['title']]);//添加了此行
}
烘焙Entity
/src/Model/Entity/Article.php
,添加了translaterait

namespace-App\Model\Entity;
使用Cake\ORM\Behavior\Translate\TranslateTrait;//增加了这一行
使用Cake\ORM\Entity;
类文章扩展了实体{
受保护的$\u可访问=[
“*”=>正确,
'id'=>false,
];
使用translateRait;//添加了此行
}
烘焙控制器
/src/Controller/ArticlesController.php
,修改如下:

namespace-App\Controller;
使用App\Controller\AppController;
类ArticlesController扩展了AppController{
公共$paginate=[
'sortWhitelist'=>[//允许在此字段上进行分页排序:
“头衔”,
“_translations.es.title”,
“\u translations.de.title”
]
];
公共职能指数(){
$query=$this->Articles->find('translations');//检索所有翻译
$this->set('articles',$this->paginate($query));
}
}
烘焙视图
/src/Template/Articles/index.ctp
,修改/简化:


表中的翻译显示正确,但无法按翻译字段排序。当我单击翻译的分页链接时,出现以下错误:

SQLSTATE[42S22]:未找到列:“order子句”中的1054未知列“\u translations.es”

格式为
Posts\u title\u translation.content
,我不知道这是从哪里来的,但我也尝试了这种方式(当然,我还在paginator白名单中添加了字段名的变体):

$this->Paginator->sort('Articles\u title\u translation','西班牙语')
$this->Paginator->sort('Articles\u title\u translation.es','西班牙语')
$this->Paginator->sort('Articles\u title\u translation.content','西班牙语')
$this->Paginator->sort('Articles\u title\u translation.es.content','西班牙语')
他们都不工作。。。(显然)

如何根据标题字段的i18n翻译对表项进行排序?

对翻译字段进行排序需要联接 通常,您只能对加入主查询的字段进行排序

翻译后的字段仅与非默认的当前区域设置连接 默认情况下,只有在当前区域设置(
I18n::locale()
)与默认区域设置(
I18n::defaultLocale()
intl.default\u locale
)不匹配的情况下(即实际需要翻译某些内容时),才会加入已翻译字段

将当前区域设置更改为非默认设置后

I18n::locale('de');
$query = $this->Articles->find('translations');
// ...
翻译行为将包含与翻译内容的关联,这就是
TableAlias\u field\u translation
别名的来源,该行为使用该命名方案为每个翻译字段创建
hasOne
关联

然后,这些字段可用于分页,但一次只能在一个区域设置中加入

确保分页器使用正确的字段 由于关联并不总是被包含,因此必须采取适当的措施确保分页器根据区域设置使用正确的字段。类似这样的代码(请注意,这只是用于演示目的的未经测试的示例代码)

如果不包含任何翻译,则会将要排序的字段从翻译字段映射到原始字段

加入并排序一个领域的所有翻译/语言 上述内容适用于单个字段的排序,该字段可以翻译,也可以不翻译。让字段的所有翻译都可用于排序超出了翻译行为的范围。虽然该行为确实加载了所有翻译,但它是通过使用
hasMany
关联来加载的,即使用单独的查询,因此它们不能用于排序。加入所有翻译都需要手动完成

这可能是一个特性请求,但我不确定这是否是一个可以证明这样的核心修改的常见用例,您可能希望在上打开一个问题或询问

话虽如此,这里有一个基本的例子,一个扩展的translate行为,它几乎完成了
TranslateBehavior::setupFieldAssociations()
TranslateBehavior::beforeFind()
所做的事情,只是稍微修改了一下。该行为采用了一个
locales
选项,该选项需要与所有locales一起提供
I18n::locale('de');
$query = $this->Articles->find('translations');
// ...
public function index()
{
    $sort = $this->request->query('sort');
    if ($sort) {
        $fieldMap = [
            'Articles_title_translation.content' => 'Articles.title'
        ];
        if (
            isset($fieldMap[$sort]) &&
            $this->Articles->locale() ===
                $this->Articles->behaviors()->get('Translate')->config('defaultLocale')
        ) {
            $this->request->query['sort'] = $fieldMap[$sort];
        }
    }

    $query = $this->Articles->find('translations');
    $this->set('articles', $this->paginate($query));
}
// Remove $this->addBehavior('Translate', ['fields' => ['title']]);
// and load the custom behavior instead (otherwise there will be an
// error about "duplicate translation finders"

$this->addBehavior('MyTranslate', [
    'fields' => ['title'],
    'locales' => ['es', 'de']
]);
namespace App\Model\Behavior;

use Cake\ORM\Behavior\TranslateBehavior;
use Cake\ORM\Query;
use Cake\ORM\Table;

class MyTranslateBehavior extends TranslateBehavior
{
    protected $_associations = [];

    public function __construct(Table $table, array $config)
    {
        $config += [
            'locales' => []
        ];

        parent::__construct($table, $config);
    }

    public function setupFieldAssociations($fields, $table, $model, $strategy)
    {
        parent::setupFieldAssociations($fields, $table, $model, $strategy);

        $alias = $this->_table->alias();
        $tableLocator = $this->tableLocator();
        $locales = $this->config('locales');

        $this->_associations = [];
        foreach ($fields as $field) {
            foreach ($locales as $locale) {
                $name = $alias . '_' . $field . '_translation_' . $locale;

                if (!$tableLocator->exists($name)) {
                    $fieldTable = $tableLocator->get($name, [
                        'className' => $table,
                        'alias' => $name,
                        'table' => $this->_translationTable->table()
                    ]);
                } else {
                    $fieldTable = $tableLocator->get($name);
                }

                $conditions = [
                    $name . '.locale' => $locale,
                    $name . '.model' => $model,
                    $name . '.field' => $field
                ];

                $this->_table->hasOne($name, [
                    'targetTable' => $fieldTable,
                    'foreignKey' => 'foreign_key',
                    'joinType' => 'LEFT',
                    'conditions' => $conditions,
                    'propertyName' => $field . '_translation_' . $locale
                ]);

                $this->_associations[] = $name;
            }
        }
    }

    public function findTranslations(Query $query, array $options)
    {
        $query->contain($this->_associations);
        return parent::findTranslations($query, $options);
    }
}
public $paginate = [
    'order' => ['Articles.title' => 'ASC']
    'sortWhitelist' => [
        'Articles.title',
        'Articles_title_translation_de.content',
        'Articles_title_translation_es.content'
    ]
];
$this->Paginator->sort('Articles.title', 'English');
$this->Paginator->sort('Articles_title_translation_es.content', 'Spanish');
$this->Paginator->sort('Articles_title_translation_de.content', 'German');