1

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

摘要:这不起作用,我无法以这种方式对翻译进行排序:

$this->Paginator->sort('_translations.es.title', 'Spanish')

长版:

  • 我正在使用带有翻译行为的i18n 表
  • 我将所有翻译(多种语言)组合在一个表中。
  • 现在我想在翻译上使用分页排序链接(按语言排序)
| Title ENGLISH    | Title SPANISH    | Title GERMAN    |  = pagination sort links
| ---------------- | ---------------- | --------------- |
| Christmas        | Navidad          | Weihnachten     |
| Spring           | Primavera        | Frühling        |
| ...

所以这是我的简化测试设置:

表格文章只有一个title需要翻译的字段。
i18n表是本书中描述的默认设置。

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

public function initialize(array $config) {
  // ... default config (removed in this post to simplify code)
  $this->addBehavior('Translate', ['fields' => ['title']]);  // added this line
}

烘焙实体/src/Model/Entity/Article.php,添加TranslateTrait

namespace App\Model\Entity;
use Cake\ORM\Behavior\Translate\TranslateTrait; // added this line
use Cake\ORM\Entity;
class Article extends Entity {
  protected $_accessible = [
    '*' => true,
    'id' => false,
  ];
  use TranslateTrait; // added this line
}

烘焙控制器 /src/Controller/ArticlesController.php,修改如下:

namespace App\Controller;
use App\Controller\AppController;
class ArticlesController extends AppController {
  public $paginate = [
    'sortWhitelist' => [  // Allow pagination sort on this fields:
      'title',
      '_translations.es.title',
      '_translations.de.title'
    ]
  ];
  public function index() {
    $query = $this->Articles->find('translations'); // Retrieve All Translations
    $this->set('articles', $this->paginate($query));
  }
}

烘焙视图 /src/Template/Articles/index.ctp,修改/简化:

<table>
  <tr>
    <th><?= $this->Paginator->sort('title', 'English') ?></th>
    <th><?= $this->Paginator->sort('_translations.es.title', 'Spanish') ?></th>
    <th><?= $this->Paginator->sort('_translations.de.title', 'German') ?></th>
  </tr>
<?php foreach ($articles as $article): ?>
  <tr>
    <td><?= h($article->title) ?></td>
    <td><?= h($article->_translations['es']->title) ?></td>
    <td><?= h($article->_translations['de']->title) ?></td>
  </tr>
<?php endforeach; ?>
</table>

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

SQLSTATE [42S22]:未找到列:1054 未知列“_translations.es”在“订单子句”中

SQL Query:
SELECT Articles.id AS `Articles__id`,
       Articles.title AS `Articles__title`,
       Articles.created AS `Articles__created`,
       Articles.modified AS `Articles__modified`
FROM articles Articles 
ORDER BY _translations.es asc LIMIT 20 OFFSET 0

这个类似的问题中,使用了格式Posts_title_translation.content- 我不知道这是从哪里来的,但我也尝试过这种方式(当然我还在分页器白名单中添加了字段名称的变体):

$this->Paginator->sort('Articles_title_translation', 'Spanish')
$this->Paginator->sort('Articles_title_translation.es', 'Spanish')
$this->Paginator->sort('Articles_title_translation.content', 'Spanish')
$this->Paginator->sort('Articles_title_translation.es.content', 'Spanish')

他们都没有工作......(显然)

如何按标题字段的 i18n 翻译对表格项目进行排序?

4

1 回答 1

2

对翻译的字段进行排序需要连接

通常,您只能对连接到主查询的字段进行排序!

翻译的字段仅与非默认的当前语言环境相结合

默认情况下,仅在当前语言环境 ( ) 与默认语言环境 ( , )I18n::locale()不匹配的情况下,即实际需要翻译某些内容时,才加入翻译的字段。I18N::defaultLocale()intl.default_locale

一旦将当前语言环境更改为非默认

I18n::locale('de');
$query = $this->Articles->find('translations');
// ...

翻译行为将包含与翻译内容的关联,这就是TableAlias_field_translation别名的来源,该行为hasOne使用该命名方案为每个翻译字段创建关联。

然后可以将这些字段用于分页,但是这一次只能加入一个语言环境!

确保分页器使用正确的字段

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

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

这会将字段映射到从翻译字段排序到原始字段,以防不包含任何翻译。

加入并排序字段的所有翻译/语言

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

这可能是一个功能请求的东西,我不确定这是否是一个常见的用例来证明这种核心修改是合理的,你可能想在GitHub 上打开一个问题或在IRC上提问。

话虽如此,这是一个基本示例,一个扩展的翻译行为,它几乎做了什么TranslateBehavior::setupFieldAssociations()TranslateBehavior::beforeFind()做什么,只是稍微修改了一下。该行为需要一个locales选项,该选项需要提供所有应加入的语言环境,因为它们无法自动计算。

src/Model/Table/ArticlesTable.php

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

src/Model/Behavior/MyTranslateBehavior.php

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

这样做应该相对容易理解,它将简单地hasOne为所有配置的语言环境中的所有字段创建并包含关联。别名将使用格式TableAlias_field_translation_locale, like Articles_title_translation_es,这是排序白名单和分页器排序链接中需要使用的格式。

应该注意的是,加入的新字段可能需要默认排序顺序,否则它可能会对用于查询翻译的子查询进行不同于主查询的排序,从而导致检索到错误的翻译!

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

也可以看看

于 2015-12-23T17:01:59.257 回答