のぐそんブログ

暗いおじさんがシコシコ書くブログです。

Eloquent(ORM)を利用するメモ(その1)

Eloquentつか(エロクアント)とはLaravelのORM(オブジェクト関係マッピング)の仕組みのことです。

ORMとは

データベースのレコードをオブジェクトとして扱える仕組みのことのようですが、いまいちピンときません。。。

Eloquentの使い方

Eloquentを使ってみます。
以下のようなusersテーブルがあるとします。

id name mail age
2 佐藤一郎 sato@sample.com 30
7 鈴木次郎 jsuzuki@sample.com 40
8 田中太郎 ttaro@sample.com 21
9 加藤万次郎 kmanjirou@sample.com 15

まずModelクラスを作成します。
モデル名はテーブル名の単数形にします。
これはLaravelの命名規則になります。

php artisan make:model User

/app/User.phpが作成されます。

次にコントローラーを作成します。

php artisan make:controller UserController

/app/Http/Controllers/UserController.phpが作成されます。

コントローラーには以下の定義をします。

<?php

namespace App\Http\Controllers;
use App\User;

use Illuminate\Http\Request;

class UserController extends Controller
{
    public function index(Request $request)
    {
        $items = User::all();
        return view('user.index', ['items' => $items]);
    }
}

viewとrouteファイル以下のような感じです。

◎/resources/views/user/index.blade.php

<table>
    <tr>
        <th>name</th>
        <th>mail</th>
        <th>age</th>
    </tr>
    @foreach ($items as $item)
        <tr>
            <td>{{$item->name}}</td>
            <td>{{$item->mail}}</td>
            <td>{{$item->age}}</td>
        </tr>
    @endforeach
</table>

◎routes/web.php

Route::get('user','UserController@index');

表示は以下になります。

クエリビルダとの違い

クエリビルダを使うのと、Eloquentを使うので差がよくわからなかったので、データを取得してみます。

クエリビルダで取得したレコードデータ

DB::table('users')->get();で取得。

Collection {#210 ▼
  #items: array:4 [▼
    0 => {#222 ▼
      +"id": "2"
      +"name": "佐藤一郎"
      +"mail": "sato@sample.com"
      +"age": "30"
    }
    1 => {#221 ▶}
    2 => {#220 ▶}
    3 => {#217 ▶}
  ]
}

Eloquentで取得したレコードデータ

User::all()で取得。

Collection {#224 ▼
  #items: array:4 [▼
    0 => User {#225 ▼
      #connection: "sqlite"
      #table: "users"
      #primaryKey: "id"
      #keyType: "int"
      +incrementing: true
      #with: []
      #withCount: []
      #perPage: 15
      +exists: true
      +wasRecentlyCreated: false
      #attributes: array:4 [▼
        "id" => "2"
        "name" => "佐藤一郎"
        "mail" => "sato@sample.com"
        "age" => "30"
      ]
      #original: array:4 [▶]
      ... 中略...
    }
    1 => User {#226 ▶}
    2 => User {#227 ▶}
    3 => User {#228 ▶}
  ]
}

クエリビルダとEloquentで違う点は、Eloquentの場合コレクションに入っているデータはモデルクラスのインスタンスということです。
その為モデルクラス(この場合は/app/User.php)に、プロパティやメソッドを追加して拡張していくことができます。

クエリビルダ ・・・ DBのレコードの配列 Eloquent ・・・ モデルクラスのインスタンの配列

モデルクラスを拡張してみる

/app/User.phpを変更する。

    public function getName()
    {
        return '私の名前は' . $this->name . 'です。';
    }

viewはこんな感じ。

<table>
    @foreach ($items as $item)
        <tr>
            <td>{{$item->getName()}}</td>
        </tr>
    @endforeach
</table>    

表示はこんな感じ。

モデルクラスのメソッド

find

Eloquentではテーブルのidはプライマリキーの整数として扱います。
その前提でidでテーブルを検索するfindメソッドがあります。

使い方
$hoge = ModelClass::find(整数);

◎/app/Http/Controllers/UserController.php

class UserController extends Controller
{
    public function index(Request $request)
    {
        $item = User::find(2);
        return view('user.index', ['item' => $item]);
    }
}

viewはこんな感じ。 配列ではなくオブジェクトとして取得する。

<table>
    <tr>
        <th>name</th>
        <th>mail</th>
        <th>age</th>
    </tr>
        <tr>
            <td>{{$item->name}}</td>
            <td>{{$item->mail}}</td>
            <td>{{$item->age}}</td>
        </tr>
</table>

表示はこんな感じ。

where

細かい条件を指定して検索するにはwhereを利用します。

使い方

複数のレコードを取得する場合はgetを使う。

$hoge = ModelClass::where(フィールド名, 値)->get();

最初のレコードを取得する場合はfirstを使う。

$hoge = ModelClass::where(フィールド名, 値)->first();

◎/app/Http/Controllers/UserController.php

class UserController extends Controller
{
    public function index(Request $request)
    {
        $item = User::where('name','田中太郎');
        return view('user.index', ['item' => $item]);
    }
}

viewはこんな感じ。

<table>
    <tr>
        <th>name</th>
        <th>mail</th>
        <th>age</th>
    </tr>
        <tr>
            <td>{{$item->name}}</td>
            <td>{{$item->mail}}</td>
            <td>{{$item->age}}</td>
        </tr>
</table>

表示はこんな感じ。

演算子を利用する場合はこんな感じ。

class UserController extends Controller
{
    public function index(Request $request)
    {
        $items = User::where('age','>=',30)->get();
        return view('user.index', ['items' => $items]);
    }
}

表示はこんな感じ。

モデルクラスのスコープについて

スコープとはその名の通り、テーブルのレコードの中の特定の範囲を示すものです。
スコープにはローカルスコープグローバルスコープの2つがあります。

ローカルスコープ

モデル内にメソッドを用意し、必要に応じて呼び出します。
モデルクラスでフィールドを絞り込んだ値を返して利用するイメージです。

◎/app/User.php

class User extends Model
{
    public function scopeMailEqual($query, $str)
    {
        return $query->where('name', $str);
    }
}

◎/app/Http/Controllers/UserController.php

コントローラーからモデルクラスに定義したローカルスコープ用のメソッドを利用する場合は、メソッドのscopeの記述は不要です。
また、第1引数の$queryも呼び出し元では不要です。
初学者の私には、すごいモヤモヤしますが。。。

class UserController extends Controller
{
    public function index(Request $request)
    {        
        $items = User::mailEqual('田中太郎')->get();

        return view('user.index', ['items' => $items]);
    }
}

グローバルスコープ

グローバルスコープではモデルの全てのレコード取得にスコープが適応されます。
グローバルスコープではbootという初期化専用のメソッドがあります。

◎/app/User.php

モデルクラスのbootメソッドをオーバーライドします。

<?php

namespace App;

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

class User extends Model
{
    protected static function boot()
    {
        parent::boot();

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

コントローラーはこんな感じでusersテーブルのレコードを全件取得する。

class UserController extends Controller
{
    public function index(Request $request)
    {
        $items = User::all();
        return view('user.index', ['items' => $items]);
    }
}

以下のようにグローバルスコープで定義した条件が適応さらた結果が表示される。

汎用的なScopeクラスを作る

複数のモデルで利用するグローバルスコープはScopeクラスとして作成して利用します。

Scopeクラスはどこに配置しても問題ないです。
今回は/app/Scopes/ScopeUser.phpのように作成します。

<?php

namespace App\Scopes;

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


class ScopeUser implements Scope
{
    public function apply(Builder $builder, Model $model)
    {
        $builder->where('age' , '>', 20);
    }
}

モデルクラスは以下のようにaddGlobalScopeメソッドにScopeクラスを指定します。

<?php

namespace App;

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

class User extends Model
{
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope(new ScopeUser);
    }
}

表示は先程と同じです。