如何在laravel中检索有限数量的相关模型并按相关模型对集合进行排序?

Posted

技术标签:

【中文标题】如何在laravel中检索有限数量的相关模型并按相关模型对集合进行排序?【英文标题】:how to retrieve limited number of related model and sort collection by related model in laravel? 【发布时间】:2020-07-19 12:21:50 【问题描述】:

我有 3 个模型 店铺模型、产品模型、图片模型

我想检索包含最近 3 个产品型号及其图片的商店集合,并根据最新产品对我的集合进行排序。 我在 laravel 6 中尝试了 leftjointjoint 以便能够对结果进行排序,但我得到了所有商店的产品(我只需要每个商店的最后 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]);

产品和店铺模型的表格布局为:

 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();
        );

【问题讨论】:

【参考方案1】:

只有两种方法可以解决这个问题:

    要么你把所有产品都拉进去,然后最后修剪它们(仅当每家商店的产品不多时才建议这样做):
$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 查询问题:您正在为每个商店执行一个新查询,因此如果您有 100 万个商店,就会有 100 万个额外查询。

编辑:(回答评论问题)

问:如何根据 $shops 的最新产品 created_at 字段对它们进行排序?

$sortedShops = $shops->sortBy(function ($shop, $key) 
    return $shop->products->first()->created_at;
)->values()->all();

sortBy 在集合上被调用(不是 uery)。它允许您遍历每个元素(在本例中为商店)并使用每个对象。请注意,如果您没有与商店关联的产品,此功能将失效。

末尾的->values()->all() 确保当您将商店转换为 json 时,您将创建一个数组,而不是 js 中的对象。

来源: https://laravel.com/docs/7.x/collections#method-sortby

编辑:(删除原来的答案,因为它不起作用)

上一个答案不起作用,因为limit(3) 将限制加载的产品总数,而不是每个商店 3 个产品(我的错)。

【讨论】:

如果它有效,请不要忘记接受答案;)如果无效,请告诉我 事实上,我认为我需要每个商店的最后一个相关产品的最新 created_at 字段才能根据该数据进行排序 取决于您的表格布局。您能发布哪些列在哪些表格中吗?然后我可以试着和你一起找出更好的答案:) 我编辑问题。我希望 $shops 按其最新产品 created_at 归档 @Mostafa,我更新了答案,让我知道它是否有效:)

以上是关于如何在laravel中检索有限数量的相关模型并按相关模型对集合进行排序?的主要内容,如果未能解决你的问题,请参考以下文章

列表模型如何按 DOWN-VOTES 的数量排序到相关模型 - laravel,雄辩

我无法从 Laravel 6 中的相关表(模型)中检索数据

Laravel Eloquent - 从相关模型中检索特定列

Laravel5.3 - 创建一定数量的相关模型 - 创建模型时

如何在 Laravel 中检索链接到多对多关系的模型?

Laravel:从数据库中检索模型