如何通过belongsToMany关联的翻译进行筛选?

时间:2018-04-15 19:34:31

标签: cakephp internationalization translation query-builder cakephp-3.5

我想查询关联的belongsToMany关系中的翻译。根据文档和this question,应该可以在翻译中查询关联。我尝试了以下(简化)代码:

    $result = $this->table()->find()
        ->where([
            $this->Activities->Tags->translationField('name') . ' LIKE' => 
                '%' . $request->filter . '%'
            ])
        ->leftJoinWith('Tags')
        ->contain(['Tags'])
        ->all()
        ->toArray();

标签和活动有很多关系。

活动:

    $this->belongsToMany('Tags', [
        'foreignKey' => 'activity_id',
        'targetForeignKey' => 'tag_id',
        'joinTable' => 'activities_tags'
    ]);

    $this->addBehavior('Translate', ['fields' => ['name', 'description']]);

标签:

    $this->belongsToMany('Activities', [
        'foreignKey' => 'tag_id',
        'targetForeignKey' => 'activity_id',
        'joinTable' => 'activities_tags'
    ]);

    $this->addBehavior('Translate', ['fields' => ['name']]);

ActivityTag:

    $this->belongsTo('Activities', [
        'foreignKey' => 'activity_id',
        'joinType' => 'INNER'
    ]);
    $this->belongsTo('Tags', [
        'foreignKey' => 'tag_id',
        'joinType' => 'INNER'
    ]);

但是,我得到以下生成的SQL:

SELECT 
    ...
FROM `activities` `Activities` 
LEFT JOIN `activities_tags` `ActivitiesTags` ON `Activities`.`id` = (`ActivitiesTags`.`activity_id`) 
LEFT JOIN `tags` `Tags` ON `Tags`.`id` = (`ActivitiesTags`.`tag_id`) 
LEFT JOIN `i18n` `Activities_name_translation` ON (
    `Activities_name_translation`.`model` = :c0 
    AND `Activities_name_translation`.`field` = :c1 
    AND `Activities_name_translation`.`locale` = :c2 
    AND `Activities`.`id` = (`Activities_name_translation`.`foreign_key`)
) 
LEFT JOIN `i18n` `Activities_description_translation` ON (
    `Activities_description_translation`.`model` = :c3 
    AND `Activities_description_translation`.`field` = :c4 
    AND `Activities_description_translation`.`locale` = :c5 
    AND `Activities`.`id` = (`Activities_description_translation`.`foreign_key`)
) 
WHERE `Tags_name_translation`.`content` like :c6

这导致我出现以下错误:

  

QLSTATE [42S22]:未找到列:1054未知列' Tags_name_translation.content'在' where子句'

缺少以下联接:

LEFT JOIN `i18n` `Tags_name_translation` ON (
   `Tags_name_translation`.`model` = :c6 
    AND `Tags_name_translation`.`field` = :c7 
    AND `Tags_name_translation`.`locale` = :c8 
    AND `Tags`.`id` = (`Tags_name_translation`.`foreign_key`)
) 

现在我的问题/编辑:

为了生成缺少的连接,我缺少什么来配置CakePHP?我的目的是通过翻译的标签过滤活动。它正在为非翻译工作。

1 个答案:

答案 0 :(得分:1)

正如链接问题中所提到的,就像包含的hasMany关联一样,belongsToMany关联在单独的查询中被检索,这就是Translate行为将跳入并包含翻译关联的位置(每个字段由单独的hasOne关联表示),以便将转换表加入。

对于*joinWith()*matching()也是如此,而它将在主查询上应用连接和条件,实际关联内容及其相关翻译再次在单独的查询中检索,即转换行为不会涉及主查询,因此转换表没有被加入。有人可能称之为ORM的缺点,也许某种钩子加入/匹配在行为可以修改的情况下会有所帮助相应的查询,但现在没有这样的事情。

所以,没有过多的考虑,你可以使用相关的子查询(是的,我知道,它可能不会表现得太好)作为过滤条件,即通过{{1查询所需的标签表格,其中包含翻译,并在Tags上使用EXISTS条件,类似于以下内容:

Activities

可以看出,这将需要手动加入连接表($tagsQuery = $this->Activities->Tags ->find() ->select(['id']) ->innerJoinWith('ActivitiesTags') ->where(function (\Cake\Database\Expression\QueryExpression $exp) use ($request) { return $exp ->equalFields('ActivitiesTags.activity_id', 'Activities.id') ->like( $this->Activities->Tags->translationField('name'), '%' . $request->filter . '%' ); }); $activitiesQuery = $this->Activities ->find() ->where(function ($exp) use ($tagsQuery) { return $exp->exists($tagsQuery); }); )(在早期的CakePHP版本中,您可能需要手动添加该关联IIRC),以便您可以匹配{{1 }}。生成的查询应如下所示:

ActivitiesTags

最有可能解决此问题的其他方法,例如创建和包含其他翻译关联"手动"在ActivitiesTags.activity_id时间。看看SELECT `Activities`.`id` AS `Activities__id`, ... FROM `activities` `Activities` WHERE EXISTS ( SELECT `Tags`.`id` AS `Tags__id` FROM `tags` `Tags` INNER JOIN `activities_tags` `ActivitiesTags` ON `Tags`.`id` = (`ActivitiesTags`.`tag_id`) LEFT JOIN `i18n` `Tags_name_translation` ON ( `Tags_name_translation`.`model` = 'Tags' AND `Tags_name_translation`.`field` = 'name' AND `Tags_name_translation`.`locale` = 'en_US' AND `Tags`.`id` = (`Tags_name_translation`.`foreign_key`) ) WHERE ( `ActivitiesTags`.`activity_id` = (`Activities`.`id`) AND `Tags_name_translation`.`content` LIKE '%foobarbaz%' ) ) Model.beforeFind的作用,您必须应用类似于TranslateBehavior::setupFieldAssociations()表的内容才能实现此目标。