Laravel 5.2 Eloquent:利用の開始

イントロダクション

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

使いはじめる前に確実にcofig/database.phpを設定してください。データベースの詳細はドキュメントで確認してください。

モデル定義

利用を開始するには、まずEloquentモデルを作成しましょう。通常モデルはappディレクトリ下に置きますが、composer.jsonファイルでオートロードするように指定した場所であれば、どこでも自由に設置できます。全てのEloquentモデルは、Illuminate\Database\Eloquent\Modelを拡張する必要があります。

モデルを作成する一番簡単な方法はmake:model Artisanコマンドを使用することです。

php artisan make:model User

モデル作成時にデータベースマイグレーションも生成したければ、--migration-mオプションを使ってください。

php artisan make:model User --migration

php artisan make:model User -m

Eloquentモデル規約

ではflightsデータベーステーブルに情報を保存し、取得するために使用するFlightモデルクラスを例として見てください。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    //
}

テーブル名

Flightモデルにどのテーブルを使用するかEloquentに指定していない点に注目してください。他の名前を明示的に指定しない限り、クラス名を複数形の「スネークケース」にしたものがテーブル名として使用されます。今回の例でEloquentはFlightモデルをflightsテーブルに保存します。モデルのtableプロパティを定義し、カスタムテーブル名を指定できます。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * モデルと関連しているテーブル
     *
     * @var string
     */
    protected $table = 'my_flights';
}

主キー

Eloquentは更にテーブルの主キーがidというカラム名であると想定しています。この規約をオーバーライドする場合はprimaryKeyプロパティを定義してください。

さらに、Eloquentは主キーを自動増分される整数値であるとも想定しています。つまり、デフォルト状態で主キーは自動的にintへキャストされます。自動増分ではない、もしくは整数値ではない主キーを使う場合、モデルにpublicの$incrementingプロパティーを用意し、falseをセットしてください。

タイムスタンプ

デフォルトでEloquentはデータベース上に存在するcreated_at(作成時間)とupdated_at(更新時間)カラムを自動的に更新します。これらのカラムの自動更新をEloquentにしてほしくない場合は、モデルの$timestampsプロパティーをfalseに設定してください。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * モデルのタイムスタンプを更新するかの指示
     *
     * @var bool
     */
    public $timestamps = false;
}

タイムスタンプのフォーマットをカスタマイズする必要があるなら、モデルの$dateFormatプロパティーを設定してください。このプロパティーはデータベースに保存される日付属性のフォーマットを決めるために使用されると同時に、配列やJSONへシリアライズする時にも使われます。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * モデルの日付カラムの保存フォーマット
     *
     * @var string
     */
    protected $dateFormat = 'U';
}

データベース接続

Eloquentモデルはデフォルトとして、アプリケーションに設定されているデフォルトのデータベース接続を使用します。モデルで異なった接続を指定したい場合は、$connectionプロパティーを使用します。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * モデルで使用するコネクション名
     *
     * @var string
     */
    protected $connection = 'connection-name';
}

モデルの取得

モデルと対応するデータベーステーブルを作成したら、データベースからデータを取得できるようになりました。各Eloquentモデルは、対応するテーブルのデータベースへすらすらとクエリできるようにしてくれるクエリビルダだと考えてください。例を見てください。

<?php

namespace App\Http\Controllers;

use App\Flight;
use App\Http\Controllers\Controller;

class FlightController extends Controller
{
    /**
     * 利用可能な全フライトの一覧を表示
     *
     * @return Response
     */
    public function index()
    {
        $flights = Flight::all();

        return view('flight.index', ['flights' => $flights]);
    }
}

カラム値に対するアクセス

Eloquentモデルインスタンスを入手したら、モデルのカラム値には対応するプロパティとしてアクセスできます。たとえばクエリの結果の各Flightインスタンスをループで取得し、nameカラムの値をechoしてみましょう。

foreach ($flights as $flight) {
    echo $flight->name;
}

制約の追加

Eloquentのallメソッドはモデルテーブルの全レコードを結果として返します。Eloquentモデルはクエリビルダとしても動作しますのでクエリに制約を付け加えることもでき、結果を取得するにはgetメソッドを使用します。

$flights = App\Flight::where('active', 1)
               ->orderBy('name', 'desc')
               ->take(10)
               ->get();

注目: Eloquentモデルはクエリビルダですから、クエリビルダで使用できる全メソッドを確認しておくべきでしょう。Eloquentクエリでどんなメソッドも使用できます。

コレクション

複数の結果を取得するallgetのようなEloquentメソッドは、Illuminate\Database\Eloquent\Collectionインスタンスを返します。CollectionクラスはEloquent結果を操作する多くの便利なクラスを提供しています。もちろんこのコレクションは配列のようにループさせることもできます。

foreach ($flights as $flight) {
    echo $flight->name;
}

結果の分割

数千のEloquentレコードを処理する必要がある場合はchunkコマンドを利用してください。chunkメソッドはEloquentモデルの「塊(chunk)」を取得し、引数の「クロージャ」に渡します。chunkメソッドを使えば大きな結果を操作するときのメモリを節約できます。

Flight::chunk(200, function ($flights) {
    foreach ($flights as $flight) {
        //
    }
});

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

注意 各チャンクごとにデータベースクエリは再実行されます。

カーソルの使用

cursorメソッドにより、カーソルを使用し、データベースレコード全体を繰り返し処理できます。大量のデータを処理する場合、cursorメソッドを使用すると、大幅にメモリ使用量を減らせるでしょう。

foreach (Flight::where('foo', 'bar')->cursor() as $flight) {
    //
}

1モデル/集計の取得

もちろん指定したテーブルの全レコードを取得することに加え、findfirstを使い1レコードだけを取得できます。モデルのコレクションの代わりに、これらのメソッドは1モデルインスタンスを返します。

// 主キーで指定したモデル取得
$flight = App\Flight::find(1);

// クエリー条件にマッチした最初のレコード取得
$flight = App\Flight::where('active', 1)->first();

また、主キーの配列をfindメソッドに渡し、呼び出すこともできます。一致したレコードのコレクションが返されます。

$flights = App\Flight::find([1, 2, 3]);

Not Found例外

モデルが見つからない時に、例外を投げたい場合もあります。これは特にルートやコントローラーの中で便利です。findOrFailメソッドとクエリの最初の結果を取得するfirstOrFailメソッドは、該当するレコードが見つからない場合にIlluminate\Database\Eloquent\ModelNotFoundException例外を投げます。

$model = App\Flight::findOrFail(1);

$model = App\Flight::where('legs', '>', 100)->firstOrFail();

この例外がキャッチされないと自動的に404HTTPレスポンスがユーザに送り返されますので、これらのメソッドを使用すればわざわざ明確に404レスポンスを返すコードを書く必要はありません。

Route::get('/api/flights/{id}', function ($id) {
    return App\Flight::findOrFail($id);
});

集計の取得

もちろんクエリビルダが提供しているcountsummaxや、その他の集計関数を使用することもできます。これらのメソッドは完全なモデルインスタンスではなく、最適なスカラー値を返します。

$count = App\Flight::where('active', 1)->count();

$max = App\Flight::where('active', 1)->max('price');

モデルの追加と更新

基本の追加

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

<?php

namespace App\Http\Controllers;

use App\Flight;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class FlightController extends Controller
{
    /**
     * 新しいflightインスタンスの生成
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        // リクエストのバリデート処理…

        $flight = new Flight;

        $flight->name = $request->name;

        $flight->save();
    }
}

この例では、やって来たHTTPリクエストのnameパラメーターをApp\Flightモデルインスタンスのname属性に代入しています。saveメソッドが呼ばれると新しいレコードがデータベースに挿入されます。saveが呼び出された時にcreated_atupdated_atタイムスタンプは自動的に設定されますので、わざわざ設定する必要はありません。

基本の更新

saveメソッドはデータベースで既に存在するモデルを更新するためにも使用されます。モデルを更新するにはまず取得する必要があり、更新したい属性をセットしてそれからsaveメソッドを呼び出します。この場合もupdated_atタイムスタンプは自動的に更新されますので、値を指定する手間はかかりません。

$flight = App\Flight::find(1);

$flight->name = 'New Flight Name';

$flight->save();

指定したクエリに一致する複数のモデルに対し更新することもできます。以下の例ではactiveで到着地(destination)がSan Diegoの全フライトに遅延(delayed)のマークを付けています。

App\Flight::where('active', 1)
          ->where('destination', 'San Diego')
          ->update(['delayed' => 1]);

updataメソッドは更新したいカラムと値の配列を受け取ります。

複数代入

一行だけで新しいモデルを保存するにはcreateメソッドが利用できます。挿入されたモデルインスタンスがメソッドから返されます。しかしこれを利用する前にEloquentモデルを複数代入から保護するために、モデルへfillableguarded属性のどちらかを設定する必要があります。

複数代入の脆弱性はリクエストを通じて予期しないHTTPパラメーターが送られた時に起き、そのパラメーターはデータベースのカラムを予期しないように変更してしまうでしょう。たとえば悪意のあるユーザがHTTPパラメーターでis_adminパラメーターを送り、それがモデルのcreateメソッドに対してマップされると、そのユーザは自分自身を管理者(administrator)に昇格できるのです。

ですから最初に複数代入したいモデルの属性を指定してください。モデルの$fillableプロパティで指定できます。たとえば、Flightモデルの複数代入でname属性のみ使いたい場合です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * 複数代入する属性
     *
     * @var array
     */
    protected $fillable = ['name'];
}

複数代入する属性を指定したら、新しいレコードをデータベースに挿入するためにcreateが利用できます。createメソッドは保存したモデルインスタンスを返します。

$flight = App\Flight::create(['name' => 'Flight 10']);

$fillableが複数代入時における属性の「ホワイトリスト」として動作する一方、$guardedの使用を選ぶことができます。$guardedプロパティーは複数代入したくない属性の配列です。配列に含まれない他の属性は全部複数台入可能です。そのため$guardedは「ブラックリスト」として働きます。もちろん$fillable$guardedのどちらか一方を使用してください。両方一度には使えません。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * 複数代入しない属性
     *
     * @var array
     */
    protected $guarded = ['price'];
}

この例ではprice以外の全属性で複数代入ができます。

他の生成メソッド

他にも属性の複数代入可能な生成メソッドが2つあります。firstOrCreatefirstOrNewです。firstOrCreateメソッドは指定されたカラム/値ペアでデータベースレコードを見つけようとします。モデルがデータベースで見つからない場合、指定された属性でレコードが挿入されます。

firstOrNewメソッドもfirstOrCreateのように指定された属性にマッチするデータベースのレコードを見つけようとします。しかしモデルが見つからない場合、新しいモデルインスタンスが返されます。firstOrNewが返すモデルはデータベースに保存されていないことに注目です。保存するにはsaveメソッドを呼び出す必要があります。

// 属性のフライトを取得するか、存在しなければ作成する
$flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);

// 属性のフライトか、存在しなければ新しいインスタンスを取得する
$flight = App\Flight::firstOrNew(['name' => 'Flight 10']);

モデル削除

モデルを削除するには、モデルに対しdeleteメソッドを呼び出します。

$flight = App\Flight::find(1);

$flight->delete();

キーによる既存モデルの削除

上記の例ではdeleteメソッドを呼び出す前にデータベースからモデルを取得しています。しかしモデルの主キーが分かっている場合なら、モデルを取得せずに削除できます。destroyメソッドを呼び出してください。

App\Flight::destroy(1);

App\Flight::destroy([1, 2, 3]);

App\Flight::destroy(1, 2, 3);

クエリによるモデル削除

もちろん一連のモデルをクエリを実行し削除することもできます。次の例はactiveではない印を付けられたフライトを削除しています。

$deletedRows = App\Flight::where('active', 0)->delete();

ソフトデリート

実際にデータベースからレコードを削除する方法に加え、Eloquentはモデルの「ソフトデリート」も行えます。モデルがソフトデリートされても実際にはデータベースのレコードから削除されません。代わりにそのモデルにdeleted_at属性がセットされ、データベースへ書き戻されます。モデルのdeleted_atの値がNULLでない場合、ソフトデリートされています。モデルのソフトでリートを有効にするにはモデルにIlluminate\Database\Eloquent\SoftDeletesトレイトを使い、deleted_atカラムを$datesプロパティに追加します。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Flight extends Model
{
    use SoftDeletes;

    /**
     * 日付へキャストする属性
     *
     * @var array
     */
    protected $dates = ['deleted_at'];
}

もちろんデータベーステーブルにもdeleted_atカラムを追加する必要があります。Laravelスキーマビルダにはこのカラムを作成するメソッドが存在しています。

Schema::table('flights', function ($table) {
    $table->softDeletes();
});

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

指定されたモデルインスタンスがソフトデリートされているかを確認するには、trashedメソッドを使います。

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

ソフトデリート済みモデルのクエリ

ソフトデリート済みモデルも含める

前述のようにソフトデリートされたモデルは自動的にクエリの結果から除外されます。しかし結果にソフトデリート済みのモデルを含めるように強制したい場合は、クエリにwithTrashedメソッドを使ってください。

$flights = App\Flight::withTrashed()
                ->where('account_id', 1)
                ->get();

withTrashedメソッドはリレーションのクエリにも使えます。

$flight->history()->withTrashed()->get();

ソフトデリート済みモデルのみの取得

onlyTrashedメソッドによりソフトデリート済みのモデルのみを取得できます。

$flights = App\Flight::onlyTrashed()
                ->where('airline_id', 1)
                ->get();

ソフトデリートの解除

時にはソフトデリート済みのモデルを「未削除」に戻したい場合も起きます。ソフトデリート済みモデルを有効な状態に戻すには、そのモデルインスタンスに対しrestoreメソッドを使ってください。

$flight->restore();

複数のモデルを手っ取り早く未削除に戻すため、クエリにrestoreメソッドを使うこともできます。

App\Flight::withTrashed()
        ->where('airline_id', 1)
        ->restore();

withTrashedメソッドと同様、restoreメソッドはリレーションに対しても使用できます。

$flight->history()->restore();

モデルの完全削除

データベースからモデルを本当に削除する場合もあるでしょう。データベースからソフトデリート済みモデルを永久に削除するにはforceDeleteメソッドを使います。

// 1モデルを完全に削除する
$flight->forceDelete();

// 関係するモデルを全部完全に削除する
$flight->history()->forceDelete();

クエリスコープ

グローバルスコープ

グローバルスコープにより、指定したモデルのクエリに対して、制約を付け加えることができます。Laravel自身のソフトデリート機能は、「削除されていない」モデルをデータベースから取得するためにグローバルスコープを使用しています。独自のグローバルスコープを書くことにより、特定のモデルのクエリに制約を確実に、簡単に、便利に指定できます。

グローバルスコープの記述

グローバルスコープは簡単に書けます。Illuminate\Database\Eloquent\Scopeインターフェイスを実装したクラスを定義します。このインターフェイスは、applyメソッドだけを実装するように要求しています。applyメソッドは必要に応じ、where制約を追加します。

<?php

namespace App\Scopes;

use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class AgeScope implements Scope
{
    /**
     * Eloquentクエリビルダへ適用するスコープ
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @return void
     */
    public function apply(Builder $builder, Model $model)
    {
        return $builder->where('age', '>', 200);
    }
}

デフォルトのLaravelアプリケーションには、スコープのためのフォルダは用意されていません。Laravelアプリケーションのappディレクトリ下に、自由にScopesフォルダーを作成してください。

グローバルスコープの適用

モデルにグローバルスコープを適用するには、そのモデルのbootメソッドをオーバライドし、addGlobalScopeメソッドを呼び出します。

<?php

namespace App;

use App\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * モデルの「初期起動」メソッド
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope(new AgeScope);
    }
}

スコープを追加した後から、User::all()は以下のクエリを生成するようになります。

select * from `users` where `age` > 200

クロージャによるグローバルスコープ

Eloquentではクロージャを使ったグローバルスコープも定義できます。独立したクラスを使うだけの理由がない、簡単なスコープを使いたい場合、特に便利です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class User extends Model
{
    /**
     * モデルの「初期起動」メソッド
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope('age', function(Builder $builder) {
            $builder->where('age', '>', 200);
        });
    }
}

addGlobalScopeの最初の引数は、スコープを削除する時に使用する識別子です。

User::withoutGlobalScope('age')->get();

グローバルスコープの削除

特定のクエリからグローバルスコープを削除した場合は、withoutGlobalScopeメソッドを使います。

User::withoutGlobalScope(AgeScope::class)->get();

複数、もしくは全部のグローバルスコープを削除したい場合も、withoutGlobalScopesメソッドが使えます。

User::withoutGlobalScopes()->get();

User::withoutGlobalScopes([FirstScope::class, SecondScope::class])->get();

ローカルスコープ

ローカルスコープによりアプリケーション全体で簡単に再利用可能な、一連の共通制約を定義できます。例えば、人気のある(popular)ユーザを全員取得する必要が、しばしばあるとしましょう。スコープを定義するには、scopeを先頭につけた、Eloquentモデルのメソッドを定義するだけです。

スコープはいつもクエリビルダインスタンスを返します。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 人気のあるユーザだけに限定するクエリスコープ
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopePopular($query)
    {
        return $query->where('votes', '>', 100);
    }

    /**
     * アクティブなユーザだけに限定するクエリスコープ
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeActive($query)
    {
        return $query->where('active', 1);
    }
}

クエリスコープの使用

スコープが定義できたらモデルのクエリ時にスコープメソッドを呼び出せます。しかしメソッドを呼び出すときにscopeプレフィックスを付ける必要はありません。様々なスコープをチェーンでつなぎ呼び出すこともできます。例を見てください。

$users = App\User::popular()->active()->orderBy('created_at')->get();

動的スコープ

引数を受け取るスコープを定義したい場合もあるでしょう。スコープにパラメーターを付けるだけです。スコープパラメーターは$query引数の後に定義しする必要があります。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 指定したタイプのユーザーだけを含むクエリーのスコープ
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeOfType($query, $type)
    {
        return $query->where('type', $type);
    }
}

これでスコープを呼び出すときにパラメーターを渡せます。

$users = App\User::ofType('admin')->get();

イベント

Eloquentモデルは多くのイベントを発行します。creatingcreatedupdatingupdatedsavingsaveddeletingdeletedrestoringrestoredのメソッドを利用し、モデルのライフサイクルの様々な時点をフックすることができます。イベントにより特定のモデルクラスが保存されたりアップデートされたりするたび、簡単にコードを実行できるようになります。

基本的な使用法

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

例としてサービスプロバイダでEloquentイベントリスナを定義してみましょう。イベントリスナの中で指定されたモデルに対しisValidメソッドを呼び出し、モデルが有効でなければfalseが返されます。Eloquentイベントリスナがfalseを返すとsaveupdate操作はキャンセルされます。

<?php

namespace App\Providers;

use App\User;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * アプリケーションサービスの初期起動処理
     *
     * @return void
     */
    public function boot()
    {
        User::creating(function ($user) {
            if ( ! $user->isValid()) {
                return false;
            }
        });
    }

    /**
     * サービスプロバイダの登録
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

ドキュメント章別ページ

ヘッダー項目移動

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

その他

?

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