EloquentのリレーションをER図と一緒に確認

Tag:

Eloquentのリレーションを活用することで、読みやすいコードにすることができます。

ここでは、下記リンク先の内容をもとにリレーションの定義方法など確認していきます。
https://readouble.com/laravel/5.4/ja/eloquent-relationships.html

リンク先で説明に利用されているデータモデルをER図で表すと、だいたい次のようになるかと思います。

では、各リレーションについてみてみます。

リレーションの定義

1対1(hasOne belongsTo)
userモデルとphoneモデルの関係(参考ページ)

ユーザーに紐づく電話と、電話を所有するユーザーの関係を定義します。

namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}
namespace App;
use Illuminate\Database\Eloquent\Model;
class Phone extends Model
{
    public function user()
    {
        return $this->belongsTo('App\User');
    }
}
1対多(hasMany belongsTo)
userモデルとpostモデルの関係(参考ページ)

ユーザーが複数の投稿を持つ関係を定義します。

namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
    public function posts()
    {
        return $this->hasMany('App\Post');
    }
}
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
    public function user()
    {
        return $this->belongsTo('App\User');
    }
}
多対多(belongsToMany belongsToMany)
userモデルとroleモデルの関係(参考ページ)

namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
    public function roles()
    {
        return $this->belongsToMany('App\Role')
            ->withPivot('column1', 'column2')
            ->withTimestamps();
    }
}
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
    public function users()
    {
        return $this->belongsToMany('App\User')
            ->withPivot('column1', 'column2')
            ->withTimestamps();
    }
}

withPivot,withTimestampsを使っています。関連モデルを取得する際に、中間テーブルの値も取得したい場合に指定が必要です。

中間テーブルのカラムの値によって条件をつけたい場合、wherePivotメソッド、wherePivotInメソッドが使えます。

namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
    public function users()
    {
        return $this->belongsToMany('App\User')
            ->withPivot('column1', 'column2')
            ->withTimestamps();
    }

    public function xxxUsers()
    {
        return $this->users()
            ->wherePivot('column1', 'xxx')
            ->wherePivotIn('column2', ['xxx', 'yyy']);
    }
}
〜経由の1対多(hasManyThrough)
countryモデルとuserモデルとpostモデルの関係(参考ページ)

「国」と「投稿」の関係を、「ユーザー」を経由した形で定義します。

namespace App;
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
    public function posts()
    {
        return $this->hasManyThrough('App\Post', 'App\User');
    }
}
1対多のポリモーフィック(morphTo morphMany)
[postモデルとvideoモデル]と[commentモデル]の関係(参考ページ)

namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
    public function comments()
    {
        return $this->morphMany('App\Comment', 'commentable');
    }
}
namespace App;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
    public function comments()
    {
        return $this->morphMany('App\Comment', 'commentable');
    }
}
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
    public function commentable()
    {
        return $this->morphTo();
    }
}
多対多のポリモーフィック(morphToMany morphedByMany)
[postモデルとvideoモデル]と[tagモデル]の関係(参考ページ)

namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
    public function tags()
    {
        return $this->morphToMany('App\Tag', 'taggable');
    }
}
namespace App;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
    public function tags()
    {
        return $this->morphToMany('App\Tag', 'taggable');
    }
}

namespace App;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model
{
    public function posts()
    {
        return $this->morphedByMany('App\Post', 'taggable');
    }

    public function videos()
    {
        return $this->morphedByMany('App\Video', 'taggable');
    }
}

リレーションを使ったモデルの取得

// 動的プロパティで「関連モデル」を取得
//(動的プロパティは「__getマジックメソッド」で実装されている)
$phone = User::find(1)->phone;


// リレーションメソッドで「関連モデル」を取得(追加制約を加える場合)
$comments = App\Post::find(1)->comments()->where('title', 'foo')->first();


// 中間テーブルのカラム取得
$user = App\User::find(1);
foreach ($user->roles as $role) {
    echo $role->pivot->created_at;
}


// 「関連モデル」が存在する「自モデル」を取得
// (存在しない → doesntHaveメソッド)
$posts = Post::has('comments')->get();


// 「関連モデル」が特定条件で存在する「自モデル」を取得
// (存在しない → whereDoesntHaveメソッド)
$posts = Post::whereHas('comments', function ($query) {
    $query->where('content', 'like', 'foo%');
})->get();


// 「関連モデル」の総数を取得
$posts = App\Post::withCount('comments')->get();
foreach ($posts as $post) {
    echo $post->comments_count;
}


// Eagerローディング
$books = App\Book::with('author')->get();


// Eagerローディング(特定条件の関連モデル)
$users = App\User::with(['posts' => function ($query) {
    $query->where('title', 'like', '%first%');
}])->get();


// 遅延Eagerローディング
$books = App\Book::all();
if ($someCondition) {
    $books->load('author', 'publisher');
} 

関連モデルの挿入/更新

親モデルが新たに所有する子モデルを追加

save(Eloquentモデルを受け取り追加)
saveMany(複数のEloquentモデルを受け取り追加)
create(配列で受け取り追加)

親モデルが子モデルの外部キーを更新

associate/dissociate

多対多(中間テーブルへの操作)

attach/detach
sync
toggle
updateExistingPivot

スポンサーリンク