Laravelで外部キーが複数ある場合のリレーション Composhipsを使って多対多のデータを取得する方法
はじめに
LaravelのEloquentは、リレーションを簡単に扱うことができます。しかし、外部キーが複数あるリレーションになると、Laravelの標準機能では使うことができません。
そんなときに便利なのが、Composhipsというパッケージです。
本記事では、Composhipsを使って多対多の外部キーが複数あるリレーションをどのように実装するかを紹介します。
複合キー(Composite Key)とは?
複合キーとは、複数のカラムを組み合わせて1つのキー(主キーや外部キー)として扱う設計のことです。
例えば、「user_id と store_id の両方が一致するデータを紐づけたい」といったケースで使われます。
LaravelのEloquentでは、通常1つのキーを使ってリレーションを定義しますが、複合キーのように2つ以上のカラムをキーにする場合、標準のリレーション定義では対応できません。
そのため、複合キーに対応したリレーションを使いたい場合は、Composhipsのような外部パッケージを導入する必要があります。
Composhipsが必要なケース
LaravelのbelongsTo, hasMany, belongsToManyなどのリレーションは、通常は単一キーで結びつけます。しかし、以下のようなケースでは、複合キーが必要になります。
例えば、user_idとcompany_idを組み合わせてリレーションを張るようなケースです
users
| Column | Description | 
|---|---|
| id | ユーザーID(PK) | 
| company_id | 所属会社ID(FK) | 
projects
| Column | Description | 
|---|---|
| id | プロジェクトID(PK) | 
| company_id | 所属会社ID(FK) | 
project_user
| Column | Description | 
|---|---|
| user_id | ユーザーID(users.id への FK) | 
| project_id | プロジェクトID(projects.id への FK) | 
| company_id | 所属会社ID(users / projects どちらにも対応可能) | 
リレーション説明
- 
usersとprojectsは多対多の関係- 中間テーブル 
project_userを通じて関連付け 
 - 中間テーブル 
 - 
users.company_id=projects.company_id(同じ会社内のユーザーとプロジェクトが紐づく) - 
project_userはuser_idとproject_idの複合キーで構成 
このような構造では、単一キーでは意図しないデータが結びついてしまうため、複合キーでのマッチングが必要です。
導入手順
Composhipsのインストール
まずは、パッケージをインストールします。
composer require topclaudy/compoships
インストール後は、特別な設定は不要で、リレーション定義に使うだけです。
モデルの準備
Composhipsを使いたいモデルにTraitを追加します。
use Awobaz\Compoships\Compoships;
class User extends Model
{
    use Compoships;
    public function projects()
    {
        return $this->belongsToMany(Project::class, 'project_user', ['user_id', 'company_id'], ['project_id', 'company_id']);
    }
}
同様に、ProjectモデルにもComposhipsを記載します。
use Awobaz\Compoships\Compoships;
class Project extends Model
{
    use Compoships;
    public function users()
    {
        return $this->belongsToMany(User::class, 'project_user', ['project_id', 'company_id'], ['user_id', 'company_id']);
    }
}
データ取得例
上記のリレーション定義ができれば、あとは通常のリレーションと同じように使えます。
$projects = User::find(1)->projects;
foreach ($projects as $project) {
    echo $project->name;
}
内部的には、user_idとcompany_idの両方を使ってproject_userテーブルを結合してくれます。
まとめ
LaravelのEloquentは標準では複合キーに非対応となっています。
ただ、Composhipsを使うことで複合キーでのリレーションが可能になります。
複合キーの設計は一見面倒に感じるかもしれませんが、Composhipsを導入すればLaravelでも扱えるようになります。
複雑なリレーションが必要なプロジェクトでは、ぜひ一度導入を検討してみてください!
Discussion