【laravel】Eloquentをざっくり理解してみよう！

この記事は、[シアトルコンサルティング株式会社 Advent Calendar](https://qiita.com/advent-calendar/2021/seattleconsulting) 2021の20日目の記事です。

こんにちは、シアトルコンサルティングの 羽田野 と申します。
この度、弊社 シアトルコンサルティング株式会社 でAdvent Calendarに参加することになりました。

TeamTech Move the WorldをMissionに掲げ、日々全力で業務に取り組んでおります！
少しでも興味を持って頂けたら下記のサイトを覗いてみてください！

コーポレートサイト
https://www.seattleconsulting.co.jp/
Wantedly
https://www.wantedly.com/companies/seattleconsulting
よろしくお願い致します！

# はじめに
以前の現場でlaravelを使用していたのですが、自分自身なかなか使い勝手がわからず苦戦したので復習がてら書いてみました。
公式サイトをもとに、今回は操作というよりも「どんなものか」、「導入まではどうするか」にフォーカスして書きます。

## eloquentについて
Eloquentはlaravelに含まれるデータ用操作ライブラリ。
使用には`config/database.php`を確実に設定する必要がある。
ここはちょっと割愛…。機会があれば書いてみようかな。

## モデル定義
Eloquentを使うためにはEloquentモデルを作成する必要があり、基本的にはappディレクトリ以下に配置して利用する。
ただし、composer.jsonファイルで読み込むように指定した場所であれば、どこでも自由に配置できる。
全てのEloquentモデルは`Illuminate\Database\Eloquent\Model`を継承する必要がある。

一番簡単にモデルを作成できるのは`make:model`のArtisanコマンドを利用する。

```bash
php artisan make:model Users
```

※ArtisanコマンドとはLaravelに標準搭載されているCLI。ファイル作成やデータベース操作など様々な機能を含んでいる。ちなみに読み方は「アルチザン」らしい。
※composer.jsonとは依存正管理ツールのcomposerで用いる定義ファイルのこと

## モデルの規約
Eloquentモデルでは、明示的にtableプロパティでテーブル名を指定しない限りは、モデル名を複数系のスネークケースにしたものがテーブル名として使用される。

```php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

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

## 主キー
Eloquentモデルでは基本的に主キーを`id`というカラムであることを想定している。
主キーをid以外のカラムに指定したいとき(オーバーライドしたいとき)にはprotectedの`primaryKey`プロパティを定義する。
例えば、`primaryKey`に`hoge_id`を設定したい場合などは下記のようになります。

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * テーブルに関連付ける主キー
     *
     * @var string
     */
    protected $primaryKey = 'hoge_id';
}

```

加えて、Eloquentでは主キーがオートインクリメント（増分整数値）であることに関しても想定しています。
そのため、主キーに関しては非数値を入れた場合でも自動的に整数キャスト（型変換）が行われるので注意が必要です。
最近では主キーをuuid形式にするケースも増えてきたので、「インクリメントを行わない」、「整数値でない値を主キーにしたい」といった場合は、
下記のように明示的に示す必要があります。

```php
<?php

class Flight extends Model
{
    /**
     * モデルのIDを自動増分するか
     *
     * @var bool
     */
    public $incrementing = false;
    
    /**
     * 自動増分IDのデータ型
     *
     * @var string
     */
    protected $keyType = 'string';
}
```
※ 余談ですが、何故uuid形式が用いられるかというとオートインクリメントの場合に比べて、下記のことから自然とセキュア性向上が期待できるのが利点のようです。

- 生成アルゴリズムに用いるだけで限りなく一意なキーを生成できること
- 連番性をなくすことによって、割当られるIDの推測を困難にできるため、悪意ある第三者から推測しづらくできること。

## タイムスタンプ
Eloquentはデータベース上に存在するcreated_atとupdated_atカラムを自動更新する。
これらのカラムの自動更新をEloquentにさせない場合には、モデルの$timestampsプロパティをfalseに設定する。
タイムスタンプのフォーマットをカスタマイズする必要がある場合には、モデルの`$dateFormat`プロパティを設定する。
このプロパティはデータベースに保存される日付属性のフォーマットを決めるために使用されると同時に、配列やJSONへシリアライズされるときにも利用される。

```php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

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

タイムスタンプを保存するカラムをカスタマイズする必要がある場合には、モデルにCREATED_ATとUPDATED_AT定数を設定する。

```php
<?php

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

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

```php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Users extends Model
{
    /**
     * モデルで使用するコネクション名
     *
     * @var string
     */
    protected $connection = 'test-connection';
}
```
## デフォルト属性値
モデルの属性にデフォルト値を指定したい場合は、そのモデルに`$attributes`プロパティを定義する。

```php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Users extends Model
{
    /**
     * 属性に対するモデルのデフォルト値
     *
     * @var array
     */
    protected $attributes = [
        'account_available' => true,
    ];
}
```

# モデルの取得
モデルと対応するデータベースを作成したら、データベースからデータを取得できるようになる。各Eloquentモデルは対応するデータベーステーブルへクエリできるようにするクエリビルダのようなものである。

```php
<?php

$users = App\Users::all();

foreach ($users as $user) {
    echo $user->name;
}
```

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

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

## モデルのリフレッシュ
`fresh`と`refresh`メソッドを使用し、モデルをリフレッシュできる。
`fresh`メソッドはデータベースからモデルを再取得する。既存のモデルインスタンスは影響を受けない。

```php
$user = App\Users::where('name', 'hatano')->first();

$freshUser = $user->fresh();
```

`refresh`メソッドはデータベースから取得したばかりのデータを使用し、既存のモデルを再構築する。

```php
$user = App\Users::where('name', 'hatano')->first();

$flight->name = 'nagaoka';

$flight->refresh();

$flight->name; // "hatano"
```

## コレクション
複数の結果を取得する`all`や`get`のようなメソッドは`Illuminate\Database\Eloquent\Collection`インスタンスを返すことができ、`Collection`クラスはEloquentの結果を操作する複数のクラスを持ってます。

```php
$users = $users->reject(function ($user) {
    return $user->deleted;
});
```

また、コレクションは配列のようにループさせることも可能。

## 結果の分割
多くのデータを持つEloquentレコードを処理する場合は`chunk`コマンドを利用することでメモリの節約ができる。
`chunk`にはいくつのレコードを持つかを設定することができ、引数のクロージャにそれらを渡すことでデータベースの結果をチャンク（塊）ごとに処理するコードを記述することができる。
クロージャへ渡すチャンクを取得するたびに、データベースクエリは実行される。

```php
Users::chunk(200, function ($users) {
    foreach ($users as $user) {
        //
    }
});
```

## カーソルの使用
更に多くのデータを処理する場合には`cursor`メソッドを用いて、一つだけクエリを実行するカーソルを作成し、データベース全体に繰り返し処理を行うことも可能である。
`cursor`を使うことで大幅にメモリ使用量を減らすことができる。

```php
foreach (Users::where('foo', 'bar')->cursor() as $user) {
    //
}
```

#最後に
今回は公式リファレンスをもとに見ていったが、使ったことの無い機能多く有り、結構楽しみながら学習できたなと感じます。
特に自身の実装ではそこまで多くのデータを操作することはなかったが`chunk`,`cursor`といったメソッドは使ってみると面白そうだな…。
今後、また触る機会を作って見ようと思いました！

今回はここまでです！
ありがとうございました！