Laravel: 使用 eloquent 连接从第 2 个 table 开始仅限制 1 条记录
Laravel: Limit only 1 record from the 2nd table using eloquent join
我有两个 table colors
和 color_translations
结构如下:
颜色
id
created_at
updated_at
1
2021-08-25
NULL
2
2021-09-01
NULL
color_translations
id
color_id
locale
title
url
1
1
en
blue
blue-cat
2
1
fr
bleu
bleu-cat
3
1
de
blau
blau-cat
4
2
de
rot
rot-cat
5
2
fr
rouge
rouge-cat
我只想将 colors
table 与 color_translations
table 中的一条记录合并,该记录将基于 locale 列,它会先查看 en locale
记录,如果不存在则从 fr locale
,然后 de locale
id
created_at
updated_at
locale
title
url
1
2021-08-25
NULL
en
blue
blue-cat
2
2021-09-01
NULL
fr
rouge
rouge-cat
我尝试使用我的 eloquent 模型这样做:
$this->colorsModel
->select(
[
'colors.*',
'color_translations.locale as locale',
'color_translations.title as title',
'color_translations.url as url'
]
)
->leftJoin ('color_translations', function ($query) {
$query->on('colors.id', '=', 'color_translations.color_id')
->orderByRaw('FIELD(color_translations.locale, "en", "fr", "de)')
->limit(1);
})->get();
使用上面的代码而不是 2 条记录我从 color_translations table
中获取所有 5 条记录
我不太了解laravel
但查询应该如下所示:
(使用@Bill Karwin here 描述的最佳性能技术)
SELECT t1.*, t2.locale, t2.title, t2.url
FROM colors t1
LEFT OUTER JOIN color_translation t2 ON t1.id = t2.color_id
LEFT OUTER JOIN color_translation t3 ON t2.color_id = t3.color_id
AND FIELD(t2.locale, 'en', 'fr', 'de') > FIELD(t3.locale, 'en', 'fr', 'de')
WHERE t3.id IS NULL;
注意如果您以后添加任何新的语言环境,该语言环境将具有最高优先级,如 FIELD()
returns 0 对于未指定的值。我建议您确保每次在您的应用程序中 运行 此查询。
我努力写在 laravel
:
$this->colorsModel
->select(
[
'colors.*',
't2.locale as locale',
't2.title as title',
't2.url as url'
]
)
->leftJoin ('color_translations AS t2', function ($query) {
$query->on('colors.id', '=', 't2.color_id')
})->leftJoin ('color_translations AS t3', function ($query) {
$query->on('t2.color_id', '=', 't3.color_id');
$query->on(DB::raw('FIELD(t2.locale, \'en\', \'fr\', \'de\')'), '>', DB::raw('FIELD(t3.locale, \'en\', \'fr\', \'de\')'));
})->whereNull('t3.id')->get();
您可以按原始查询顺序使用案例。这将从选项中选择任何一个。
$this->colorsModel
->select(
[
'colors.*',
'color_translations.locale as locale',
'color_translations.title as title',
'color_translations.url as url'
]
)
->leftJoin ('color_translations', function ($query) {
$query->on('colors.id', '=', 'color_translations.color_id')
->orderByRaw('FIELD(color_translations.locale, CASE WHEN (color_translations.locale = "en") THEN en
WHEN (color_translations.locale = "fr") THEN fr
ELSE de )')
->limit(1);
})->get();
如果你想用 SQL/Eloquent 做到这一点而不影响性能,你确实可以通过查询所有翻译来使用 orderBy:
$locales_ordered = ['en', 'fr', 'nl'];
array_walk($locales_ordered, function(&$x) {$x = "'$x'";});
$query = Colors::with([ 'translations' => function ($q) use ($locales_ordered) {
$q->orderByRaw('FIELD(colors_translations.locale,' . implode(',', $locales_ordered) . ') ASC')->first();
} ])
->first();
$translation = $query->first()->translations->first();
这将获得第一个关联的翻译,按您的自定义语言环境列表顺序排序,因此语言环境回退。假设您有适当的模型,并且您的模型和翻译后的模型之间存在 translations
关系 (color hasMany color_translations
)。
array_walk
是使用字符串将 orderByRaw
和 FIELD
的值用双引号括起来。
您可以将其包装在 Color
模型的作用域函数中以便轻松查询:
public function scopeWithFallbackTranslation($query)
{
$locales_ordered = ['en', 'fr', 'nl']; //todo get from helper class, headers, ssession etc..
array_walk($locales_ordered, function(&$x) {$x = "'$x'";});
return $query
->with([ 'translations' => function ($q) use ($locales_ordered) {
$q
->orderByRaw('FIELD(email_notification_senders_translations.locale,' . implode(',', $locales_ordered) . ') ASC')
->first();
} ]);
}
然后调用它:Color::withFallbackTranslation()->where(stuff)->get()
请注意,->translations
仍将是一个集合,因此需要 ->first()
(和空检查)。例如,您可以使用 Color 模型上的自定义属性获取单个对象。
我有两个 table colors
和 color_translations
结构如下:
颜色
id | created_at | updated_at |
---|---|---|
1 | 2021-08-25 | NULL |
2 | 2021-09-01 | NULL |
color_translations
id | color_id | locale | title | url |
---|---|---|---|---|
1 | 1 | en | blue | blue-cat |
2 | 1 | fr | bleu | bleu-cat |
3 | 1 | de | blau | blau-cat |
4 | 2 | de | rot | rot-cat |
5 | 2 | fr | rouge | rouge-cat |
我只想将 colors
table 与 color_translations
table 中的一条记录合并,该记录将基于 locale 列,它会先查看 en locale
记录,如果不存在则从 fr locale
,然后 de locale
id | created_at | updated_at | locale | title | url |
---|---|---|---|---|---|
1 | 2021-08-25 | NULL | en | blue | blue-cat |
2 | 2021-09-01 | NULL | fr | rouge | rouge-cat |
我尝试使用我的 eloquent 模型这样做:
$this->colorsModel
->select(
[
'colors.*',
'color_translations.locale as locale',
'color_translations.title as title',
'color_translations.url as url'
]
)
->leftJoin ('color_translations', function ($query) {
$query->on('colors.id', '=', 'color_translations.color_id')
->orderByRaw('FIELD(color_translations.locale, "en", "fr", "de)')
->limit(1);
})->get();
使用上面的代码而不是 2 条记录我从 color_translations table
中获取所有 5 条记录我不太了解laravel
但查询应该如下所示:
(使用@Bill Karwin here 描述的最佳性能技术)
SELECT t1.*, t2.locale, t2.title, t2.url
FROM colors t1
LEFT OUTER JOIN color_translation t2 ON t1.id = t2.color_id
LEFT OUTER JOIN color_translation t3 ON t2.color_id = t3.color_id
AND FIELD(t2.locale, 'en', 'fr', 'de') > FIELD(t3.locale, 'en', 'fr', 'de')
WHERE t3.id IS NULL;
注意如果您以后添加任何新的语言环境,该语言环境将具有最高优先级,如 FIELD()
returns 0 对于未指定的值。我建议您确保每次在您的应用程序中 运行 此查询。
我努力写在 laravel
:
$this->colorsModel
->select(
[
'colors.*',
't2.locale as locale',
't2.title as title',
't2.url as url'
]
)
->leftJoin ('color_translations AS t2', function ($query) {
$query->on('colors.id', '=', 't2.color_id')
})->leftJoin ('color_translations AS t3', function ($query) {
$query->on('t2.color_id', '=', 't3.color_id');
$query->on(DB::raw('FIELD(t2.locale, \'en\', \'fr\', \'de\')'), '>', DB::raw('FIELD(t3.locale, \'en\', \'fr\', \'de\')'));
})->whereNull('t3.id')->get();
您可以按原始查询顺序使用案例。这将从选项中选择任何一个。
$this->colorsModel
->select(
[
'colors.*',
'color_translations.locale as locale',
'color_translations.title as title',
'color_translations.url as url'
]
)
->leftJoin ('color_translations', function ($query) {
$query->on('colors.id', '=', 'color_translations.color_id')
->orderByRaw('FIELD(color_translations.locale, CASE WHEN (color_translations.locale = "en") THEN en
WHEN (color_translations.locale = "fr") THEN fr
ELSE de )')
->limit(1);
})->get();
如果你想用 SQL/Eloquent 做到这一点而不影响性能,你确实可以通过查询所有翻译来使用 orderBy:
$locales_ordered = ['en', 'fr', 'nl'];
array_walk($locales_ordered, function(&$x) {$x = "'$x'";});
$query = Colors::with([ 'translations' => function ($q) use ($locales_ordered) {
$q->orderByRaw('FIELD(colors_translations.locale,' . implode(',', $locales_ordered) . ') ASC')->first();
} ])
->first();
$translation = $query->first()->translations->first();
这将获得第一个关联的翻译,按您的自定义语言环境列表顺序排序,因此语言环境回退。假设您有适当的模型,并且您的模型和翻译后的模型之间存在 translations
关系 (color hasMany color_translations
)。
array_walk
是使用字符串将 orderByRaw
和 FIELD
的值用双引号括起来。
您可以将其包装在 Color
模型的作用域函数中以便轻松查询:
public function scopeWithFallbackTranslation($query)
{
$locales_ordered = ['en', 'fr', 'nl']; //todo get from helper class, headers, ssession etc..
array_walk($locales_ordered, function(&$x) {$x = "'$x'";});
return $query
->with([ 'translations' => function ($q) use ($locales_ordered) {
$q
->orderByRaw('FIELD(email_notification_senders_translations.locale,' . implode(',', $locales_ordered) . ') ASC')
->first();
} ]);
}
然后调用它:Color::withFallbackTranslation()->where(stuff)->get()
请注意,->translations
仍将是一个集合,因此需要 ->first()
(和空检查)。例如,您可以使用 Color 模型上的自定义属性获取单个对象。