【Laravel】HasManyThroughを使いやすくする

Eloquent:リレーション 6.x Laravel#Has Many Through
 LaravelではEloquent(データベース中のテーブルをマッピングしたモデルクラス)のリレーションを表現する機能が備わっています。HasManyThroughはその中の一つで中間テーブルをはさんだ多対多の関係を表現できます。問題のHasManyThroughは次の様に記述できます。

### DB
countries
    id - integer
    name - string

users
    id - integer
    country_id - integer
    name - string

posts
    id - integer
    user_id - integer
    title - string
class Country extends Model
{
    public function posts()
    {
        // ()ないは主キーと
        return $this->hasManyThrough(
            'App\Post',// つなげる先のテーブルクラス
            'App\User',// 中間テーブルクラス
            'country_id', // 中間テーブルの外部キー
            'user_id', // つなげる先のテーブルの外部キー
            'id', // 元テーブルのローカルキー
            'id' // つなげる先のテーブルのローカルキー
        );
    }
}
## 別ファイル
$country = Country::first();
// プロパティとしていきなり呼べるようになる
$posts = $ocuntry->posts; // Collection<Post>

 便利なのですが危険なくらい混乱します。というのも引数が6個もある上、どこかの主キーの言い換えが4個占めていて大変紛らわしいためです。よく使う状況に合わせて何かしらメソッドを作った方がいいです。例えば自分がよく使うのは次です。

class BaseEloquent extends Model
{
    /**
     * @param  string         $tgtClass                       関係を結ぶテーブルクラス
     * @param  string         $throughClass                   通る中間テーブルクラス
     * @param  string         $throughHasThisPrimaryKeyColumn 中間テーブルの持つ$thisの主キーを指すカラム
     * @param  string         $throughHasTgtPrimaryKeyColumn  中間テーブルの持つ関係先の主キーを指すカラム
     * @return HasManyThrough
     */
    public function hasManyThroughWrapper(
        string $tgtClass,
        string $throughClass,
        string $throughHasThisPrimaryKeyColumn,
        string $throughHasTgtPrimaryKeyColumn
    ): HasManyThrough {
        return $this->hasManyThrough(
            $tgtClass,
            $throughClass,
            $throughHasThisPrimaryKeyColumn,
            (new $tgtClass())->getKeyName(),
            $this->getKeyName(),
            $throughHasTgtPrimaryKeyColumn,
        );
    }
}

 これは次の様な二つの1対多でつながった中間テーブルを対象にしたメソッドです。
 
 上記PUML図のテーブル定義を対象にすると次の様に定義できます。

class User extends BaseEloquent
{
    /**
     * コメントした投稿を取得
     * @return HasManyThrough
     */
    public function commentedPosts(): HasManyThrough
    {
        return $this->hasManyThroughWrapper(
            Post::class, 
            Comment::class,
            'user_id', // 引数のカラム名は中間テーブル(commentsテーブル)の持つカラム名だけで済む
            'post_id'
        );
    }
}

 Eloquent には主キー名を取得するメソッドである getKeyName があります。これを使って引数を減らせるのでうまい具合に言い換えメソッドを作れます。

>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG