Laravelによるアプリ開発のための逆引きTips

Laravelは非常に使いやすいフレームワークですが、いざ仕事でアプリケーションを実装していくと、Laravelのお作法に従うことが推奨されるケースが多く、機能も膨大なので、当然ですが分からない事が次々出てきます。
この記事では備忘録も兼ね、逆引きTips的にLaravelでの各種実装方法を紹介していきます。
※記事執筆時に使っているLaravelのバージョンは5.7です。

## 紹介している項目
* Storage
* View
* Controller
* Route
* Database (Eloquent ORM)
* Validation
* その他

※加筆更新修正中

## Storage

### ファイルの存在をチェックしたい
``try/catch``を使ってファイルが存在するかチェックします。

```php
try {
    $file = Storage::get('images/hoge.png');
    return $file;

} catch (FileNotFoundException $exception) {
    return 'File Not Found';
}
```

コメント欄で@sola-msrさんに``exits``でもチェックできることをご指摘頂きました。こちらのほうが記述がシンプルですね。`bool`が返されます。

```php
Storage::exists('images/hoge.png');
// or
Storage::disk('s3')->exists('images/hoge.png'); // diskを指定してチェックする場合
```

### ファイルを保存したい

```php
// $requestからphotoというblob値を受け取っている前提。`storage/app/images`に画像が保存される
$filename = date('Ymd-His'). '.png';
$path = $request->photo->storeAs('images', $filename);
```

### Storageのファイルを削除したい
``storage/app``から先のディレクトリパスを指定して画像を削除します。

```php
Storage::delete('images/' . $filename);
```

### 画像をbase64で出力したい

```php
// `storage/app/images/hoge.png`をbase64に変換する
return base64_encode(Storage::get('images/' . $filename));
```


### 指定の全ファイルを取得したい

```php
// 指定したディレクトリ`storage/app/images/`の全ファイルの配列を取得
Storage::files('images');

// 指定したディレクトリ`storage/app/images/`のサブディレクトリを含めた全てのファイルの配列を取得
Storage::allFiles('images');
```

### 指定の全ディレクトリを取得したい

```php
// 指定したディレクトリ`storage/app/images/`の全ディレクトリの配列
Storage::directories('images');

// サブディレクトリ下の全ディレクトリも取得
Storage::allDirectories('images');
```

### ディレクトリの作成・削除をしたい

```php
// `storage/app/images/`を作成
Storage::makeDirectory('images');

// 削除（フォルダ内のファイルは削除される）
Storage::deleteDirectory('images');
```

## View

### リダイレクト先に値を渡したい
``with``で値を渡すことでsessionから値を受け取れます。

```php:ResultController.php
return redirect('/contact/complete')->with([
        'memberID'=> $memberID
      ]);
```

```php:result.blade.php
<dl>
    <dt>会員No.</dt>
    <dd><strong>{{ session('memberID') }}</strong></dd>
</dl>
```

### Sessionの値で値を出し分けたい

```php
@if(Session::has('message'))
<div class="alert alert-waring">
    <strong>{{ session('message') }}</strong>
</div>
@endif
```



### POST直前の入力値を復元したい

フォームの完了画面から入力画面に戻るときなどに使うことが多いです。テキスト入力などのinputタグは`old`のみで復元できます。

```php
<p>{{ old('message') }}</p>
```

### セレクトボックスでPOST前の画面に戻った際、値を復元したい

```php
<select name="year">
    <option @if(old('year')=='2018') selected @endif>2018</option>
    <option @if(old('year')=='2019') selected @endif>2019</option>
    <option @if(old('year')=='2020') selected @endif>2020</option>
</select>
```

### チェックボックスでPOST前の画面に戻った際、値を復元したい

`in_array`でチェックした値でcheckedを出し分けます。

```php
<label>
    <input type="checkbox" name="tasks[]" value="1" {{ (is_array(old('tasks')) and in_array(1, old('tasks'))) ? ' checked' : '' }}> task1
</label>
```

## Controller

### Requestの値を全て取得したい
POSTされた内容を全て表示するときなどに利用します。

```php
$request->all();
```

### Requestの値から特定の値を取得したい

```php
$request->user_name;
// or
$request['user_name'];
```

## Route

### CRUDのAPIを設定したい
makeコマンドでcontrollerを作る時に``--resource``をオプションで付けるだけ。

```bash
$ php artisan make:controller HogeController --resource
```

routes/api.phpに``Route::resource``でRouteを指定します。

```php
// CRUD全てを使いたい場合
Route::resource('sample', 'SampleController');

// CRUDの一部機能のみを使う場合
Route::resource('sample', 'SampleController', ['only' => ['index', 'create', 'edit', 'destroy']]);
```

### ページネーション用データを取得したい
LaravelのCollectionクラスには``pagenate``メソッドが用意されており、ページネーション用のJSON書き出しをサポートしています。

```php
Route::get('/users', function () {
    return new UserCollection(User::paginate());
});
```

## Database (Eloquent ORM)

### Table内のすべてのデータを取得したい

```php
User::all();
```

### Table内の最新の1件を取得したい
もっともシンプルな方法。`find`の引数はテーブル初期値に含まれている`id`を使います。

```php
User::find(1);
```

### 指定キーに該当する1件だけを取得したい
主にuserテーブルなど、レコード1件1件がユニークな時などに利用します。

```php
User::where('member_id', $member_id)->first()
```

### データの作成順に取得したい（降順・昇順）
標準のtableに含まれる`created_at`をキーにソートして取得します。

```php
# 昇順
User::orderBy('created_at', 'desc')->get()

# 降順
User::orderBy('created_at', 'asc')->get()
```

### 古いデータ順に取得したい（昇順）
引数を指定しない場合、'created_at'がデフォルトで指定されます。

```php
User::oldest('created_at')->get();
```

### 指定する条件に一致するレコードが存在するかチェックしたい

```php
User::where('member_id', $member_id)->exists() // boolを返す
```

### 新しくレコードを保存したい（store）
Model`User`を作って`use`していることが前提です。`save()`することで簡単にデータベースに保存できます。定義された値が一つでも無いとエラーが出るので注意です。

```php
$user = new User();

foreach ($request as $key => $value) {
    $user[$key] = ($value != null)? $value : '';
}

$user->save();
```

### 指定キーに該当するレコードを更新したい（update）
``->first()``などをつけるとうまく動作しない時がありました。基本ドキュメントに書かれているように、クエリビルダー単体に``update``付けて実行してください。

```php
User::where('member_id', $member_id)->update(['name' => 'yamada'])
```

### 指定キーに該当するレコードを削除したい（destory）

```php
$user = User::where('member_id', $member_id)->first();
$user->delete();
```

## Validation

### validateで使えるルールを知りたい
公式サイト、またはQittaの以下サイトで確認できます。Qittaの下記記事はテーブルで一覧表記となっており、ルールを探しやすいです。

* [Available Validation Rules - Laravel公式サイト](https://laravel.com/docs/5.7/validation#available-validation-rules)
* [Laravelのバリデーションで指定できる内容をざっくりまとめ直しました]
* (https://qiita.com/fagai/items/9904409d3703ef6f79a2)

### オプションフィールドにもバリデーションをかけたい
オプションフィールド（任意の入力）にもバリデーションをかけたい時がありますが、入力した後に値を削除、再び入力してバリデーションをかけると空入力になっているのにバリデーションが働きエラーが起きるケースがあります。原因は$request値が`null`となり、他のバリデートがnullに対して行われるからです。そんな場合は`nullable`を設定することで対処できます。

```php
$validatedData = $request->validate([
    'key1' => 'required|numeric', //必須の入力値かつ数値のみを許容
    'key2' => 'nullable|numeric', //任意の入力値かつ数値のみを許容
]);
```

### validateでカスタムのバリデーションを作りたい
``AppServiceProvider.php``に登録することで拡張が可能です。ここでは``ひらがな表記``のカスタムバリデーションを作る前提で説明します。
設置場所は問いませんが、バリデーション用のClassファイルを作成します。以下の例では``App\Validator``配下に``RuleValidator.php``というファイルを新規で設置します。

```php:app/Validator/RuleValidator.php
namespace App\Validator;

class RuleValidator {
    public function validateHiragana($attribute, $value, $parameters){
        if (mb_ereg('^[ぁ-ん]+$', $value)) { // ひらがなにのみtrueを返す
            return true;
        }
        return false;
    }
}
```

``AppServiceProvider.php``のbootにカスタムValidatorを登録します。

```php:app/Provider/AppServiceProvider.php
// ~~省略~~
public function boot()
{
    Validator::extend(
        'hiragana', 'App\Validator\RuleValidator@validateHiragana' // 無名関数でも書ける
    );
}
// ~~省略~~
```

エラーメッセージにも対応させるため、langフォルダ内（言語設定に合わせる）に含まれている``validation.php``にkeyとvalueを登録。これで作業は完了です。

```php:app/Resources/lang/ja/validation.php
// ~~省略~~
"hiragana" => ":attributeはひらがなを入力してください。",
// ~~省略~~
```

この例の場合、以下のように使用することができます。

```php

$request->validate([
  'first_kana' => 'required|hiragana',
]);
```

## その他

### Collection型からデータを取り出したい
取得したデータや作成データは、すべてEloquentのCollectionクラスで返ってきます。返ってきたデータを`toArray()`で配列化することも可能ですが、Collection型の場合は配列化しなくても、`foreach`で取得が可能です。

```php
$members = $request;

foreach($members as $member) {
  dump($member->name);
}
```

### Collection型から指定keyデータを除外する
`request`からキーを指定して除外することが可能です。以下の例はフォーム値から必要のないデータを除外しています。

```php
$input = $request->except('action', '_token');
```

### blobデータをviewで受け取って、storageへ画像として保存したい
手前味噌ですが、以下記事を参考にしてください。JSでblob化、blob化したデータをLaravel APIへPOSTしています。

[axiosでPOSTした画像をLaravel APIで保存する](https://qiita.com/kgsi/items/0a85766ae04799a1174e)