Laravelのドキュメントを読み解く 〜Eloquent ORM〜

# 前提
- WindowsでLaravelを使うための基本知識についてまとめています
- リファレンスなどをもとに学習したことを自分が理解しやすいように直してまとめています
- Laravel5.4のリファレンスをもとにしています
- まとめの粒度や順番は主観によるものです。省略している部分もあります

# 概要
- Laravelはデータベースの扱いがとても簡単にできるフレームワークです
- この記事ではLaravelの真骨頂とも言えるEloquentについてまとめています
- Eloquentを使うととても簡単にデータベースを使うことができます

# Eloquent ORMの利用
## 準備
- Eloquentを使うためにはモデルを作成する必要があります

		// モデルの作成コマンド（パスカルケース推奨）
		php artisan make:model モデル

- データベースとEloquentの接続
	- Laravelの規則に沿ってテーブル名などを命名していた場合、モデルを作成するだけでデータベースの操作を行うことができます
		- 主なルール
			- モデル名はパスカルケース
			- テーブル名はモデル名を小文字でスネークケース（複数形表記）
			- マイグレーションを使ってデフォルトのtimestampをカラムに入れる
			- 主キーとなるカラムの名称はidにする
	- 規則通りの命名にしていない場合
		- 規則にそっていない場合はすぐにはモデルとEloquentを繋ぐことができません。モデル内に下記の記述を行い、オーバーライドする必要があります

				// 繋ぐテーブル名がルール通りにしていない場合は必要
				protected $table = 'hoge_table';

				// デフォルトのタイムスタンプを使わない場合に必要
				public $timestamps = false;

				// 主キー（プライマリーキー）の名前を変更した場合は必要
				protected $primaryKey = 'hoge_id';
				
				// 自動増分ではない、もしくは整数値ではない主キーを使う場合に必要
				public $incrementing = false;
				
				// データベースの接続先をそのモデルだけ変更する場合に必要
				protected $connection = 'connection-name';

## Eloquentの使い方
- Eloquentモデルはクエリビルダとしても動作しますので、ほとんど使い方は同じです
- use App\Hogeを文頭などに書いてあげると、モデルを使う時の記述量をさらに減らすことができます

		// Hogeモデル（テーブル）からデータを取得する
		$flights = App\Hoge::where('active', 1)->orderBy('name', 'desc')->get();


## コレクション
- データベースから取得した結果が1件とは限らないような取得方法の場合（allやgetでの取得）、EloquentではCollectionとして値を返します
- Laravelにはコレクションを扱うための様々なメソッドが用意されています
	- https://readouble.com/laravel/5.4/ja/collections.html#method-all

## データの取得方法
- 複数

		// 配列のようにループさせて取得する
		foreach ($flights as $flight) {
		    echo $flight->name;
		}
		
		// 主キーから複数レコードを取得（結果はコレクションが返される）
		$flights = App\Flight::find([1, 2, 3]);

- 1つだけ取得
		// 主キーで指定したモデル取得
		$flight = App\Flight::find(1);
		
		// クエリー条件にマッチした最初のレコード取得
		$flight = App\Flight::where('active', 1)->first();

- 例外判定
	- find()、first()メソッドを使った場合は、取得できるレコード件数は0か１件です。0件だった場合に例外処理を行いたい場合はfindOrFail()、firstOrFail()を使用します。

			try {
			  $model = App\Flight::findOrFail(1);
			} catch (ModelNotFoundException $e) {
			  // Error処理
			}

## 大量のデータを扱う場合は
- 大量のデータを処理する際には、速度を優先する場合はcursor()、メモリ使用量が一定値を超えないようにしたい場合はchunk()を使うのが良さそうとのこと

- 検証結果
	- http://qiita.com/ryo511/items/ebcd1c1b2ad5addc5c9d

- 記述方法

		// chunk
		Flight::chunk(200, function ($flights) {
		    foreach ($flights as $flight) {
		        // 処理の中身
		    }
		});
		
		// カーソル
		foreach (Flight::where('foo', 'bar')->cursor() as $flight) {
		    // 処理の中身
		}		

## レコードの追加、更新、削除
- 1レコードの追加、更新、削除

		// 追加
		$flight = new Flight;
		$flight->name = $request->name;
		$flight->save();

		// 更新
		$flight = App\Flight::find(1);
		$flight->name = 'New Flight Name';
		$flight->save();
		
		// 削除
		User::find(1)->delete();

- 複数レコードの追加、更新

		// 追加
		$data = array(
		    array('name'=>'hoge1', 'age'=>'40'),
		    array('name'=>'hoge2', 'age'=>'20'),
		);
		User::insert($data);
		
		// 更新（updateメソッドの引数に更新したいカラムと値の配列を渡す）
		App\Flight::where('active', 1)
          ->where('destination', 'San Diego')
          ->update(['delayed' => 1]);
          
		// 削除
		App\Flight::where('active', 1)
		  ->where('destination', 'San Diego')
		  ->delete();
       
- 備忘録
	- 複数の追加や更新であってもループとsave()メソッドを使うことで代用することができる。複数代入問題の懸念などもあるのでこちらを推奨している人も多い。
	- insertはEloqumentのデフォルトのタイムスタンプを自動的に入れない
	- insertは$fillableもチェックしてくれない

                     
                                                               
## クエリースコープ
- 概要
	- 同じクエリーを書くのは面倒な作業で、コードの質を低下させる原因にもなります。Laravelにはそれを解決させるための方法としてクエリースコープという仕組みが用いられています。

- ローカルスコープ（特定のクエリーに対して制御を行う）
	- スコープの書き方（※モデル内に記述します）

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

	- 使い方

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

- グローバルスコープ（特定のモデル内の全クエリに対して制御を行う）
	- スコープの書き方（Illuminate\Database\Eloquent\Scopeインターフェイスを実装したクラスを新規で作成してその中に記述します）

			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)
			    {
			        $builder->where('age', '>', 200);
			    }
			}

	- 使い方（適用させるモデル内に記述します）

			protected static function boot()
			{
			    parent::boot();
			
			    static::addGlobalScope(new AgeScope);
			}





# イベント
- Laravelには、モデルが作られるタイミングや更新されるタイミングなどの時点をフックとして、特定の処理を発生させることができるイベントという機能があります

# アクセサとミューテター
- C#で言うところのgetterとsetterのようなもの
- 該当のカラムに対して値を取得したり、代入した時に何らかの処理をほどこすことができる

- アクセサ

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

- ミューテター

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


## 日付のミューテター
- 取得したデータをCarbonインスタンスへ変換します

		// 自分で作成したモデル内に記載し、オーバーライドする
		protected $dates = [
		    'created_at',
		    'updated_at',
		    'deleted_at'
		];

## 参考サイト
- データベースから取得したjsonを配列に変換する
	- https://readouble.com/laravel/5.4/ja/eloquent-mutators.html#array-and-json-casting
- コレクションに対する処理をオリジナルで作成する
	- https://readouble.com/laravel/5.4/ja/eloquent-collections.html#custom-collections
- モデルを使って取得したデータの変換
	- https://readouble.com/laravel/5.4/ja/eloquent-serialization.html
- Laravel5でのイベント処理と優先順位
	- http://qiita.com/sasakure-kei@github/items/e8bc1f02c93a4c2f7042
- フォームなどから送られてくるPOSTデータを使う場合の懸念点（複数代入とも言われている）
	- https://laravel-room.com/mass-assignment
- firstOrCreate と firstOrNewの違い
	- http://qiita.com/sutchan/items/eee8852269ac862669d9
- テーブルにレコードがあった場合Update、なかった場合Insertするベストな方法
	- http://syossan.hateblo.jp/entry/2014/09/02/122026
- DBデータ操作（DBファサード、クエリビルダ、Eloquent）
	- http://www.dn-web64.com/archives/web/db_createsql/

# バックナンバー
- Laravelのドキュメントを読み解く 〜開発準備〜
	- http://qiita.com/yunomi/items/682946d04a55c98c1036