Eloquent(ORM)を利用するメモ(その1)
Eloquentつか(エロクアント)とはLaravelのORM(オブジェクト関係マッピング)の仕組みのことです。
ORMとは
データベースのレコードをオブジェクトとして扱える仕組みのことのようですが、いまいちピンときません。。。
Eloquentの使い方
Eloquentを使ってみます。
以下のようなusers
テーブルがあるとします。
id | name | 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); } }
表示は先程と同じです。