Laravel 5.1 Eloquent:リレーション

イントロダクション

データベーステーブルは大抵の場合他のものと関連しています。たとえばブログポストは多くのコメントを持つか、それを投稿したユーザーと関連しています。Eloquentはそうしたリレーションを簡単に管理し操作できるようにするとともに、様々なタイプのリレーションをサポートしています。

リレーションの定義

EloquentのリレーションとはEloquentモデルクラスの関数として定義します。Eloquentモデル自身と同様にリレーションはパワフルなクエリービルダーとして動作しますので、関数として定義しているリレーションはパワフルなメソッドのチェーンとクエリー能力を提供できるのです。例を見てください。

$user->posts()->where('active', 1)->get();

リレーションの使い方へ進む前に、リレーションの各タイプをどのように定義するかを学びましょう。

1対1

1対1関係が基本です。たとえばUserモデルはPhoneモデル一つと関係しているとしましょう。このリレーションを定義するには、phoneメソッドをUserモデルに世知します。phoneメソッドはベースのEloquentモデルクラスのhasOneメソッドを結果として返す必要があります。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザーに関連する電話レコードを取得
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

hasOneメソッドの最初の引数は関係するモデルの名前です。リレーションが定義できたらEloquentの動的プロパティを使って、関係したモデルを取得できます。動的プロパティによりモデル上のプロパティのようにリレーション関数にアクセスできます。

$phone = User::find(1)->phone;

Eloquentはリレーションの外部キーがモデル名に基づいていると仮定します。この場合自動的にPhoneモデルはuser_id外部キーを持っていると仮定します。この規約をオーバーライドしたければ、hasOneメソッドの第2引数を指定してください。

return $this->hasOne('App\Phone', 'foreign_key');

Eloquentは親のidカラムと一致する外部キーの値を持っていると仮定します。言い換えればEloquentはユーザーのidカラムの値をPhoneレコードのuser_idカラムに存在しないか探します。リレーションで他のidを使いたければ、hadOneメソッドの第3引数でカスタムキーを指定してください。

return $this->hasOne('App\Phone', 'foreign_key', 'local_key');

逆の関係の定義

これでUserからPhoneモデルへアクセスできるようになりました。今度はPhoneモデルからそれを所有しているUserへアクセスするリレーションを定義しましょう。hasOneの逆のリレーションを定義するには、belongsToメソッドを使います。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Phone extends Model
{
    /**
     * この電話を所有するUserを取得
     */
    public function user()
    {
        return $this->belongsTo('App\User');
    }
}

上の例でEloquentはPhoneモデルのuser_idに一致するidを持つUserモデルを見つけようとします。Eloquentはリレーションメソッド名に_idのサフィックスを付けた名前をデフォルトの外部キー名とします。しかしPhoneモデルの外部キーがuser_idでなければ、belongsToメソッドの第2引数にカスタムキー名を渡してください。

/**
 * この電話を所有するUserを取得
 */
public function user()
{
    return $this->belongsTo('App\User', 'foreign_key');
}

親のモデルの主キーがidでないか子のモデルと違ったカラムで紐付けたい場合は、親テーブルのカスタムキー名をbelongsToメソッドの第3引数に渡してください。

/**
 * この電話を所有するUserを取得
 */
public function user()
{
    return $this->belongsTo('App\User', 'foreign_key', 'other_key');
}

1対多

「1対多」リレーションは一つのモデルが他の多くのモデルを所有する関係を定義するために使います。ブログポストが多くのコメントを持つのが一例です。他のEloquentリレーションと同様に1対多リレーションはEloquentモデルの関数として定義します。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    /**
     * ブログポストのコメントを取得
     */
    public function comments()
    {
        return $this->hasMany('App\Comment');
    }
}

EloquentはCommentモデルに対する外部キーを自動的に決めることを心に留めてください。規約によりEloquentは自分自身のモデル名の「スネークケース」に_idのサフィックスをつけた名前と想定します。ですから今回の例でEloquentはCommentモデルの外部キーをpost_idであると想定します。

リレーションを定義したらcommentsプロパティによりコメントのコレクションへアクセスできます。Eloquentは「動的プロパティ」を提供しているのでモデルのプロパティとして定義したリレーション関数へアクセスできることを覚えておきましょう。

$comments = App\Post::find(1)->comments;

foreach ($comments as $comment) {
    //
}

もちろん全リレーションはクエリービルダーとしても働きますから、commentsメソッドを呼び出すときにどのコメントを取得するのかという制約を追加でき、クエリーに条件を続けてチェーンでつなげます。

$comments = App\Post::find(1)->comments()->where('title', 'foo')->first();

hasOneメソッドと同様に、外部キーとローカルキーをhasManyメソッドに追加の引数として渡すことでオーバーライドできます。

return $this->hasMany('App\Comment', 'foreign_key');

return $this->hasMany('App\Comment', 'foreign_key', 'local_key');

逆のリレーションを定義

これでポストの全コメントにアクセスできます。今度はコメントから親のポストへアクセスできるようにしましょう。hasManyリレーションの逆を定義するには子のモデルでbelongsToメソッドによりリレーション関数を定義します。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    /**
     * このコメントを所有するポストを取得
     */
    public function post()
    {
        return $this->belongsTo('App\Post');
    }
}

リレーションが定義できたらCommentPostモデルをpost動的プロパティにより取得しましょう。

$comment = App\Comment::find(1);

echo $comment->post->title;

前例でEloquentはCommentモデルのpost_idと一致するidPostモデルを見つけようとします。Eloquentはリレーションメソッドの名前に_idのサフィックスをつけた名前をデフォルトの外部キーとします。しかしCommentモデルの外部キーがpost_idでなければ、belongsToメソッドの第2引数にカスタムキー名を指定してください。

/**
 * このコメントを所有するポストを取得
 */
public function post()
{
    return $this->belongsTo('App\Post', 'foreign_key');
}

親モデルが主キーにidを使っていない場合、もしくは子供のモデルに異なったカラムを紐つけたい場合は、belongsToメソッドの第3引数にテーブルのカラム名を指定してください。

/**
 * このコメントを所有するポストを取得
 */
public function post()
{
    return $this->belongsTo('App\Post', 'foreign_key', 'other_key');
}

多対多

多対多の関係はhasOnehasManyリレーションよりも多少複雑な関係です。このような関係として、ユーザ(user)が多くの役目(roles)を持ち、役目(role)も大勢のユーザー(users)に共有されるという例が挙げられます。たとえば多くのユーザーは"管理者"の役目を持っています。usersrolesrole_userの3テーブルがこの関係には必要です。role_userテーブルは関係するモデル名をアルファベット順に並べたもので、user_idrole_idを持つ必要があります。

多対多リレーションはベースのEloqunetクラスのbelongsToManyメソッド呼び出しを記述することで定義します。例としてUserモデルにrolesメソッドを定義してみましょう。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * userに所属する役割を取得
     */
    public function roles()
    {
        return $this->belongsToMany('App\Role');
    }
}

リレーションが定義できたら、roles動的プロパティを使いユーザーの役割にアクセスできます。

$user = App\User::find(1);

foreach ($user->roles as $role) {
    //
}

もちろん他のリレーションタイプと同様にリレーションを制約するクエリーをrolesに続けてチェーンすることができます。

$roles = App\User::find(1)->roles()->orderBy('name')->get();

前に述べたようにリレーションの結合テーブルの名前を決めるため、Eloquentは2つのモデル名をアルファベット順に結合します。しかしこの規約は自由にオーバーライドできます。belongsToManyメソッドの第2引数に渡してください。

return $this->belongsToMany('App\Role', 'user_roles');

結合テーブル名のカスタマイズに加えテーブルのキーカラム名をカスタマイズするには、belongsToManyメソッドに追加の引数を渡してください。第3引数はリレーションを定義しているモデルの外部キー名で、一方の第4引数には結合するモデルの外部キー名を渡します。

return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'role_id');

逆のリレーションを定義

多対多のリレーションの逆リレーションを定義するには、関連するモデルでもbelongsToManyを呼び出してください。引き続きユーザーと役割の例を続けますがRoleモデルでusersメソッドを定義してみましょう。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Role extends Model
{
    /**
     * 役割を所有するユーザー
     */
    public function users()
    {
        return $this->belongsToMany('App\User');
    }
}

ご覧の通り一方のUserと全く同じ定義のリレーションです。違いはApp\Userモデルを参照していことです。同じbelongsToManyメソッドを使っているのですから、通常のテーブル名、キーカスタマイズのオプションは逆の多対多リレーションを定義するときでも全て使用できます。

中間テーブルのカラム取得

既に学んだように、多対多リレーションの操作には中間テーブルが必要です。Eloquentこのテーブルを操作する便利な手段を用意しています。例としてUserオブジェクトが関連するRoleオブジェクトを持っているとしましょう。このリレーションへアクセスした後、モデルのpivot属性を使い中間テーブルにアクセスできます。

$user = App\User::find(1);

foreach ($user->roles as $role) {
    echo $role->pivot->created_at;
}

取得したそれぞれのRoleモデルは自動的にpivot属性と自動的に結合されます。この属性は中間テーブルを表すモデルを含んでおり、他のElouquentモデルと同様に使用できます。

デフォルトでモデルキーはpivotオブジェクト上のものを表しています。中間テーブルがその他の属性を持っている場合、リレーションを定義するときに指定できます。

return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');

もし中間テーブルのcreated_atupdated_atタイムスタンプを自動的に保守したい場合は、withTimestampsメソッドをリレーション定義に付けてください。

return $this->belongsToMany('App\Role')->withTimestamps();

Has Many Through

has many through(〜経由の多対多)リレーションは、仲介するテーブルを通して直接関連付けしていないテーブルへアクセスするための、便利な近道を提供します。たとえばCountryモデルはUsersモデルを経由して、多くのPostsを所有することでしょう。テーブルは以下のような構成になります。

countries
    id - integer
    name - string

users
    id - integer
    country_id - integer
    name - string

posts
    id - integer
    user_id - integer
    title - string

たとえpostsテーブルにcountry_idが存在しなくても、hasManyThroughリレーションではCountryのPostへ$country->postsによりアクセスできます。このクエリーを行うためにEloquentは仲介するusersテーブルのcountry_idを調べます。一致するユーザーIDが存在していたらpostsテーブルのクエリーに利用します。

ではリレーションのテーブル構造を理解したところで、Countryモデルを定義しましょう。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Country extends Model
{
    /**
     * この国の全ポストを取得
     */
    public function posts()
    {
        return $this->hasManyThrough('App\Post', 'App\User');
    }
}

hasManyThroughメソッドの第一引数は最終的にアクセスしたいモデル名で、第2引数は仲介するモデル名です。

リレーションのクエリー実行時では典型的なEloquentの外部キー規約が使用されます。リレーションのキーをカスタマイズしたい場合はhasManyThroughメソッドの第3引数と、第4引数を指定してください。第3引数は仲介モデルの外部キー名、第4引数は最終的なモデルの外部キー名です。

class Country extends Model
{
    public function posts()
    {
        return $this->hasManyThrough('App\Post', 'App\User', 'country_id', 'user_id');
    }
}

ポリモーフィック関係

テーブル構造

ポリモーフィックリレーションはあるモデルを一つの関係だけで、複数のモデルに所属させるものです。たとえばスタッフメンバーと商品の写真を保存するとしましょう。ポリモーフィックリレーションによりphotosテーブル一つを両方のシナリオで使用できます。最初にこのリレーションを構築するために必要な構造を確認してください。

staff
    id - integer
    name - string

products
    id - integer
    price - integer

photos
    id - integer
    path - string
    imageable_id - integer
    imageable_type - string

photosテーブル上の2つの重要なカラム、imageable_idimageable_typeに注目してください。imageable_idカラムは所有しているスタッフか製品のIDを持っています。一方のimageable_typeカラムは所収されているモデルのクラス名を持っています。imageable_typeimageableリレーションにアクセスされた時に返す、所有しているモデルの「タイプ」をORMが決定するためのカラムです。

モデル構造

次にこのリレーションを構築するために必要なモデル定義を見てみましょう。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Photo extends Model
{
    /**
     * 所有している全imageableモデルの取得
     */
    public function imageable()
    {
        return $this->morphTo();
    }
}

class Staff extends Model
{
    /**
     * 全スタッフメンバーの写真の取得
     */
    public function photos()
    {
        return $this->morphMany('App\Photo', 'imageable');
    }
}

class Product extends Model
{
    /**
     * 全商品の写真の取得
     */
    public function photos()
    {
        return $this->morphMany('App\Photo', 'imageable');
    }
}

ポリモーフィックリレーションの取得

データベーステーブルとモデルが定義できたらモデルを使いリレーションにアクセスできます。たとえばスタッフメンバーの全写真にアクセスするにはphotos動的プロパティーを使用するだけです。

$staff = App\Staff::find(1);

foreach ($staff->photos as $photo) {
    //
}

ポリモーフィックモデルからポリモーフィックリレーションの所有者をmorphToを呼び出すメソッド名にアクセスすることで取得できます。今回の例の場合Photoモデルのimageableメソッドです。動的プロパティーとしてメソッドにアクセスしてみましょう。

$photo = App\Photo::find(1);

$imageable = $photo->imageable;

Photoモデルのimageableリレーションは写真を所有しているモデルのタイプにより、StaffProductインスタンスを返します。

カスタムポリモーフィックリレーション

関連付けられたモデルのタイプを保存するため、デフォルトでLaravelははっきりと区別できるクラス名を使います。たとえば上記の例で、LikePostCommentに所属しているとすると、likable_typeはデフォルトでApp\PostApp\Commentのどちらかになるでしょう。しかし、データーベースはアプリケーションの内部構造と分離したいかと思います。その場合、リレーションの"morph map"を定義し、クラス名の代わりに使用する、各モデルに関連づいたテーブル名をEloquentへ指示することができます。

Relation::morphMap([
    App\Post::class,
    App\Comment::class,
]);

もしくは、各モデルに関連づいた、カスタム文字列を指定することもできます。

Relation::morphMap([
    'posts' => App\Post::class,
    'likes' => App\Like::class,
]);

morphMapAppServiceProviderの中で登録できます。要望に合わせて、独立したサービスプロバイダを作成することもできるでしょう。

ポリモーフィック関係の多対多

テーブル構造

伝統的なポリモーフィックリレーションに加え、「多対多」ポリモーフィックリレーションも指定することができます。たとえばブログのPostVideoモデルはTagモデルに対するポリモーフィックリレーションを共有できます。多対多ポリモーフィックリレーションを使うことで、ブログポストとビデオの両者に所有されている一意のタグのリストを取得できます。最初にテーブル構造を確認しましょう。

posts
    id - integer
    name - string

videos
    id - integer
    name - string

tags
    id - integer
    name - string

taggables
    tag_id - integer
    taggable_id - integer
    taggable_type - string

モデル構造

次にモデルにその関係を用意しましょう。PostVideoモデルは両方ともベースのEloquentクラスのmorphToManyメソッドを呼び出すtagsメソッドを持っています。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    /**
     * ポストに対する全タグを取得
     */
    public function tags()
    {
        return $this->morphToMany('App\Tag', 'taggable');
    }
}

逆のリレーションの定義

次にTagモデルで関係する各モデルに対するメソッドを定義する必要があります。ですからこの例の場合、postsメソッドとvideosメソッドを用意します。

<?php

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');
    }
}

リレーションの取得

データベーステーブルとモデルが定義できたら、モデルを使いリレーションにアクセスできます。たとえばポストに対する全タグへアクセスするには、単にtags動的プロパティーを使用するだけです。

$post = App\Post::find(1);

foreach ($post->tags as $tag) {
    //
}

さらにmorphedByManyを呼び出すメソッドの名前にアクセスし、ポリモーフィックモデルからポリモーフィックリレーションの所有者を取得することも可能です。この例の場合Tagモデルのpostsvideosメソッドです。では動的プロパティーとしてメソッドを呼び出しましょう。

$tag = App\Tag::find(1);

foreach ($tag->videos as $video) {
    //
}

リレーションのクエリー

Eloquentリレーションは全てメソッドとして定義され、リレーションのクエリーを実際に記述しなくてもメソッドを呼び出すことでそのリレーションのインスタンスを取得できます。付け加えるなら、全タイプのEloquentリレーションもクエリービルダーとしても動作し、データベースに対してSQLが最終的に実行される前にそのリレーションのクエリーをチェーンで続けて記述できます。

たとえばブログシステムで関連した多くのPostモデルを持つUserモデルを想像してください。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザーの全ポストの取得
     */
    public function posts()
    {
        return $this->hasMany('App\Post');
    }
}

次のようにpostsリレーションのクエリーに追加の制約を付け加えられます。

$user = App\User::find(1);

$user->posts()->where('active', 1)->get();

リレーションにもクエリービルダーメソッドが使用できる点に注目です!

リレーションメソッド v.s. 動的プロパティー

Eloquentリレーションクエリーに追加の制約を加える必要がなければ、シンプルにそのリレーションへプロパティーとしてアクセスできます。UserPostの例を続けるとして、ユーザーの全ポストには次のようにアクセスできます。

$user = App\User::find(1);

foreach ($user->posts as $post) {
    //
}

動的プロパティーは「遅延ロード」されます。つまり実際にアクセスされた時にだけそのリレーションのデータはロードされます。そのため開発者は多くの場合にEagerローディングを使い、モデルをロードした後にアクセスするリレーションを前もってロードしておきます。Eagerロードはモデルのリレーションをロードするため実行されるSQLクエリーを大幅に減らしてくれます。

存在するリレーションのクエリー

関連付けたモデルのレコードに基づいて、モデルのレコードに対するマッチングを絞り込みたい場合もあるでしょう。たとえば最低でも一つのコメントを持つ、全ブログポストを取得したい場合を考えてください。これを行うためにはhasメソッドを使用します。

// 最低1つのコメントを持つ全ポストの取得…
$posts = App\Post::has('comments')->get();

演算子と数を指定しクエリーをカスタマイズすることもできます。

// 3つ以上のコメントを持つ全ポストの取得…
$posts = Post::has('comments', '>=', 3)->get();

ネストしたhas文は「ドット」記法で組立てられます。たとえば最低一つのコメントと評価を持つ全ポストを取得する場合です。

// 最低1つのコメントと、それに対する評価を持つ全ポストの取得…
$posts = Post::has('comments.votes')->get();

もっと強力な機能がお望みならばhasの問い合わせに"where"で条件をつけるために、whereHasorWhereHasを利用して下さい。これらのメソッドによりリレーションの制約にカスタマイズした制約を追加できます。たとえばコメントの内容を調べることです。

// like foo%の制約に一致する最低1つのコメントを持つ全ポストの取得
$posts = Post::whereHas('comments', function ($query) {
    $query->where('content', 'like', 'foo%');
})->get();

Eagerローディング

Eloquentリレーションをプロパティーとしてアクセする場合、リレーションのデータは「遅延ロード」されます。つまりプロパティーにアクセスされるまで実際にリレーションのデータはロードされることはありません。しかし、Eloquentでは親モデルにクエリーする時点で「Eagerロード」できます。EagerローディングはN+1クエリー問題を軽減するために用意しています。たとえばBookモデルがAuthorモデルと関連していると考えてください。関係はこのように定義されます。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Book extends Model
{
    /**
     * この本を書いた著者を取得
     */
    public function author()
    {
        return $this->belongsTo('App\Author');
    }
}

では全書籍とその著者を取得しましょう。

$books = App\Book::all();

foreach ($books as $book) {
    echo $book->author->name;
}

このループではまず全ての本をテーブルから取得するために1クエリー実行され、それから著者をそれぞれの本について取得します。ですから25冊あるならば、このループで26クエリーが発生します。

ありがたいことにクエリーの数を徹底的に減らすためにEagerローディングを使うことができます。withメソッドを使い指定してください。

$books = App\Book::with('author')->get();

foreach ($books as $book) {
    echo $book->author->name;
}

この操作では2つだけしかクエリーが実行されません。

select * from books

select * from authors where id in (1, 2, 3, 4, 5, ...)

複数のリレーションに対するEagerローディング

一回の操作で異なった複数のリレーションをEagerローディングする必要がある場合もあります。その場合でも、ただwithメソッドに引数を追加で渡すだけです。

$books = App\Book::with('author', 'publisher')->get();

ネストしたEagerローディング

ネストしたリレーションをEagerロードする場合は「ドット」記法が使用できます。例としてEloquent文で全著者と著者個人のコンタクトも全部Eagerロードしてみましょう。

$books = App\Book::with('author.contacts')->get();

Eagerロードの制約

場合によりリレーションをEagerロードしたいが、Eagerロードクエリーに制約を追加したい場合があります。例を見てください。

$users = App\User::with(['posts' => function ($query) {
    $query->where('title', 'like', '%first%');

}])->get();

この例でEloquentはtitleカラムの内容にfirstという言葉を含むポストのみをEagerロードしています。もちろんEagerローディング操作をもっとカスタマイズするために他のクエリービルダーを呼び出すこともできます。

$users = App\User::with(['posts' => function ($query) {
    $query->orderBy('created_at', 'desc');

}])->get();

遅延Eagerローディング

既に親のモデルを取得した後にリレーションをEagerロードする必要がある場合もあるでしょう。たとえばこれはどの関連しているモデルをロードするかを動的に決める場合に便利です。

$books = App\Book::all();

if ($someCondition) {
    $books->load('author', 'publisher');
}

Eagerロードに追加の制約を付ける必要があるなら、loadメソッドへ「クロージャー」を渡してください。

$books->load(['author' => function ($query) {
    $query->orderBy('published_date', 'asc');
}]);

関係したモデルの挿入

saveメソッド

Eloquentは新しいモデルをリレーションに追加するために便利なメソッドを用意しています。たとえばPostモデルに新しいCommentを挿入する必要がある場合です。Commentpost_id属性を自分で設定する代わりに、リレーションのsaveメソッドで直接Commentを挿入できます。

$comment = new App\Comment(['message' => 'A new comment.']);

$post = App\Post::find(1);

$post->comments()->save($comment);

動的プロパティーとしてcommentsリレーションにアクセスできない点には注意してください。代わりにリレーションの取得でcommentsメソッドを呼び出しています。saveメソッドは自動的に新しいCommentモデルのpost_idへ適した値を代入します。

複数の関連したモデルを保存する必要があるなら、saveManyメソッドを使用できます。

$post = App\Post::find(1);

$post->comments()->saveMany([
    new App\Comment(['message' => 'A new comment.']),
    new App\Comment(['message' => 'Another comment.']),
]);

saveと多対多関係の挿入

多対多リレーションを操作する場合、saveメソッドの第2引数へ追加の中間テーブルの属性を指定できます。

App\User::find(1)->roles()->save($role, ['expires' => $expires]);

createメソッド

savesaveManyメソッドに加え、createメソッドも使用できます。属性の配列を引数に受け付け、モデルを作成しデータベースへ挿入します。savecreateの違いはsaveが完全なEloquentモデルを受け付けるのに対し、createは普通のPHPの「配列」を受け付ける点です。

$post = App\Post::find(1);

$comment = $post->comments()->create([
    'message' => '新しいコメント。',
]);

createメソッドを使用する前に属性の複数代入に関するドキュメントを読んでおいてください。

"Belongs To"リレーションの更新

belongsToリレーションを更新する場合はassociateメソッドを使います。このメソッドは子モデルへ外部キーをセットします。

$account = App\Account::find(10);

$user->account()->associate($account);

$user->save();

belongsToリレーションを削除する場合はdissociateメソッドを使用します。このメソッドはリレーションの子モデルの外部キーをリセットします。

$user->account()->dissociate();

$user->save();

多対多関係の挿入

attach/detach

多対多リレーションを操作時により便利なように、Eloquentはヘルパメソッドをいくつか用意しています。例としてユーザーが多くの役割りを持ち、役割も多くのユーザーを持てる場合を考えてみましょう。モデルを結びつけている中間テーブルにレコードを挿入することにより、ユーザーに役割を持たせるにはattachメソッドを使います。

$user = App\User::find(1);

$user->roles()->attach($roleId);

モデルにリレーションを割りつけるときに中間テーブルに挿入したい追加のデータを配列で渡すこともできます。

$user->roles()->attach($roleId, ['expires' => $expires]);

もちろんユーザーから役割を削除する必要がある場合もあるでしょう。多対多リレーションのレコードを削除するにはdetachメソッドを使います。detachメソッドは中間テーブルから対応するレコードを削除します。しかし両方のモデルはデータベースに残ります。

// ユーザーから役割を一つ切り離す…
$user->roles()->detach($roleId);

// ユーザーから役割を全部切り離す…
$user->roles()->detach();

便利なようにattachdetachの両方共にIDの配列を指定することができます。

$user = App\User::find(1);

$user->roles()->detach([1, 2, 3]);

$user->roles()->attach([1 => ['expires' => $expires], 2, 3]);

便利なsync

多対多の関連を構築するためにsyncメソッドも使用できます。syncメソッドへは中間テーブルに設置しておくIDの配列を渡します。その配列に指定されなかったIDは中間テーブルから削除されます。ですからこの操作が完了すると、中間テーブルには配列中のIDだけが存在することになります。

$user->roles()->sync([1, 2, 3]);

IDと一緒に中間テーブルの追加の値を渡すことができます。

$user->roles()->sync([1 => ['expires' => true], 2, 3]);

親のタイムスタンプの更新

CommentPostに所属しているように、あるモデルが他のモデルに所属(belongsToもしくはbelongsToMany)しており、子のモデルが更新される時に親のタイムスタンプを更新できると便利なことがあります。たとえばCommentモデルが更新されたら、所有者のPostupdated_atタイムスタンプを自動的に"touch"したい場合です。Eloquentなら簡単です。子のモデルにtouchesプロパティを追加しリレーション名を指定してください。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    /**
     * 更新する全リレーション
     *
     * @var array
     */
    protected $touches = ['post'];

    /**
     * ポストに所属しているコメント取得
     */
    public function post()
    {
        return $this->belongsTo('App\Post');
    }
}

これでCommentが更新されると、所有しているPostupdated_atカラムも同時に更新されます。

$comment = App\Comment::find(1);

$comment->text = 'このコメントを修正!';

$comment->save();

ドキュメント章別ページ

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュをURLへ付加します。

移動

クリックで即時移動します。

バージョン

設定

適用ボタンクリック後に、全項目まとめて適用されます。

カラーテーマ
和文指定 Pagination
和文指定 Scaffold
Largeスクリーン表示幅
インデント
本文フォント
コードフォント
フォント適用確認

フォントの指定フィールドから、フォーカスが外れると、当ブロックの内容に反映されます。EnglishのDisplayもPreviewしてください。

フォント設定時、表示に不具合が出た場合、当サイトのクッキーを削除してください。

バックスラッシュを含むインライン\Code\Blockの例です。

以下はコードブロックの例です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザに関連する電話レコードを取得
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

設定を保存する前に、表示が乱れないか必ず確認してください。CSSによるフォントファミリー指定の知識がない場合は、フォントを変更しないほうが良いでしょう。

キーボード・ショートカット

オープン操作

PDC

ページ(章)移動の左オフキャンバスオープン

HA

ヘッダー移動モーダルオープン

MS

移動/設定の右オフキャンバスオープン

ヘッダー移動

T

最初のヘッダーへ移動

E

最後のヘッダーへ移動

NJ

次ヘッダー(H2〜H4)へ移動

BK

前ヘッダー(H2〜H4)へ移動

その他

?

このヘルプページ表示
閉じる