Laravel 将 1 列值作为数组加载关系
Laravel load relationship with 1 column values as array
Laravel 中有一种方法可以加载只有 1 列值的关系并将其作为数组吗?
我有 belongsToMany
关系,从 Page
模特到 Country
、State
模特。
public function countries(){
return $this->belongsToMany(Country::class, 'page_country', 'page_id', 'country_id');
}
public function states(){
return $this->belongsToMany(State::class, 'page_state', 'page_id', 'state_id');
}
当我加载这些关系时,我需要将结果作为值数组 [1,2,3,4]
(相关模型的 ID)获取。换句话说,我只需要为我的 Page
加载 states
和 countries
中的 id
所以不是键值对:
[
'states' => ['id' => 1, 'id' => 2, ...],
'countries' => ['id' => 1, 'id' => 2, ...]
]
我想实现这个:
[
'states' => [1, 2, ...],
'countries' => [1, 2, ...]
]
我目前在做的是:
加载关系:
public function view(Page $page){
$page->load([
'countries:id',
'states:id'
]);
//Transform to get array of ids for each relation
$page->countries->transform(function($item){
return $item->id;
});
$page->states->transform(function($item){
return $item->id;
});
//and return as json
return response()->json($page)
}
也许有更好的方法来实现这一点?因为当我需要加载 10 个关系时,我需要 运行 10 个转换函数。
据我所知,这对于急切加载关系是不可能的。由于预先加载已经为每个关系执行了额外的数据库查询,因此数据库上没有额外的负载来执行此操作:
public function view(Page $page)
{
$result = $page->toArray();
$result['countries'] = $page->countries()->pluck('id');
$result['states'] = $page->states()->pluck('id');
//and return as json
return response()->json($result)
}
您似乎很担心必须执行此操作 10 次,因此如果您愿意,可以将其放入循环中。
public function view(Page $page)
{
$result = $page->toArray();
$relationships = ['countries', 'states', ];
foreach ($relationship as $rel) {
$result[$rel] = $page->{$rel}()->pluck('id');
}
//and return as json
return response()->json($result)
}
但实际上,您只编写一次代码,那么编写 10 次有什么问题?
您可能还想看看 Eloquent resources and collections;根据您的用例,它们可能会有所帮助。
最后一个建议是远离 Eloquent 并使用查询构建器:
public function view(int $page)
{
$result = Page::select('pages.*')
->selectRaw('GROUP_CONCAT(countries.id) AS countries')
->selectRaw('GROUP_CONCAT(states.id) AS states')
->leftJoin('countries', 'countries.page_id', 'pages.id')
->leftJoin('states', 'states.page_id', 'pages.id')
->where('pages.id', $page)
->groupBy('pages.id')
->get();
//and return as json
return response()->json($result)
}
这样做的好处是可以大大减少对数据库的访问次数,但必须熟悉一些丑陋的 SQL 语法。它将 return 外国 ID 作为逗号分隔列表,您可以在客户端处理,或在 return 响应之前处理。
您可以为此使用 Accessor。将这些函数添加到您的 Page.php
模型中:
public function getCountryIdsAttribute() {
return $this->countries->pluck('id');
}
public function getStateIdsAttribute() {
return $this->states->pluck('id');
}
此外,如果您想在 JSON 响应中自动包含这些内容,请同时添加以下内容:
protected $appends = ['countryIds', 'stateIds'];
现在,您的代码将基本相同:
public function view(Page $page) {
$page->load([
'countries:id',
'states:id'
]);
return response()->json($page);
}
您仍然希望为 $this->countries->pluck('id')
和 $this->states->pluck('id')
加载 countries
和 states
以在不调用 2 个额外查询的情况下运行,但是您的响应看起来像这个:
[
'states' => ['id' => 1, 'id' => 2, ...],
'countries' => ['id' => 1, 'id' => 2, ...],
'country_ids' => [1, 2, ...],
'state_ids' => [1, 2, ...]
]
如果您不想包含 states
和 countries
,您只需将它们添加到 Page.php
模型中的 hidden
:
protected $hidden = ['countries', 'states'];`
这将导致:
[
'country_ids' => [1, 2, ...],
'state_ids' => [1, 2, ...]
]
您可以在此处查看有关 appends
和 hidden
模型属性的更多信息:
https://laravel.com/docs/8.x/eloquent-serialization
这样做的主要缺点是您的字段在作为 JSON 对象返回时不再命名为 countries
和 states
,但这确实消除了额外迭代的需要,同时映射、手动调用覆盖等
Laravel 中有一种方法可以加载只有 1 列值的关系并将其作为数组吗?
我有 belongsToMany
关系,从 Page
模特到 Country
、State
模特。
public function countries(){
return $this->belongsToMany(Country::class, 'page_country', 'page_id', 'country_id');
}
public function states(){
return $this->belongsToMany(State::class, 'page_state', 'page_id', 'state_id');
}
当我加载这些关系时,我需要将结果作为值数组 [1,2,3,4]
(相关模型的 ID)获取。换句话说,我只需要为我的 Page
states
和 countries
中的 id
所以不是键值对:
[
'states' => ['id' => 1, 'id' => 2, ...],
'countries' => ['id' => 1, 'id' => 2, ...]
]
我想实现这个:
[
'states' => [1, 2, ...],
'countries' => [1, 2, ...]
]
我目前在做的是:
加载关系:
public function view(Page $page){
$page->load([
'countries:id',
'states:id'
]);
//Transform to get array of ids for each relation
$page->countries->transform(function($item){
return $item->id;
});
$page->states->transform(function($item){
return $item->id;
});
//and return as json
return response()->json($page)
}
也许有更好的方法来实现这一点?因为当我需要加载 10 个关系时,我需要 运行 10 个转换函数。
据我所知,这对于急切加载关系是不可能的。由于预先加载已经为每个关系执行了额外的数据库查询,因此数据库上没有额外的负载来执行此操作:
public function view(Page $page)
{
$result = $page->toArray();
$result['countries'] = $page->countries()->pluck('id');
$result['states'] = $page->states()->pluck('id');
//and return as json
return response()->json($result)
}
您似乎很担心必须执行此操作 10 次,因此如果您愿意,可以将其放入循环中。
public function view(Page $page)
{
$result = $page->toArray();
$relationships = ['countries', 'states', ];
foreach ($relationship as $rel) {
$result[$rel] = $page->{$rel}()->pluck('id');
}
//and return as json
return response()->json($result)
}
但实际上,您只编写一次代码,那么编写 10 次有什么问题?
您可能还想看看 Eloquent resources and collections;根据您的用例,它们可能会有所帮助。
最后一个建议是远离 Eloquent 并使用查询构建器:
public function view(int $page)
{
$result = Page::select('pages.*')
->selectRaw('GROUP_CONCAT(countries.id) AS countries')
->selectRaw('GROUP_CONCAT(states.id) AS states')
->leftJoin('countries', 'countries.page_id', 'pages.id')
->leftJoin('states', 'states.page_id', 'pages.id')
->where('pages.id', $page)
->groupBy('pages.id')
->get();
//and return as json
return response()->json($result)
}
这样做的好处是可以大大减少对数据库的访问次数,但必须熟悉一些丑陋的 SQL 语法。它将 return 外国 ID 作为逗号分隔列表,您可以在客户端处理,或在 return 响应之前处理。
您可以为此使用 Accessor。将这些函数添加到您的 Page.php
模型中:
public function getCountryIdsAttribute() {
return $this->countries->pluck('id');
}
public function getStateIdsAttribute() {
return $this->states->pluck('id');
}
此外,如果您想在 JSON 响应中自动包含这些内容,请同时添加以下内容:
protected $appends = ['countryIds', 'stateIds'];
现在,您的代码将基本相同:
public function view(Page $page) {
$page->load([
'countries:id',
'states:id'
]);
return response()->json($page);
}
您仍然希望为 $this->countries->pluck('id')
和 $this->states->pluck('id')
加载 countries
和 states
以在不调用 2 个额外查询的情况下运行,但是您的响应看起来像这个:
[
'states' => ['id' => 1, 'id' => 2, ...],
'countries' => ['id' => 1, 'id' => 2, ...],
'country_ids' => [1, 2, ...],
'state_ids' => [1, 2, ...]
]
如果您不想包含 states
和 countries
,您只需将它们添加到 Page.php
模型中的 hidden
:
protected $hidden = ['countries', 'states'];`
这将导致:
[
'country_ids' => [1, 2, ...],
'state_ids' => [1, 2, ...]
]
您可以在此处查看有关 appends
和 hidden
模型属性的更多信息:
https://laravel.com/docs/8.x/eloquent-serialization
这样做的主要缺点是您的字段在作为 JSON 对象返回时不再命名为 countries
和 states
,但这确实消除了额外迭代的需要,同时映射、手动调用覆盖等