Eloquentと共に生きる2018

この記事は[Laravel Advent Calendar 2018 - Qiita](https://qiita.com/advent-calendar/2018/laravel)の22日目の記事です。

Laravel標準ORMであるEloquentとcollectionについて書きます。

- Eloquent、collectionの2018年追加された機能のTips
- Eloquent+collectionで実現する機能

#2018年追加された機能のTips
## Laravel5.5.29以降
### doesntExists()メソッドが追加されました

データ存在チェックで

```php
if( !User::where('email', 'user@example.com')->exists() ){
}
```
と書いてたものが

```php
if( User::where('email', 'user@example.com')->doesntExists()){
 }
```
と書けるようになりました。

### withTrashed()メソッドに引数指定が追加されました

`withTrashed()`メソッドは論理削除したデータを取得データに含める場合に使用します。

今回の変更により、元々以下のようにフラグで切り分けて使っていたのを

```php

public function index(Request $request)
{
    $query = User::query();

    if ($request->showDeleted) {
        $query->withTrashed();
    }

    return $query->get();
}
```

下記のようにwithTrashed()メソッドの引数に依存して使用する/しないを切り替える事が出来ます。

```php
public function index(Request $request)
{
    return User::withTrashed($request->showDeleted)->get();
}
```
## 5.6以降
### `Model`に`$casts`を定義することで取得時指定したデータ形式にキャストされます。

```php
protected $casts = [
    'birthday' => '誕生日:Y-m-d',
    'logined_at' => 'datetime:Y-m-d H:00',
];
```
### `orWhereDay()`、`orWhereMonth()`、`orWhereYear()`が追加されました

```php
\App\User::whereDay('updated_at', '=', 1)
    ->orWhereDay('updated_at', '=', 2); 
```

### `loadMissing()`メソッドが追加されました
リレーションがloadingしてない時だけloadingしてくれます。

```php

public function format(Book $book)
{
    // リレーション先のauthorのloading(loadingされてないときだけ)
    $book->loadMissing('author');

    return [
        'name' => $book->name,
        'author' => $book->author->name
    ];
}
```
## Laravel5.7以降

### ページネーションの中央のリンクに対するサイドの数を設定出来るようになりました。
デフォルトは3になります。

```php
// [1][2][3]...[6][7][8]【10】[11][12][13]...[20][21][22]
User::paginate(10)

// [1][2][3]...[9]【10】[11]...[20][21][22]
User::paginate(10)->onEachSide(1); 
```

### Collectionに`whenEmpty()`、`whenNotEmpty()`、`unlessEmpty()`、`unlessNotEmpty()`メソッドが追加されました

`whenEmpty()`の処理はコレクションが空の時にコールバックが呼ばれます。

```php

$collection = new Collection;

$collection->whenEmpty(function ($collection) {
    return $collection->push('test');
});

$this->assertSame(['test'], $collection->toArray());
```

`unlessEmpty()`の処理はコレクションが空でない時にコールバックが呼ばれます。

```php

$collection = new Collection;

$collection->unlessEmpty(function ($collection) {
    return $collection->push('abcd');
});

$this->assertSame([], $collection->toArray());
```

今年だけでも色々追加されてますね！
注意深く追っていかないと、気付かない機能もたくさんありそうです！！


#Eloquent+collectionで実現する機能
前提としてクエリで出来ることはクエリに任せる方が良いと思っているのですが、
データ量が少なくて再利用で済ませたい時やクエリ発行数を気にする時などは
collectionの機能でまかなう方が良い事も多々あると思います。

## `OrderBy`を`SortBy`や`sortByDesc`で
```php
$flights = App\Flight::where('active', 1)
               ->orderBy('name', 'desc')
               ->get();
```
上でやってることは下でも
出来ます。

```php
$flights = App\Flight::where('active', 1)
               ->get();
$sorted = $flights->sortByDesc('name');
```

## 単純に重複なしにするだけでいいなら全部取ってから`unique`でもいい
`orderBy`とか`DISTINCT`を意識せずにやる

```php
$comments = App\UserComment::get();
$commentCount = $comments->unique('user_id');
```

## ある条件で1レコード目だけ抜きたい時
下手に条件付けてクエリに書かずに、データ取ってきて`shift`とかするのもありかも

```php
$users = App\User::where('active', 1)->get();
$shiftUser = $users->shift();
```

## レコードを更新順に並べて取得して重複を無くす
取得したタイミングで一緒に`unique`までする。

```php
$comments = App\UserComment::orderBy('updated_at', 'desc')
                             ->get()
                             ->unique('user_id');
```

# 最後に
Laravelを使い始めた当初、collectionの機能の理解が乏しかったため、eloquentとcollectionを使ったベストプラクティスが出来てなかったと思います。

何でもクエリで取ってきて解決させれば良いという事はなくて、バランスよく各機能を使っていく判断が出来るよう情報のキャッチアップとtry&errorを続けていきたいと思います！

来年は[Laravel JP Conference](https://conference2019.laravel.jp/)も初の開催がありますし、もっとLaravelが盛り上がっていければと思います！！