【Laravel】リレーションされた要素数を取得する

プロジェクトが複雑になればなるほど、データベースを用いたやりとりの難易度も上がっていきます。
複数のテーブルをリレーションさせる事はよくあります。
Laravelではリレーション周りの関数も色々用意されており、複雑なリレーションにも柔軟に対応できます。

さて、
今回はリレーションされた要素数を扱いたい場合の方法について記載します。

実装仕様

例えば、ブログサービスを作るとします。
仕様上、ブログの記事にはカテゴリが1つ付与されています。
今回実現したいことは、カテゴリ毎の登録データ数を取得したいです。

テーブル構成

次のようなテーブル構成だったとします。

  • 記事(entries)
  • 記事カテゴリ(entry_categories)

f:id:nakahashi_h:20201116124624p:plain

記事カテゴリと記事をリレーションさせて、リレーションからデータ件数を取得できるようにします。
また、記事によっては公開/非公開のものが分かれているので、公開中の件数だけ取得できるようにします。

モデルのリレーション設定

記事モデル

記事用のEntryモデルを次のように設定します。
今回の要件では、リレーションの設定は不要ですので省略しても問題ありません。

<?php
// Entry.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Entry extends Model
{
    /**
     * EntryCategoryのリレーション
     */
    public function entryCategory() {
        return $this->hasOne(EntryCategory::class);
    }
}

記事カテゴリモデル

カテゴリ用のEntryCategoryモデルを次のように設定します。
公開状況(ER図のpublished)が有効なものだけ取得したいので、whereを追加します。

<?php
// EntryCategory.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class EntryCategory extends Model
{
    /**
     * Entryのリレーション
     */
    public function entries() {
        return $this->hasMany(Entry::class);
    }

    /**
     * 公開中のブログのみ
     */
    public function entriesIsPublised() {
        return $this->entries()
            ->where('published', 1);
    }

}

リレーションされた要素数を取得する

readouble.com

Laravelが用意しているリレーションはwithを使用する方法があります。
チェーンメソッドでリレーションされたデータベースに紐づくモデルを呼び出せます。

リレーションされた要素数だけ取得したい場合はwithCountを使用します。
これを使用する事で、リレーション元のattributeに(リレーション名のスネークケース)_countと言う要素が追加されます。

例えば、Controllerでカテゴリリレーションされた記事の件数を取得する場合は次のようになります。

<?php

namespace App\Http\Controllers;

use App\Entry;
use App\EntryCategory;

class EntryController extends Controller
{
    public function index()
    {
        // withCountにリレーションの関数名を指定します
        $entryCategories = EntryCategory::withCount('entriesIsPublised')
            ->get();

        foreach($entryCategories as $entryCategory) {
            // キャメルケースが、スネークケースに変換されますので、次のような呼び出し方になります。
            dump($entry->entries_is_publised_count);
        }
    }
}

©︎2017-2018 WebSandBag