Laravel 5.dev Eloquent ORM

イントロダクション

Eloquent ORMはLaravelに含まれており、美しくシンプルなアクティブレコードによるデーター操作の実装です。それぞれのデータベーステーブルは関連する「モデル」と結びついています。

使用を始める前に、cofig/database.phpで設定を確実に済ませてください。

基本的な使用法

使い始めるには、Eloquentモデルを作成してください。通常、モデルは'app'ディレクトリー下に置きますが、composer.jsonファイルでオートロードするように指定した場所であれば、どこでも自由に設置できます。。

Eloquentモデルを定義する

class User extends Eloquent {}

また、make:modelコマンドを使用し、Eloquentモデルを生成することもできます。

php artisan make:model Photo

Userモデルにどのテーブルを使用するのかをEloquentに指定していないことに注意してください。クラス名を小文字の複数形にしたものが、テーブル名として使用されます。他の名前を使用したい場合はそれを指定します。ですからこの場合、EloquentはUserモデルをusersテーブルに保存します。モデルのtableプロパティを定義し、テーブルを指定することもできます。

class User extends Eloquent {

    protected $table = 'my_users';

}

注目:Eloquentは更にテーブルの主キーとしてidというカラムであると仮定しています。この規約をオーバーライドする場合はprimaryKeyプロパティを定義してください。同様に、connectionプロパティを定義することで、そのモデルを取り扱うデータベース接続をオーバーライドすることもできます。

一度モデルを定義したら、テーブルのレコードを取得したり、作成したりする準備は整います。デフォルトではupdated_atcreated_atカラムをテーブルに用意しておく必要があることに注意してください。もしこれらのカラムが自動的に更新されたくないのでしたら、$timestampsfalseに設定してください。

全モデルを取得する

$users = User::all();

主キーで1レコードを取得する

$user = User::find(1);

var_dump($user->name);

クエリービルダーの全メソッドは、Eloquentモデルを使ってクエリーする場合に使用できます。

主キーでモデルを取得するか、例外を投げる

モデルが見つからない場合、例外を投げたいこともあることでしょう。例外はApp::errorハンドラーを使用して捕捉し、404ページを表示することが可能です。

$model = User::findOrFail(1);

$model = User::where('votes', '>', 100)->firstOrFail();

エラーハンドラーを登録するには、ModelNotFoundExceptionをリッスンしてください。

use Illuminate\Database\Eloquent\ModelNotFoundException;

App::error(function(ModelNotFoundException $e)
{
    return Response::make('Not Found', 404);
});

Eloquentモデルを使用し、クエリーを行う

$users = User::where('votes', '>', 100)->take(10)->get();

foreach ($users as $user)
{
    var_dump($user->name);
}

Eloquentを使った集計

もちろんクエリービルダーの集計関数も使用できます。

$count = User::where('votes', '>', 100)->count();

Fluentインターフェイスで必要なクエリーを生成できない場合、whereRawを使用してください。

$users = User::whereRaw('age > ? and votes = 100', array(25))->get();

結果を分割する

大きな(数千の)Eloquentレコードを処理する必要がある場合、RAMが食いつぶされないように、chunkコマンドを利用してください。

User::chunk(200, function($users)
{
    foreach ($users as $user)
    {
        //
    }
});

最初の引数には「チャンク(塊)」ごとにいくつのレコードを処理するかを渡します。2番めの引数にはクロージャーを渡し、そのデータベースからの結果をチャンクごとに処理するコードを記述します。

クエリー接続の指定

Eloquentクエリーの実行時に、データベース接続を指定することができます。onメソッドを使用してください。

$user = User::on('connection-name')->find(1);

Read/Write接続を利用している場合に、"write"接続で強制的にクエリーする場合は、以下のメソッドを使用してください。

$user = User::onWriteConnection()->find(1);

複数代入

新しいモデルを作成する時、モデルのコンストラクターに属性の配列を渡します。それらの属性は複数代入としてモデルに結び付けられます。これは便利ですが、ユーザーの入力を闇雲にモデルに渡してしまうことは、重大なセキュリティーの欠陥になり得ます。何でもかんでもユーザー入力をモデルに渡せば、ユーザーは何でもモデルの属性を変更できるのです。 このため、全てのEloquentモデルはデフォルトで複数代入されないように保護されています。

複数代入を使用するにはfillableguardedプロパティをモデルに設定してください。

モデルに複数代入可能な属性を定義する

fillableプロパティーは複数代入を許す属性です。クラスとインスタンスの両レベルで設定できます。

class User extends Eloquent {

    protected $fillable = array('first_name', 'last_name', 'email');

}

この例では、リストされている3属性のみ、複数代入されます。

モデルで保護する属性を定義する

このfillableの反対がguardedです。「ホワイトリスト」ではなく、「ブラックリスト」として利用します。

class User extends Eloquent {

    protected $guarded = array('id', 'password');

}

注目:guardedを使用する場合、守られて(guarded)いないカラムが更新されてしまうため、Input::get()や、ユーザーがコントロールできる配列をそのまま、saveupdateメソッドには渡すべきではありません。

全属性を複数代入から保護する

この例ではidpassword属性が複数代入の対象外となります。その他の属性は複数代入されます。属性を複数代入から保護するには、guardプロパティーを使用してください。

protected $guarded = array('*');

INSERT、UPDATE、DELETE

モデルから新しいレコードを作成するには、モデルの新しいインスタンスを作成し、saveメソッドを呼び出します。

新しいモデルを保存する

$user = new User;

$user->name = 'John';

$user->save();

注目:典型的にEloquentモデルは自動的にキーを増加させ使用します。しかしながら自分でキーを設定したい場合は、モデルのincrementingプロパティをfalseにセットしてください。

もしくは一行で新しいモデルを保存するためにcreateメソッドを使用することも可能です。メソッドから挿入されたモデルのインスタンスがリターンされます。しかしながら全Eloquentモデルは複数代入から保護されているため、これを使用する前に操作対象のモデルに対しfillableguardedプロパティのどちらかを指定しておく必要があります。

IDの自動増分を設定しているモデルを保存、もしくは新しく作成した後、IDを取得したい場合は、オブジェクトのid属性にアクセスしてください。

$insertedId = $user->id;

モデルに保護された属性を設定する

class User extends Eloquent {

    protected $guarded = array('id', 'account_id');

}

モデルのcreateメソッドを使用する

// データベースに新しいユーザーを作成する
$user = User::create(array('name' => 'John'));

// 属性により、ユーザーを取得するか、存在していない場合は作成する
$user = User::firstOrCreate(array('name' => 'John'));

// 属性により、ユーザーを取得するか、新しいインスタンスを生成する
$user = User::firstOrNew(array('name' => 'John'));

取得したモデルを更新する

モデルを更新する場合、初めに取得し、その内容を変更し、それからsaveメソッドを使用します。

$user = User::find(1);

$user->email = 'john@foo.com';

$user->save();

モデルと関連を保存する

時々、モデルだけでなく、全部の関連を保存したい場合もあるでしょう。そんな場合は、pushメソッドを使用してください。

$user->push();

複数のモデルに対しクエリーで更新することもできます。

$affectedRows = User::where('votes', '>', 100)->update(array('status' => 2));

注意:上記のように一連のモデルをまとめてEloquentクエリービルダーで更新する場合、モデルのイベントは発行されません。

存在しているモデルを削除する

削除するには、シンプルにインスタンスに対しdeleteメソッドを使用してください。

$user = User::find(1);

$user->delete();

キーを指定し、存在しているモデルを削除する

User::destroy(1);

User::destroy(array(1, 2, 3));

User::destroy(1, 2, 3);

もちろん、deleteクエリーで複数のモデルを削除できます。

$affectedRows = User::where('votes', '>', 100)->delete();

モデルのタイムスタンプだけをアップデートする

モデルのタイムスタンプをただアップデートしたい場合は、touchメソッドを使用します。

$user->touch();

ソフトデリート

モデルをソフトデリートする場合、データーベースから実際に削除されるわけではありません。代わりにレコードへdeleted_atタイムスタンプをセットします。モデルのソフトデリートを有効にするには、モデルに対しSoftDeletesを適用してください。

use Illuminate\Database\Eloquent\SoftDeletes;

class User extends Eloquent {

    use SoftDeletes;

    protected $dates = ['deleted_at'];

}

deleted_atカラムをテーブルに追加するには、マイグレーションでsoftDeletesメソッドを使用してください。

$table->softDeletes();

これでモデルに対しdeleteメソッドを使用すれば、deleted_atカラムに現在の時間がセットされます。ソフトデリートされたモデルに対しクエリーされても「削除済み」のモデルはクエリー結果に含まれません。

ソフトデリートされたモデルも結果に含めるように強制する

ソフトデリートされたモデルも結果に含めたい場合、withTrashedメソッドをクエリーに使用してください。

$users = User::withTrashed()->where('account_id', 1)->get();

定義済みの関連付けに対して、withTrashedメソッドを使用することができます。

$user->posts()->withTrashed()->get();

もしソフトデリートされたモデルのみに対しクエリーしたい場合はonlyTrashedメソッドを使用します。

$users = User::onlyTrashed()->where('account_id', 1)->get();

ソフトデリートされたモデルを有効状態へ復活させるにはrestoreメソッドを使ってください。

$user->restore();

restoreメソッドはクエリーと共に使用することもできます。

User::withTrashed()->where('account_id', 1)->restore();

withTrashedと同様に、restoreメソッドも、関連付けに対して使用することができます。

$user->posts()->restore();

モデルを本当にデータベースから削除したい場合は、forceDeleteメソッドを使用してください。

$user->forceDelete();

これも関連付けに対して使用することもできます。

$user->posts()->forceDelete();

指定したモデルのインスタンスがソフトデリートされたものか判断するには、trashedメソッドを使用します。

if ($user->trashed())
{
    //
}

タイムスタンプ

デフォルトでEloquentはデータベースのcreated_at(作成日時)とupdated_at(更新日時)カラムを自動的に更新します。上記のタイムスタンプカラムをテーブルに追加するだけで、Eloquentは残りの面倒を見てくれます。Eloquentによる2つのカラムのメンテナンスがご希望でなければ、モデルに以下のプロパティを追加してください。

自動タイムスタンプを無効にする

class User extends Eloquent {

    protected $table = 'users';

    public $timestamps = false;

}

カスタムタイムスタンプフォーマットを指定する

タイムスタンプのフォーマットをカスタマイズしたい場合は、getDateFormatメソッドをモデルの中でオーバーライドしてください。

class User extends Eloquent {

    protected function getDateFormat()
    {
        return 'U';
    }

}

クエリースコープ

クエリースコープを定義する

スコープはモデルに対するクエリーのロジックを簡単に再使用できるようにしてくれます。スコープを定義するには、モデルのメソッド名にscopeをプレフィックスとして付けてください。

class User extends Eloquent {

    public function scopePopular($query)
    {
        return $query->where('votes', '>', 100);
    }

    public function scopeWomen($query)
    {
        return $query->whereGender('W');
    }

}

クエリースコープを使用する

$users = User::popular()->women()->orderBy('created_at')->get();

動的スコープ

スコープでパラメーターを受け取りたい場合もあります。スコープのメソッドにパラメーターを追加するだけです。

class User extends Eloquent {

    public function scopeOfType($query, $type)
    {
        return $query->whereType($type);
    }

}

スコープの呼び出し時に、パラメーターを渡してください。

$users = User::ofType('member')->get();

グローバルスコープ

モデルの全クエリーに適用させるスコープを定義したいこともあるでしょう。実際に、Eloquentの「ソフトデリート」機能はこれを使用しています。グローバルスコープは、PHPのトレイトとIlluminate\Database\Eloquent\ScopeInterfaceの実装を組み合わせ、定義します。

最初にトレイトを定義します。例として、Laravelに組み込まれているSoftDeletesを使いましょう。

trait SoftDeletes {

    /**
     * モデルのため、ソフトデリートトレイトを起動する
     *
     * @return void
     */
    public static function bootSoftDeletes()
    {
        static::addGlobalScope(new SoftDeletingScope);
    }

}

Eloquentモデルにbootトレイトの名前Traitという命名規則に一致するメソッドが存在するならば、Eloquentモデルの起動時に、グローバールスコープを登録するか、他の皆さんが行いたいことを行う機会を作るために、そのトレイトメソッドは呼び出されます。スコープはapplyremoveメソッドの2つを持つ、ScopeInterfaceを実装しなくてはなりません。

applyメソッドは、Illuminate\Database\Eloquent\Builderクエリービルダーオブジェクトを受け取り、そのスコープで付け加えたいwhere節を追加してから、返す責任があります。removeメソッドも「ビルダー」オブジェクトを受け取り、applyにより付け加えられたアクションを取り消してから、返す必要があります。言い換えれば、removeは追加されたwhere節(もしくは他の節)を削除しなくてはなりません。これにより、SoftDeletingScopeのメソッドは、次のようになります。

/**
 * 指定されたEloquentクエリービルダーにスコープを適用する
 *
 * @param  \Illuminate\Database\Eloquent\Builder  $builder
 * @return void
 */
public function apply(Builder $builder)
{
    $model = $builder->getModel();

    $builder->whereNull($model->getQualifiedDeletedAtColumn());
}

/**
 * 指定されたEloquentクエリービルダーからスコープを削除する
 *
 * @param  \Illuminate\Database\Eloquent\Builder  $builder
 * @return void
 */
public function remove(Builder $builder)
{
    $column = $builder->getModel()->getQualifiedDeletedAtColumn();

    $query = $builder->getQuery();

    foreach ((array) $query->wheres as $key => $where)
    {
        // where節がソフトデリートのデータ制約によるものであれば、クエリーから削除し
        // whereのキーをリセットする。これにより開発者は削除済みモデルを関連付けに
        // 含めることができ、遅延ロードされた結果をセットできる。
        if ($this->isSoftDeleteConstraint($where, $column))
        {
            unset($query->wheres[$key]);

            $query->wheres = array_values($query->wheres);
        }
    }
}

リレーション

もちろん、データベーステーブルは他のテーブルと関連があることでしょう。例えば、ブログポストは多くのコメントを持つでしょうし、注文は購入したユーザーと関連しているでしょう。Eloquentはこれらの関連を簡単に操作できるように管理します。Laravelは多くの関連をサポートしています。

1対1

1対1関係を定義する

1対1関係は基本です。例えば、UserモデルはPhoneモデルを一つ持つとしましょう。この関係をEloquentで定義します。

class User extends Eloquent {

    public function phone()
    {
        return $this->hasOne('App\Phone');
    }

}

hasOneメソッドの最初の引数は関係するモデルの名前です。関連を定義したら、Eloquentの動的プロパティを使用し取得できます。

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

この文は以下のSQLとして動作します。

select * from users where id = 1

select * from phones where user_id = 1

Eloquentはモデル名を元に関連の外部キーを決めることに注意してください。この場合、Phoneモデルはuser_id外部キーを使用しようとします。この規約をオーバーライドしたい場合、hasOneメソッドの第2引数を指定してください。さらにその関連で使用されるべきローカルカラムを指定するために、メソッドの第3引数を渡すこともできます。

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

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

逆の関連を定義する

Phoneモデルの関連を逆に定義するには、belongsToメソッドを使います。

class Phone extends Eloquent {

    public function user()
    {
        return $this->belongsTo('App\User');
    }

}

上の例で、Eloquentは、phonesテーブルのuser_idカラムを探します。もし、他の外部キーを定義したい場合は、belongsToメソッドの第2引数に渡してください。

class Phone extends Eloquent {

    public function user()
    {
        return $this->belongsTo('App\User', 'local_key');
    }

}

さらに、親のテーブルの関連するカラム名を第3引数として指定することもできます。

class Phone extends Eloquent {

    public function user()
    {
        return $this->belongsTo('App\User', 'local_key', 'parent_key');
    }

}

1対多

1対多の関連の例には、「プログポストは多くのコメントを持つ(has many)」があります。このように関係をモデルに定義します。

class Post extends Eloquent {

    public function comments()
    {
        return $this->hasMany('App\Comment');
    }

}

これで動的プロパティを使用し、ポストのコメントへアクセスできます。

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

取得するコメントを制限する必要がある場合、commentsメソッドを呼び出し条件をチェーンでつなげることができます。

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

ここでも、hasManyメソッドの第2引数を指定し、外部キーの規約をオーバーライドできます。また、hasOneリレーションと同様に、ローカルカラムも指定できます。

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

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

逆の関連を定義する

Commentモデルに逆の関係を定義するには、belongsToを使用します。

class Comment extends Eloquent {

    public function post()
    {
        return $this->belongsTo('App\Post');
    }

}

多対多

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

多対多の関係はbelongsToManyメソッドで定義します。

class User extends Eloquent {

    public function roles()
    {
        return $this->belongsToMany('App\Role');
    }

}

では、Userモデルを通して、役割を取得しましょう。

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

もしピボットテーブル名に規約から外れた名前を使用したい場合は、belongsToManyメソッドの第2引数に渡してください。

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

更に関係するキー名の規約をオーバーライドするには:

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

もちろん、Roleモデルに対し逆のリレーションを定義することもできます。

class Role extends Eloquent {

    public function users()
    {
        return $this->belongsToMany('App\User');
    }

}

他テーブルを経由した多対他

「〜経由の多対他(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によりアクセスできます。関連を定義付けましょう。

class Country extends Eloquent {

    public function posts()
    {
        return $this->hasManyThrough('App\Post', 'User');
    }

}

関連付けのキーを手動で指定したい場合、メソッドの第3引数と、第4引数として渡して下さい。

class Country extends Eloquent {

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

}

多様対応関係

多様対応(polymorphic)関係はあるモデルを一つの関係だけで、複数のモデルに所属させるものです。例えば写真モデルがあり、スタッフモデルと注文モデルの両方に所属しているとしましょう。この関係を次のように定義できます。

class Photo extends Eloquent {

    public function imageable()
    {
        return $this->morphTo();
    }

}

class Staff extends Eloquent {

    public function photos()
    {
        return $this->morphMany('App\Photo', 'imageable');
    }

}

class Order extends Eloquent {

    public function photos()
    {
        return $this->morphMany('App\Photo', 'imageable');
    }

}

多様対応関係で取得する

では、スタッフと注文を通して、写真を取得してみましょう。

$staff = Staff::find(1);

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

多様対応関連の所有者を取得する

しかし、本当の「多様対応関係」マジックは、スタッフと注文を写真('Photo')モデルを通じてアクセスする場合に現れます。

$photo = Photo::find(1);

$imageable = $photo->imageable;

Photoモデルのimageable関係は写真を所有しているモデルのタイプにより、StaffもしくはOrderどちらかのインスタンスをリターンします。

多様対応関係のテーブル構造

これがどの様に動作するのか理解を助けるために、多様対応関係のデータベース構造を説明しましょう。

staff
    id - integer
    name - string

orders
    id - integer
    price - integer

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

注目すべき鍵となるフィールドは、photosテーブルのimageable_idimageable_typeです。この例の場合、IDは所有しているスタッフか注文のIDで、タイプは所有しているモデルのクラス名です。これにより、ORMが所有しているモデルのタイプを決定し、imageable関係でアクセスした時にそのモデルをリターンする仕組みです。

多対多 多様対応関係

多対他 多様対応関係のテーブル構造

伝統的な多様対応関係に加え、多対多-多様対応関係も指定することができます。例えば、ブログの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モデルは、tagsメソッドを通じ、両方共にmorphToMany関係を持ちます。

class Post extends Eloquent {

    public function tags()
    {
        return $this->morphToMany('App\Tag', 'taggable');
    }

}

Tagモデルでは、それぞれの関係を表すメソッドを定義します。

class Tag extends Eloquent {

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

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

}

リレーションの問い合わせ

SELECT時にリレーションを問い合わせる

モデルのレコードにアクセスする場合、関連付けたモデルのレコードに基づいて制限したい場合もあると思います。例えば、最低でも一つのコメントを持つ、全ブログポストを取得したい場合です。これを行うためにはhasメソッドを使用します。

$posts = Post::has('comments')->get();

演算子とレコード数も指定できます。

$posts = Post::has('comments', '>=', 3)->get();

ネストしたhas文は、「ドット」記法で組立てられます。

$posts = Post::has('comments.votes')->get();

もっと強い力がほしいなら、hasの問い合わせに、"where"で条件をつけるために、whereHasorWhereHasを利用して下さい。

$posts = Post::whereHas('comments', function($q)
{
    $q->where('content', 'like', 'foo%');

})->get();

動的プロパティ

Eloquentは動的プロパティーを使用して関係にアクセスする方法を提供しています。Eloquentは自動的にその関係をロードし、賢いことにget(1対多関係)メソッドとfirst(1対1関係)メソッドを使い分けます。動的プロパティーでアクセスするにはその関係名をメソッド名として使います。例えば、次のPhoneモデルをご覧ください。

class Phone extends Eloquent {

    public function user()
    {
        return $this->belongsTo('App\User');
    }

}

$phone = Phone::find(1);

以下のようにメールアドレスをechoします。

echo $phone->user()->first()->email;

その代わりに、もっとシンプルで短く記述できます。

echo $phone->user->email;

複数の結果をリターンするリレーションは、Illuminate\Database\Eloquent\Collectionクラスのインスタンスを返します。

Eagerローディング

EagerローディングはN+1クエリー問題を軽減するために存在しています。例えば、BookモデルがAuthorモデルと関連していると考えてください。関係はこのように定義されます。

class Book extends Eloquent {

    public function author()
    {
        return $this->belongsTo('App\Author');
    }

}

では、次のコードを考察してみましょう。

foreach (Book::all() as $book)
{
    echo $book->author->name;
}

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

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

foreach (Book::with('author')->get() as $book)
{
    echo $book->author->name;
}

上のループでは、2つだけしかクエリーが実行されません。

select * from books

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

賢くEagerローディングを使用することで、アプリケーションのパフォーマンスを大幅に向上させることができます。

もちろん、一度に複数の関係をEagerローディングすることもできます。

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

ネストした関連でさえ、Eagerローディングできます。

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

上記では、author関係がEagerローディングされ、それからauthorのcontacts関係がEagerローディングされます。

Eagerローディングの条件付け

関係をEagerローディングする時に、条件を付けたい場合もあります。一例をご覧ください。

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

}))->get();

この例では、ユーザーの写真をEagerローディングしていますが、写真のタイトルが"first"という単語で構成されているものだけが取得されます。

もちろん、Eagerローディングクロージャーは、「検索条件」指定だけに限定されていません。ソート順も指定できます。

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

}))->get();

遅延Eagerローディング

既に存在しているモデルコレクションから直接関係をEagerローディングすることも可能です。これは関係するモデルをロードするかどうかを動的に決める場合や、キャッシュと結びつける場合に便利です。

$books = Book::all();

$books->load('author', 'publisher');

関連したモデルの挿入

関連するモデルを追加する

関連するモデルを新しく挿入する機会も多いと思います。例えば、ポストに対して新しいコメントを挿入する場合です。モデルに外部キーとしてpost_idをセットする代わりに、新しいコメントを親のPostモデルから直接挿入できます。

$comment = new Comment(array('message' => 'A new comment.'));

$post = Post::find(1);

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

この例では、post_idフィールドは自動的にセットされ、コメントが挿入されます。

関連するモデルを一度に追加することもできます。

$comments = array(
    new Comment(array('message' => 'A new comment.')),
    new Comment(array('message' => 'Another comment.')),
    new Comment(array('message' => 'The latest comment.'))
);

$post = Post::find(1);

$post->comments()->saveMany($comments);

関係づけられているモデル(所属)

belongsTo関係を更新する場合、associateメソッドを使用できます。このメソッドは子供のモデルに外部キーをセットします。

$account = Account::find(10);

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

$user->save();

関連したモデルの挿入 (Many To Many)

多対多の関連を操作する場合も、関連するモデルを挿入したい場合があるでしょう。UserRoleの例を使い、続けて説明します。attachメソッドを使用し、新しい役目をユーザーに追加することができます。

多対多モデルを追加する

$user = User::find(1);

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

その関係に対してピボットテーブルに保存する属性の配列を渡すこともできます。

$user->roles()->attach(1, array('expires' => $expires));

もちろんattachの反対はdetachです。

$user->roles()->detach(1);

attachdetachの両方共に、IDの配列を指定することができます。

$user = User::find(1);

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

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

多対多モデルの追加にSyncを使用する

さらにsyncメソッドで関連するモデルを追加することもできます。syncメソッドはピボットテーブルに設置するIDの配列を渡します。これによる操作が終了すると、そのモデルに対する中間テーブルは配列で指定されたIDだけになります。

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

Syncを使い、ピボットデーターを追加する

IDを指定して他のピボットテーブル値を関連付けることもできます。

$user->roles()->sync(array(1 => array('expires' => true)));

時にはひとつのコマンドで、関連するモデルを作成し、追加したいこともあるでしょう。その場合、saveメソッドを使用してください。

$role = new Role(array('name' => 'Editor'));

User::find(1)->roles()->save($role);

この例では、新しいRoleモデルは保存され、ユーザーモデルに結び付けられます。更に以下の操作で、関連するテーブルに設定する属性の配列を渡すことができます。

User::find(1)->roles()->save($role, array('expires' => $expires));

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

例えばCommentPostに所属しているような場合、子供のモデルが変更された場合、所属している(belongsTo)親のタイムスタンプも変更されると便利です。Commentモデルが更新されたら、Postが持っているupdated_atタイムスタンプも自動的に更新したい場合などです。Eloquentでは簡単に実現できます。子供のモデルでtouchesプロパティーに、関連名を付け加えてください。

class Comment extends Eloquent {

    protected $touches = array('post');

    public function post()
    {
        return $this->belongsTo('App\Post');
    }

}

これで、Postに所有されているCommentが更新されると、Postupdated_atカラムも更新されるようになります。

$comment = Comment::find(1);

$comment->text = 'Edit to this comment!';

$comment->save();

ピボットテーブルの操作

既に学んだように、多対多の関連を操作するためには、中間テーブルが必要です。Eloquentはこのテーブルを操作するために役に立つ手法を提供しています。例えば、Userオブジェクトが多くの関連するRoleオブジェクトを所有しているとしましょう。この関連にアクセスした後に、モデルに対するピボット(pivot)テーブルにもアクセスできます。

$user = User::find(1);

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

取得されたRoleモデルそれぞれは、自動的にpivot属性を与えられていることに注目してください。この属性は中間テーブルを表すモデルで、他のEloquentモデルと同様に使用できます。

デフォルトでは、pivotオブジェクトにはキーしかありません。もし、あなたのピボットテーブルにその他の属性があるのでしたら、関連を定義する時にそれを指定する必要があります。

return $this->belongsToMany('App\Role')->withPivot('foo', 'bar');

これでRoleモデルのpivotオブジェクトで、foobar属性はアクセス可能になります。

もしピボットテーブルでも自動的にcreated_atupdated_atタイムスタンプを更新したい場合は、関係の定義に対してwithTimestampsメソッドを使用してください。

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

Pivotテーブルのレコードを削除する

モデルのピボットテーブルの全レコードを削除したい場合は、detachメソッドを使用してください。

User::find(1)->roles()->detach();

この操作はrolesテーブルからレコードを削除せず、ピボットテーブルに対してだけ操作されることに注目してください。

ピボットテーブル上のレコードを更新する

場合により、ピボットテーブルを削除せずに、更新する必要があることもあるでしょう。存在するピボットテーブルを更新したい場合は、updateExistingPivotメソッドが使用できます。

User::find(1)->roles()->updateExistingPivot($roleId, $attributes);

カスタムピボットモデルを定義する

Laravelではカスタムピボットモデルを定義することさえできます。カスタムモデルを定義するためには、最初にEloquentを拡張したBaseモデルクラスを作成します。そして他のEloquentモデルでは、デフォルトのEloquentの代わりに、このカスタムベースモデルを拡張しましょう。ベースモデルの中に、カスタムピボットモデルのインスタンスを返す、次のような関数を追加してください。

public function newPivot(Model $parent, array $attributes, $table, $exists)
{
    return new YourCustomPivot($parent, $attributes, $table, $exists);
}

コレクション

getメソッドであれ、関連によるものであれ、Eloquentが複数のレコードをリターンする場合、Eloquent Collectionオブジェクトが返されます。このオブジェクトはIteratorAggregate PHPインターフェイスの実装で、そのため配列のように操作できます。更に、結果を操作するためのバラエティーに富んだ他のパワフルなメソッドも持っています。

特定のキーをコレクションに含んでいるか調べる。

例えば、containsメソッドを使用し、指定した主キーを結果に含んでいるか調べることができます。

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

if ($roles->contains(2))
{
    //
}

コレクションは配列やJSONに変換することもできます。

$roles = User::find(1)->roles->toArray();

$roles = User::find(1)->roles->toJson();

コレクションを文字列にキャストすると、JSONがリターンされます。

$roles = (string) User::find(1)->roles;

コレクションの反復処理

Eloquentコレクションは、中に含んでいるアイテムをループしたり、フィルタリングしたりする便利なメソッドも持っています。

$roles = $user->roles->each(function($role)
{
    //
});

コレクションのフィルタリング

コレクションをフィルタリングする場合、指定されたコールバックは、array_filterのコールバックとして利用されます。

$users = $users->filter(function($user)
{
    return $user->isAdmin();
});

注目:コレクションをフィルタリングするか、JSONに変換する場合、valuesメソッドを最初に呼び出し、配列のキーをリセットしてください。

コレクションの各オブジェクトにコールバックを適用する

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

$roles->each(function($role)
{
    //
});

値でコレクションをソートする

$roles = $roles->sortBy(function($role)
{
    return $role->created_at;
});

値でコレクションをソートする

$roles = $roles->sortBy('created_at');

カスタムコレクションタイプをリターンする

カスタムメソッドをつけた、カスタムコレクションオブジェクトをリターンしたい場合もあるでしょう。EloquentモデルのnewCollectionメソッドをオーバーライドしてください。

class User extends Eloquent {

    public function newCollection(array $models = array())
    {
        return new CustomCollection($models);
    }

}

アクセサーとミューテーター

アクセサーの定義

Eloquentはモデルの属性を設定したり取得したりする時に、内容を変更する便利な方法を提供しています。モデルのアクセサーを宣言するには、getFooAttributeメソッドを定義するだけです。データベースのカラムがスネークケースであったとしても、メソッドはキャメルケースにしなくてはならないことに注意してください。

class User extends Eloquent {

    public function getFirstNameAttribute($value)
    {
        return ucfirst($value);
    }

}

上記の例は、first_nameカラムのアクセサーです。アクセサーに属性の値を渡していることに注目してください。

ミューテーターを定義する

ミューテーターも同じやり方で定義します。

class User extends Eloquent {

    public function setFirstNameAttribute($value)
    {
        $this->attributes['first_name'] = strtolower($value);
    }

}

日付ミューテーター

デフォルトでEloquentはcreated_at、updated_atカラムを Carbonのインスタンスに変換します。Carbonは便利なメソッドが揃っており、PHPネイティブのDateTimeクラスを拡張しています。

これらのフィールドの自動変換をカスタマイズ、またはこの変換を完全に無効にすることもモデルのgetDatesメソッドをオーバーライドすることで可能です。

public function getDates()
{
    return array('created_at');
}

日付項目にはUNIXタイムスタンプ、日付文字列(Y-m-d)、日付時間文字列、そしてもちろんDateTimeCarbonのインスタンスを設定することができます。

日付ミューテーターを完全に無効にしたい場合は、getDatesメソッドから空配列をリターンしてください。

public function getDates()
{
    return array();
}

属性の型変換

常に他のデータ型へ型変換したい属性がある場合、モデルのcastsプロパティーへ属性を追加してください。もしくは、手間がかかりますが各属性をミューテターでそれぞれ定義してください。castsプロパティの使用例をご覧ください。

/**
 * ネイティブなタイプへ型変換しなくてはならない属性
 *
 * @var array
 */
protected $casts = [
    'is_admin' => 'boolean',
];

これで、例えデータベース上では整数として値が保存されていても、is_admin属性にアクセスすれば、いつも論理型へ型変換されます。サポートしている変換型は、integerrealfloatdoublestringbooleanarrayです。

array型変換はシリアライズ済みのJSONとして保存されているカラムに利用すると特に便利です。例えば、データベースにシリアライズされたJSONがTEXTタイプのフィールドにある時、array型変換を指定しておけば、そのEloquentモデルへアクセすると、属性がPHP配列へ自動的に非シリアライズされます。

/**
 * ネイティブなタイプへ型変換しなくてはならない属性
 *
 * @var array
 */
protected $casts = [
    'options' => 'array',
];

これで、Eloquentモデルを使用します。

$user = User::find(1);

// $optionsは配列…
$options = $user->options;

// optionsは自動的にJSONに戻される…
$user->options = ['foo' => 'bar'];

モデルイベント

Eloquentモデルは多くのイベントを発行します。以降のメソッドを利用し、モデルのライフサイクルの様々な時点をフックすることができます。:creatingcreatedupdatingupdatedsavingsaveddeletingdeletedrestoringrestored

いつでも新しいアイテムが最初に保存される場合に、creatingcreatedイベントが発行されます。新しくないアイテムにsaveメソッドが呼び出されると、updatingupdatedイベントが発行されます。どちらの場合にも、savingsavedイベントは発行されます。

イベントにより保存操作をキャンセルする

もし、creatingupdatingsavingdeletingイベントでfalseがリターンされると、そのアクションはキャンセルされます。

User::creating(function($user)
{
    if ( ! $user->isValid()) return false;
});

イベントリスナーを登録する場所

EventServiceProviderはモデルのイベント結合を登録するため、便利に使えます。例をご覧ください。

/**
 * アプリケーションの他のイベントを登録する
 *
 * @param  \Illuminate\Contracts\Events\Dispatcher  $events
 * @return void
 */
public function boot(DispatcherContract $events)
{
    parent::boot($events);

    User::creating(function($user)
    {
        //
    });
}

モデルオブザーバー

モデルのイベントを取りまとめるにはモデルオブザーバーを登録してください。オブザーバークラスは対応するそれぞれのモデルイベントのメソッドを用意します。例えば、監視するのがcreatingupdatingsavingイベントなら、その名前のメソッドを用意します。もちろん他のモデルイベント名も追加できます。

ですから、モデルオブザーバーは次のような形式になります。

class UserObserver {

    public function saving($model)
    {
        //
    }

    public function saved($model)
    {
        //
    }

}

オブザーバーのインスタンスはobserveメソッドを使いモデルに登録してください。

User::observe(new UserObserver);

配列とJSONへの変換

モデルを配列に変換する

JSONでAPIを作成する場合、モデルと関連をしばしば配列やJSONに変換する必要が起きます。そのため、Eloquentはこれを行うメソッドを含んでいます。モデルとそれにロードされている関連を配列に変換するには、toArrayメソッドが使用できます。

$user = User::with('roles')->first();

return $user->toArray();

モデルの全コレクションを配列に変換することさえできることに注目してください。

return User::all()->toArray();

モデルをJSONに変換する

モデルをJSONに変換するには、toJsonメソッドを使用します。

return User::find(1)->toJson();

モデルをルートでリターンする

モデルやコレクションを文字列に変換する場合、JSON形式に変換されることに注目してください。これが意味するのはEloquentオブジェクトを直接アプリケーションのルートからリターンできるということです!

Route::get('users', function()
{
    return User::all();
});

配列とJSONへの変換から特定の属性を除く

パスワードのように、モデルの配列やJSON形式に含める属性を制限したい属性もあります。そのためにはhiddenプロパティーをモデルで定義してください。

class User extends Eloquent {

    protected $hidden = array('password');

}

注意:リレーションを隠している場合、動的なアクセサー名を使用せずに、リレーションのメソッド名を使用してください。

反対に、visibleプロパティでホワイトリストを定義することもできます。

protected $visible = array('first_name', 'last_name');

通常、データーベースのカラムに対応しない属性を追加する必要もあるでしょう。これを行うには、先ずシンプルに値を返すアクセサーを定義してください。

public function getIsAdminAttribute()
{
    return $this->attributes['admin'] == 'yes';
}

アクセサーを作成したら、モデルのappendsプロパティーにその値を追加してください。

protected $appends = array('is_admin');

一度属性をappendsリストに追加したら、モデルの配列とJSON形式の両方へ、含まれるようになります。appends配列の属性は、モデル中のvisiblehidden設定の影響を受けます。

ドキュメント章別ページ

Artisan CLI

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュを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)へ移動

その他

?

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