Как получить ограниченное количество связанных моделей и отсортировать коллекцию по связанной модели в laravel? - PullRequest
0 голосов
/ 07 апреля 2020

У меня есть 3 модели Магазин модель, Продукт модель, Изображение модель

Я хочу получить коллекцию магазинов с последними 3 Модель товара с их фотографиями и сортировка моей коллекции на основе новейшего продукта. Я попытался leftjoint и joint в laravel 6 , чтобы иметь возможность отсортировать результат, но я получаю продукт всех магазинов (мне нужны только последние 3 продукта для каждого магазина),

Когда я использую Joint, я не могу получить изображения продуктов. Я также попробовал метод «with» в laravel, Я не смог отсортировать результат по 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 Ответ

0 голосов
/ 07 апреля 2020

Существует только 2 способа решения этой проблемы:

  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 проблема запроса: вы выполняете новый запрос для каждого магазина, поэтому, если у вас есть миллион магазинов, это будет миллион дополнительных запросов.

РЕДАКТИРОВАТЬ: (отвечая на вопрос с комментариями)

Q : Как я могу отсортировать $ магазины по их последнему продукту в поле create_at?

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

sortBy вызывается для коллекции (не uery). Это позволяет вам go по каждому элементу (в этом "Делайте покупки в магазинах") и используйте каждый объект. Обратите внимание, что эта функция не будет работать, если у вас нет товаров, связанных с магазином.

* В конце ->values()->all() убедитесь, что при преобразовании магазинов в json, вы создадите массив, а не объект в js.

Источник: https://laravel.com/docs/7.x/collections#method -sortby

РЕДАКТИРОВАТЬ: (удаляется исходный ответ как не работает)

  • Предыдущий ответ не работает, потому что limit(3) ограничит общее количество d загруженных товаров вместо 3 товаров в магазине (мой плохой).
...