0

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

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

活动标签:

    $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 'where 子句'中的未知列'Tags_name_translation.content'

缺少以下联接:

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?我的意图是通过翻译的标签过滤活动。它适用于非翻译。

4

1 回答 1

1

如链接问题中所述,就像包含的hasMany关联一样,belongsToMany关联是在单独的查询中检索的,这就是翻译行为会跳入并包含翻译关联的地方(每个字段都由一个单独的hasOne关联表示),所以翻译表将被加入。

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

因此,无需过多考虑,您可以例如使用相关子查询(是的,我知道,它可能表现不佳)作为过滤条件,即通过Tags表查询所需的标签,其中翻译将被包括在内,并使用例如EXISTS条件 on 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);
    });

可以看出,这将需要手动加入连接表(ActivitiesTags)(在早期的 CakePHP 版本中,您可能需要手动添加该关联 IIRC),以便您可以匹配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()和做什么TranslateBehavior::beforeFind(),你必须应用类似于你的Activities表的东西才能实现这一点。

于 2018-04-16T12:17:11.250 回答