如何在 laravel 中检索有限数量的相关模型并按相关模型对集合进行排序?
how to retrieve limited number of related model and sort collection by related model in laravel?
我有3个模型
店铺型号,商品型号,图片型号
我想检索包含最后 3 个产品型号及其图片的商店集合,并根据最新产品对我的集合进行排序。
我在 laravel 6 中尝试了 leftjoint
和 joint
来对结果进行排序,但我得到了所有商店的产品(我只需要最后 3 个产品每家商店),
当我使用关节时,我无法检索产品图片
我也尝试过 laravel 中的“with
”方法,我无法根据 product.creatred
_at 对结果进行排序,而且我也通过这种方法获得了所有相关产品。(正如我提到的,我需要最后 3 个产品)
class Shop extends Model
{
public function products()
{
return $this->hasMany('App\Product');
}
}
class Product extends Model
{
public function shop()
{
return $this->belongsTo('App\Shop');
}
public function pictures()
{
return $this->morphMany('App\hPicture', 'pictureable');
}
}
Shop::select('shops.*', 'products.id', 'products.shop_id', 'products.name as pname', 'products.user_id', 'products.code', 'products.price')
->with(['pictures', 'products.pictures'])
->leftjoin('products', function ($leftJoin) {
$leftJoin->on('shops.id', '=', 'products.shop_id');
});
$dataList = $dataList->orderBy($field, $order);
$dataList = $dataList->paginate(5)->appends(['sortField' => $field, 'sortOrder' => $order]);
产品和商店模型的 table 布局是:
Schema::create('shops', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('slug');
$table->string('phone')->nullable();
$table->string('address')->nullable();
$table->timestamps();
$table->string('description')->nullable();
$table->uuid('uuid');
});
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->integer('shop_id')->unsigned();
$table->foreign('shop_id')->references('id')->on('shops');
$table->string('name');
$table->string('code');
$table->string('slug');
$table->integer('price');
$table->uuid('uuid');
$table->timestamps();
});
只有两种解决方法:
- 要么你把所有的产品都拉进来,最后trim它们(只有在每个商店的产品不是太多的情况下才可取):
$shops = Shop::with(['products' => function($subQuery) {
$subQuery
->with('pictures') //Running in scope of product, also load each product's pictures
->orderBy('created_at', 'desc');
}])
->get();
foreach ($shops as $shop) {
$shop->setRelation('products', $shop->products->take(3));
}
注意:
您将加载链接到您加载的商店的每个产品。你可能会遇到内存问题。
- 只拿你需要的,但引入了一个 n+1 查询问题(只建议少量
$shops
:
$shops = Shop::get();
foreach ($shops as $shop) {
$shop->load([
'products' => function($query) {
$query
->orderBy('created_at', 'desc')
->limit(3)
->get();
}]);
}
注意:
N+1个查询问题:你是对每个店铺执行一个新的查询,所以如果你有一百万个店铺,那就是一百万个额外的查询。
编辑:(回答评论问题)
问:如何根据最新产品 created_at 字段对 $shops 进行排序?
$sortedShops = $shops->sortBy(function ($shop, $key) {
return $shop->products->first()->created_at;
})->values()->all();
sortBy
在集合上被调用(不是查询)。它允许您遍历每个元素(在本例中为商店)并使用每个对象。请注意,如果您没有链接到商店的产品,此功能将失败。
最后的 ->values()->all()
确保当您将商店转换为 json 时,您将创建一个数组,而不是 js 中的对象。
来源:
https://laravel.com/docs/7.x/collections#method-sortby
编辑:(删除原始答案,因为它不起作用)
- 之前的答案无效,因为
limit(3)
将限制加载的产品总量,而不是每个商店 3 个产品(我的错)。
我有3个模型 店铺型号,商品型号,图片型号
我想检索包含最后 3 个产品型号及其图片的商店集合,并根据最新产品对我的集合进行排序。
我在 laravel 6 中尝试了 leftjoint
和 joint
来对结果进行排序,但我得到了所有商店的产品(我只需要最后 3 个产品每家商店),
当我使用关节时,我无法检索产品图片
我也尝试过 laravel 中的“with
”方法,我无法根据 product.creatred
_at 对结果进行排序,而且我也通过这种方法获得了所有相关产品。(正如我提到的,我需要最后 3 个产品)
class Shop extends Model
{
public function products()
{
return $this->hasMany('App\Product');
}
}
class Product extends Model
{
public function shop()
{
return $this->belongsTo('App\Shop');
}
public function pictures()
{
return $this->morphMany('App\hPicture', 'pictureable');
}
}
Shop::select('shops.*', 'products.id', 'products.shop_id', 'products.name as pname', 'products.user_id', 'products.code', 'products.price')
->with(['pictures', 'products.pictures'])
->leftjoin('products', function ($leftJoin) {
$leftJoin->on('shops.id', '=', 'products.shop_id');
});
$dataList = $dataList->orderBy($field, $order);
$dataList = $dataList->paginate(5)->appends(['sortField' => $field, 'sortOrder' => $order]);
产品和商店模型的 table 布局是:
Schema::create('shops', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('slug');
$table->string('phone')->nullable();
$table->string('address')->nullable();
$table->timestamps();
$table->string('description')->nullable();
$table->uuid('uuid');
});
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->integer('shop_id')->unsigned();
$table->foreign('shop_id')->references('id')->on('shops');
$table->string('name');
$table->string('code');
$table->string('slug');
$table->integer('price');
$table->uuid('uuid');
$table->timestamps();
});
只有两种解决方法:
- 要么你把所有的产品都拉进来,最后trim它们(只有在每个商店的产品不是太多的情况下才可取):
$shops = Shop::with(['products' => function($subQuery) {
$subQuery
->with('pictures') //Running in scope of product, also load each product's pictures
->orderBy('created_at', 'desc');
}])
->get();
foreach ($shops as $shop) {
$shop->setRelation('products', $shop->products->take(3));
}
注意:
您将加载链接到您加载的商店的每个产品。你可能会遇到内存问题。
- 只拿你需要的,但引入了一个 n+1 查询问题(只建议少量
$shops
:
$shops = Shop::get();
foreach ($shops as $shop) {
$shop->load([
'products' => function($query) {
$query
->orderBy('created_at', 'desc')
->limit(3)
->get();
}]);
}
注意:
N+1个查询问题:你是对每个店铺执行一个新的查询,所以如果你有一百万个店铺,那就是一百万个额外的查询。
编辑:(回答评论问题)
问:如何根据最新产品 created_at 字段对 $shops 进行排序?
$sortedShops = $shops->sortBy(function ($shop, $key) {
return $shop->products->first()->created_at;
})->values()->all();
sortBy
在集合上被调用(不是查询)。它允许您遍历每个元素(在本例中为商店)并使用每个对象。请注意,如果您没有链接到商店的产品,此功能将失败。
最后的 ->values()->all()
确保当您将商店转换为 json 时,您将创建一个数组,而不是 js 中的对象。
来源: https://laravel.com/docs/7.x/collections#method-sortby
编辑:(删除原始答案,因为它不起作用)
- 之前的答案无效,因为
limit(3)
将限制加载的产品总量,而不是每个商店 3 个产品(我的错)。