如何通过 belongsToMany 关联的翻译进行过滤?
How to filter by translations of a belongsToMany association?
我想在关联的 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]: Column not found: 1054 Unknown column 'Tags_name_translation.content' in 'where clause'
缺少以下连接:
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`)
)
现在我的question/Edit:
为了生成缺少的连接,我缺少什么来配置 CakePHP?我的目的是通过翻译的 Tags 过滤 Activities。它适用于非翻译。
如链接问题中所述,就像包含的 hasMany
关联一样,belongsToMany
关联是在单独的查询中检索的,这就是翻译行为将跳入并包含翻译关联(每个字段都由一个单独的 hasOne
关联表示),因此翻译 table 将被加入。
*joinWith()
和 *matching()
也是如此,虽然它将在主查询上应用连接和条件,但实际的关联内容及其相关翻译将再次在单独的查询中检索,即翻译行为不会参与主查询,因此翻译 table 不会被加入。人们可能会称之为 ORM 的缺点,也许是 [=37= 的某种挂钩] 在行为可以相应地修改查询的情况下会有帮助,但目前还没有这样的东西。
因此,无需过多考虑,您可以使用相关子查询(是的,我知道,它可能不会执行得太好)作为过滤条件,即通过 Tags
table,其中将包含翻译,并在 Activities
上使用例如 EXISTS
条件,类似于:
$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);
});
正如所见,这将需要手动加入 table (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
时间创建和包含额外的翻译关联 "manually"。看看 TranslateBehavior::setupFieldAssociations()
和 TranslateBehavior::beforeFind()
的作用,你必须应用类似于 Activities
table 的东西才能实现这一点。
我想在关联的 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]: Column not found: 1054 Unknown column 'Tags_name_translation.content' in 'where clause'
缺少以下连接:
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`)
)
现在我的question/Edit:
为了生成缺少的连接,我缺少什么来配置 CakePHP?我的目的是通过翻译的 Tags 过滤 Activities。它适用于非翻译。
如链接问题中所述,就像包含的 hasMany
关联一样,belongsToMany
关联是在单独的查询中检索的,这就是翻译行为将跳入并包含翻译关联(每个字段都由一个单独的 hasOne
关联表示),因此翻译 table 将被加入。
*joinWith()
和 *matching()
也是如此,虽然它将在主查询上应用连接和条件,但实际的关联内容及其相关翻译将再次在单独的查询中检索,即翻译行为不会参与主查询,因此翻译 table 不会被加入。人们可能会称之为 ORM 的缺点,也许是 [=37= 的某种挂钩] 在行为可以相应地修改查询的情况下会有帮助,但目前还没有这样的东西。
因此,无需过多考虑,您可以使用相关子查询(是的,我知道,它可能不会执行得太好)作为过滤条件,即通过 Tags
table,其中将包含翻译,并在 Activities
上使用例如 EXISTS
条件,类似于:
$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);
});
正如所见,这将需要手动加入 table (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
时间创建和包含额外的翻译关联 "manually"。看看 TranslateBehavior::setupFieldAssociations()
和 TranslateBehavior::beforeFind()
的作用,你必须应用类似于 Activities
table 的东西才能实现这一点。