Eloquent をおさらい


Laravelでデータベースの操作を直感的に行えるEloquent。
断片的にしか理解していなかったので、基礎的なところからドキュメントをよく読んでおさらいしてみることにしました。

###注意点

EloquentORMについて始める前に、データベース接続の設定を先にしてください。

#1. Eloquentの基本

- Eloquentはデフォルトで `app` ディレクトリに置かれている
	-  `composer.json` を元に auto-load できるのであればどこにおいてもOK
- 全てのEloquentモデルは `Illuminate\Database\Eloquent\Model` を継承する

##作成方法

```
php artisan make:model User
```

一緒にマイグレーションファイルも作る場合は、 `--migration` か `-m` オプションをつける

```
php artisan make:model User --migration
php artisan make:model User -m
```

##サンプル

`flights` テーブルに紐づくモデル `Flight.php` を作るとする。

```php:Flight.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    //
}
```

##テーブルとの紐付け

`$table`プロパティでモデルとテーブルを紐づける。

```php:Flight.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'my_flights';
}
```

##主キー

Eloquent にデフォルトで設定されている主キー（Primary Key）は `id` 。

####主キーを変更する：`$primaryKey`

`id` を無効にして、他のものを主キーとして設定できる。

####インクリメントを無効にする：`$incrementing`

`false` にすると、インクリメントを無効にできる。

##タイムスタンプ

Eloquent はデフォルトだと自動的に `created_at` と `updated_at` を各テーブルに記録する。

####無効にする：`$timestamps`

`false` にすると、勝手にタイムスタンプがつかないようにできる。

####フォーマットを変える：`$dateFormat`

http://php.net/manual/ja/function.date.php なんかを参考に。

```php:Flight.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The storage format of the model's date columns.
     *
     * @var string
     */
    protected $dateFormat = 'U';
}
```

####カラム名を変更： `CREATED_AT` `UPDATED_AT`

モデル内で定数を設定する。

```php:Flight.php
<?php

class Flight extends Model
{
    const CREATED_AT = 'creation_date';
    const UPDATED_AT = 'last_update';
}
```

##データベース接続

基本的には `.env` で設定したDBに接続されるが、 `$connection` プロパティを使えば一部他のDBに接続が可能。

```php:Flight.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The connection name for the model.
     *
     * @var string
     */
    protected $connection = 'connection-name';
}
```

#2. データを取得してみる

Eloquentは強力な[クエリビルダ](https://laravel.com/docs/5.4/queries)として使える。

###全件を取得する

```php
<?php

use App\Flight;

$flights = App\Flight::all();

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

###いろいろ制約するとき

クエリビルダとしていろんな制約をするときは、最後に `get` メソッドで結果を取得する。

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

###`Collection`クラスを使う

Eloquent は`all`や`get`以外にも色んな形で結果を返すことができ、それらは`Illuminate\Database\Eloquent\Collection` インスタンスとして返ってくる。

####要らないものを排除して結果を返す：`reject`

```php
$collection = collect([1, 2, 3, 4]);

$filtered = $collection->reject(function ($value, $key) {
    return $value > 2;
});

$filtered->all();

// [1, 2]
```

ちなみにその逆は **`filter`** メソッド。

####分割して結果を返す： `chunk`

```php
$collection = collect([1, 2, 3, 4, 5, 6, 7]);

$chunks = $collection->chunk(4);

$chunks->toArray();

// [[1, 2, 3, 4], [5, 6, 7]]
```

####その他の`Collection`クラス

https://laravel.com/docs/5.4/eloquent-collections#available-methods

##単一レコードだけ取得する

###主キーを使う：`find`

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

####`find` メソッドは、複数レコードを指定して取得も可能

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

###他のキーを使う：`first`

```php
$flight = App\Flight::where('active', 1)->first();
```

###例外処理：`findOrFail` `firstOrFail`

この2つのメソッドを使うと、結果が返ってこなかった時に `Illuminate\Database\Eloquent\ModelNotFoundException` が投げられます。
`Exception` をキャッチされなければ `404` が自動的に返される。

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

##「数」を返す：`count` `sum` `max`

モデルインスタンスではなく「数」に関する結果だけが欲しい時につかえる。

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

#3. データを登録・更新してみる

##登録してみる

- インスタンスを `new` する
- 属性を `set` する
- `save` する

```php
<?php

namespace App\Http\Controllers;

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

class FlightController extends Controller
{
    /**
     * Create a new flight instance.
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        // Validate the request...

        $flight = new Flight;

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

        $flight->save();
    }
}
```

##更新してみる

- 登録とほぼ同じ手順
	- モデルを取ってくる
	- 属性を `set` する
	- `save` する

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

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

$flight->save();
```

###複数いっぺんに更新：`update`

更新するカラム名と値は配列にする。

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

##データが勝手に書き込まれるのを防ぐ：`fillable` `guarded`

別記事でまとめてます。

http://qiita.com/shosho/items/e44974563012f0bd0a61#massassignmentexception

##他のメソッド

###なければ作って登録する：`firstOrCreate`

DBに与えられたカラムと値が存在していなかったら、DBに挿入される。

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

$flight = App\Flight::firstOrCreate(
    ['name' => 'Flight 10'], ['delayed' => 1]
);
```

###なければインスタンスを作る：`firstOrNew`

`firstOrCreate` と似ているが、なければ与えられた属性を `set` してインスタンスが作成される。
**手動で `save` を呼びだす必要があるので注意。**

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

$flight = App\Flight::firstOrNew(
    ['name' => 'Flight 10'], ['delayed' => 1]
);
```

###あれば更新して、なければ登録する：`updateOrCreate`

```php
$flight = App\Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99]
);
```

#4. データを削除する

- 消したいものデータを取得
- `delete` する

```php
$flight = App\Flight::find(1);
$flight->delete();
```

###主キーを使って削除

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

###クエリーで削除

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

##論理削除

実際のデータは削除せず「削除したことにして」、この場合は `deleted_at` に削除した日時が入る。

###1. トレイト、`$dates`プロパティの実装

```php:Flight.php
<?php

namespace App;

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

class Flight extends Model
{
    use SoftDeletes;

    /**
     * The attributes that should be mutated to dates.
     *
     * @var array
     */
    protected $dates = ['deleted_at'];
}
```

###2. テーブルに`deleted_at`カラムを追加する

###3. メソッドを呼び出す

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

###論理削除したデータを抽出

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

#感想

- 削除の「Querying Soft Deleted Models」以降は目を通せなかった
	- `Getting Started` であっても知らないことはいっぱい
- 「Eloquentにあるなんか便利なやつ」が`Collection`クラスだと認識できた
	- `Collection` クラスについてもおさらいしたい
- Eloquentを使うときにはクエリビルダを多用することがわかった
	- クエリビルダについてもおさらいしたい

##参考
https://laravel.com/docs/5.4/eloquent