Laravel 【多対多】 カウントと並び替え


目的


  • 注目タグなるものを作成したい

  • 商品に登録されてる数の多いものを注目タグとしたい

  • そしてあわよくば登録数の多い順に表示させたい


最初に


  • 商品一覧と、それに紐づくタグを表示させるページを作成する

  • ひとつの商品に、複数のタグを登録できるものとする


    • 商品(items)テーブル、タグ(tags)テーブル、中間テーブル(item_tag)を作成・使用

    • 商品(items)モデル、タグ(tags)モデルを作成・使用




DB

items
tags
item_tag

id
id
id

name
name
item_id

display(表示/非表示)
created_at
tag_id

created_at
updated_at

updated_at


Model


Item.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Item extends Model
{
public function tag(){
return $this->belongsToMany('App\Tag', 'item_tag', 'item_id', 'tag_id');
}
}



Tag.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Tag extends Model
{
public function item(){
return $this->belongsToMany('App\Item', 'item_tag', 'tag_id', 'item_id');
}

}



実装


IndexController.php

<?php

namespace App\Http\Controllers\Index;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Item;
use App\Tag;

class TopController extends Controller
{
public function index()
{
$items = Item::where('display', 1)->get();//下記1.
$attentionTag = Tag::withCount('item')//下記2.
->orderBy('item_count', 'desc')//下記3.
->whereHas('item', function ($query) {//下記4.
$query->where('display', 1);//表示されているアイテム
});
->get();

return view('index')->with(compact('items', 'attentionTag'));
}
}


1. display(表示/非表示)が1(表示)のデータをすべて取得

2. withCountメソッドでリレーションの数を取得

3. 回数の多い順に並び替……?'item_count'なんてカラムあったっけ…?

Laravel 5.7 Eloquent:リレーションによると

リレーション結果の件数を実際にレコードを読み込むことなく知りたい場合は、withCountメソッドを使います。件数は結果のモデルの{リレーション名}_countカラムに格納されます。

便利…{リレーション名}_countカラムをテーブルに作成していなくてもよいのだ…

ここにたどり着く途中、中身をみたけど'item _count'っていうのちゃんとあったの…しゅごい…

4.whereHasメソッドでリレーションの制約にカスタマイズした制約を追加


で、できた…

これで商品に登録されている回数の多い順にタグを取得できました。


おまけ

もっと条件が多くなる、そして同じ条件を何度も使う場合…

なが~~~い条件文をコピペペタペタしてたら見栄えが悪い…;

ということから、モデルにまとめることを覚えました。

コントローラーが劇的に短くなる…!!!!!


IndexController.php

<?php

namespace App\Http\Controllers\Index;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Item;
use App\Tag;

class TopController extends Controller
{
public function index()
{
$attentionTag = Tag::getAttentionTag()->get();

return view('index')->with(compact('items', 'attentionTag'));
}
}



Tag.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Tag extends Model
{
public function item(){
return $this->belongsToMany('App\Item', 'item_tag', 'tag_id', 'item_id');
}

public static function getAttentionTag()
{
return Tag::withCount('item')->orderBy('item_count', 'desc')
->whereHas('item', function ($query) {
$query->where('display', 1);
});
}
}


う~んしゅごい…

以上です。