【问题标题】:CakePHP 3: Paginator sort by languages (with i18n translate behavior)CakePHP 3:按语言排序的分页器(具有 i18n 翻译行为)
【发布时间】:2016-03-29 08:25:00
【问题描述】:

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

总结:这行不通,我不能这样按翻译排序:

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

加长版:

  • 我正在使用带有 translate behaviori18n 表
  • 我在一个表格中列出了所有翻译(多种语言)。
  • 现在我想在翻译上使用 pagination sort links(按语言排序)
| Title ENGLISH    | Title SPANISH    | Title GERMAN    |  = pagination sort links
| ---------------- | ---------------- | --------------- |
| Christmas        | Navidad          | Weihnachten     |
| Spring           | Primavera        | Frühling        |
| ...

这是我的简化测试设置:

Articles 表只有一个字段 title 需要翻译。
i18n 表默认设置为 described in the book

烘焙表格/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
}

烘焙Entity/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
}

烘焙Controller /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

this similar question 中使用了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 翻译对表格项进行排序?

【问题讨论】:

    标签: sorting cakephp pagination cakephp-3.1 cakephp-3.x


    【解决方案1】:

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

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

    翻译后的字段只加入非默认的当前语言环境

    默认情况下,仅在当前语言环境(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 格式,如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');
    

    另见

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-01
      • 2012-03-25
      • 2015-12-31
      • 2015-10-27
      • 1970-01-01
      • 1970-01-01
      • 2012-09-08
      • 1970-01-01
      相关资源
      最近更新 更多