Категории типов сообщений Laravel

Мне нужен массив с категориями, в каждой категории по 4 поста: массив с двумя постами с type_1 и массив с двумя постами с type_2. Без типа_3.

Дисплей должен выглядеть так (без использования лезвия):

category name_1

    post type_1 name
    post type_1 name

    post type_2 name
    post type_2 name

category name_2

    post type_1 name
    post type_1 name

    post type_2 name
    post type_2 name

post_table:

$table->enum('type', ['type_1', 'type_2', 'type_3'])->default('type_1');
$table->unsignedBigInteger('category_id')->index();
$table->foreign('category_id')->references('id')->on('categories');

категории_таблица:

$table->string('name');

Модель категории:

return $this->hasMany('App\Models\Post');

Почтовая модель:

return $this->belongsTo('App\Models\Category');

Я пытался:

$categories = ['category_1', 'category_2'];

foreach ($categories as $cat) {
    $postsType1[$cat->name] = $cat->posts()->where('type', 'type_1')->latest()->limit(2)->get();
    $postsType2[$cat->name] = $cat->posts()->where('type', 'type_2')->latest()->limit(2)->get();
}

Но это два отдельных массива в категориях. Мне нужен один массив категорий с двумя сообщениями каждого типа.

🤔 А знаете ли вы, что...
С PHP можно создавать красивые и функциональные графики с помощью библиотеки GD.


767
1

Ответ:

Решено

Вы можете использовать коллекцию groupBy() с несколькими условиями

$posts = Post::query()
    ->latest()
    ->get()
    ->groupBy([
        function($post) {
            return $post->category->name;
        },
        function($post) {
            return $post->type;
        },
    ])
    ->map(function($records, $category) {
      return $records->sortKeys()->map(function($rows, $type){
          return $rows->take(2)->all();
      });
    })
    ->all();

И если вы хотите просто сгруппировать по названию категории и отсортировать по типу сообщения

$posts = Post::get()
    ->group(function($post){
        return $post->category->name;
    })
    -> map(function($value, $key){
        return $value->sortBy('type')->sortByDesc('created_at')->take(2);
    })
    ->all();

ОБНОВЛЯТЬ Чтобы избежать получения всех записей Post без каких-либо ограничений, другим вариантом будет:

  • создайте составной индекс в таблице сообщений для повышения производительности запросов
//in migration file for the posts table
$table->index(['type', 'category_id']);
  • определить 3 отношения в модели категории
class Category extends Model
{

    public function type1News()
    {
        return $this->hasMany(Post::class)->where('type', 'type_1')->latest();
    }

    public function type2News()
    {
        return $this->hasMany(Post::class)->where('type', 'type_2')->latest();
    }

    public function type3News()
    {
        return $this->hasMany(Post::class)->where('type', 'type_3')->latest();
    }
}

Затем, чтобы получить данные

 $categories = Cache::remember('categoriesWithLatestNews', 3600, function() {
    $cats = Category::get();

    $cats->each->load([
        'type1News' => function($query){
            return $query->take(2);
        },
        'type2News' => function($query){
            return $query->take(2);
        },
        'type3News' => function($query){
            return $query->take(2);
        }
    ]);

    return $cats;
});

С 1 миллионом записей в таблице сообщений время отклика впервые составило 2,75 секунды. (Без составного индекса время отклика превышает 120 с.)

Затем после того, как он кэшируется в течение указанных секунд, поэтому время отклика находится в мс.

Хотя этот подход требует нескольких запросов к базе данных, всего 31 запрос (10 записей категорий): 1 для получения всех категорий, а затем 3 для каждой категории для получения отношений.