Laravel5 チュートリアル ブログもどきを作る(3) ブログ記事を保存する

それでは[前回](https://qiita.com/yumgoo17/items/55152da02372dd203bcb)に引き続いて、今回はフォームに入力したブログ記事の内容を、データベースに保存する処理を書いていきたいと思います。



## ルーティングの追加
ブログ記事を保存する処理のURLと、それを実行するメソッドを設定します。
`app/routes/web.php` を開いて、下記の一文を追加します。

```app/routes/web.php
Route::post('admin/post', 'AdminBlogController@post')->name('admin_post');
```

`http://{ホスト名}/admin/post` に POST でアクセスすると、`AdminBlogController` の `post` メソッドを実行する、という意味になります。



## リクエストクラスの作成（バリデーションの設定）
ここでは「フォームリクエスト」を使ってバリデートを行います。下記 artisan コマンドを打って、`AdminBlogRequest` というリクエストクラスを作成します。

```
php artisan make:request AdminBlogRequest
```

そうすると、`app/Http/Requests/AdminBlogRequest.php` が作成されるので、それを開いて下記のように編集します。

```app/Http/Requests/AdminBlogRequest.php
class AdminBlogRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        // ユーザーがこのリクエストの権限を持っているかどうかを判断する
        // ここではひとまず true にしておく
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        // バリデーションルールはここに追加する
        // 項目名 => ルールという形式で、ルールが複数ある場合は '|' で区切る
        return [
            'post_date' => 'required|date',                 // 必須・日付
            'title'     => 'required|string|max:255',       // 必須・文字列・最大値（255文字まで）
            'body'      => 'required|string|max:10000',     // 必須・文字列・最大値（10000文字まで）
        ];
    }

    public function messages()
    {
        // 表示されるバリデートエラーメッセージを個別に編集したい場合は、ここに追加する
        // 項目名.ルール => メッセージという形式で書く
        // プレースホルダーを使うこともできる
        // 下記の例では :max の部分にそれぞれ設定した値（255, 10000）が入る
        return [
            'post_date.required' => '日付は必須です',
            'post_date.date'     => '日付は日付形式で入力してください',
            'title.required'     => 'タイトルは必須です',
            'title.string'       => 'タイトルは文字列を入力してください',
            'title.max'          => 'タイトルは:max文字以内で入力してください',
            'body.required'      => '本文は必須です',
            'body.string'        => '本文は文字列を入力してください',
            'body.max'           => '本文は:max文字以内で入力してください',
        ];
    }
}
```

他にどういうバリデーションルールがあるかは、[日本語ドキュメント](https://readouble.com/laravel/5.5/ja/validation.html)を参照してください。入力値が配列型のときもバリデートできますし、ある条件を満たしたときにバリデートを行う、ということなども可能です。

設定したフォームリクエストバリデーションは、下記のようにコントローラーのメソッドの引数に、タイプヒントで指定すると、メソッド実行前に、バリデートが実行されます。

```php
public function someMethod(AdminBlogRequest $request)
{
	// バリデートOKなら、このメソッドが実行される
    // バリデートNGなら、このメソッドは実行されずに、元のアドレスにリダイレクトされる
    // その際、エラー内容が自動的にフラッシュデータとしてセッションに保存される
}
```

引数のタイプヒントに指定するだけで、バリデーションを実行してくれるので便利ですし、コントローラーにごりごりとバリデーションの設定を書かなくて良く、コードもスッキリするのでオススメです。



## モデルの作成
続いて DB にアクセスするためのモデルクラスを作成します。Laravel では Eloquent ORM と呼ばれる、非常に強力なORマッパーが用意されています。

マイグレーションファイルを作成するときに、一緒にモデルクラスファイルも作成したので、すでに `app/Article.php` というファイルが存在すると思います。`app` ディレクトリ配下に `Models` というディレクトリを作成して、こちらに移動しておきましょう。移動後は `app/Models/Article.php` というパスになるはずです。

それでは、このファイルを開いて、下記のように編集します。

```app/Models/Article.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    // 対象テーブルのプライマリキーのカラム名を指定する。デフォルトは 'id' というカラム名が想定されている。
    protected $primaryKey = 'article_id';

    // 「複数代入」を利用するときに指定する。追加・編集可能なカラム名のみを指定する。
    // $guarded プロパティを利用すると、逆に、追加・編集不可能なカラムを指定できる。
    protected $fillable = ['post_date', 'title', 'body'];

    // $dates プロパティには、日時が入るカラムを設定する（日付ミューテタ）
    // そうすると、その値が自動的に Carbon インスタンスに変換される
    protected $dates = ['post_date', 'created_at', 'updated_at', 'deleted_at'];
}
```

「複数代入」というのは、引数に渡される配列のキー名と、テーブルのカラム名が一致する場合、一致するキーの値を、一致するカラムに保存するという処理です。この「複数代入」を利用するために、[前回](https://qiita.com/yumgoo17/items/55152da02372dd203bcb)フォームの各部品の名前を articles テーブルのカラム名と同じにしたというわけです。
変更するつもりのないカラムのデータが書き換えられるのを防ぐために、`$fillable` か `$guareded` を指定します。詳しくは[日本語ドキュメント](https://readouble.com/laravel/5.5/ja/eloquent.html)を参照してください。

日付[ミューテタ](https://readouble.com/laravel/5.5/ja/eloquent-mutators.html)のところで登場する Carbon についてですが、Carbon とは、PHP の DateTime クラスを拡張した日付操作ライブラリで、日付を操作するための便利なメソッドがたくさん用意されており、Laravel に最初から組み込まれています。ミューテタも Carbon もこれから追って紹介していきます。Carbon の詳細は[公式ドキュメント](https://carbon.nesbot.com/docs/)を参照してください。



## コントローラーの編集
コントローラーを編集し「複数代入」を使って、ブログ記事を保存する処理を書いていきます。`app/Http/Controllers/AdminBlogController.php` を開いて下記のように編集します。

```app/Http/Controllers/AdminBlogController.php
<?php

namespace App\Http\Controllers;

use App\Http\Requests\AdminBlogRequest;
use App\Models\Article;

class AdminBlogController extends Controller
{
	/** @var Article */
    protected $article;

    function __construct(Article $article)
    {
        // Article モデルクラスのインスタンスを作成
        // 「依存注入」により、コンストラクタの引数にタイプヒントを指定するだけで、
        // インスタンスが生成される（コンストラクターインジェクション）
        $this->article = $article;
    }

	...（中略）...

    /**
     * ブログ記事保存処理
     *
     * @param AdminBlogRequest $request
     * @return \Illuminate\Http\RedirectResponse
     */
    public function post(AdminBlogRequest $request)
    {
        // こちらも引数にタイプヒントを指定すると、
        // AdminBlogRequest のインスタンスが生成される（メソッドインジェクション）
        // そして、AdminBlogRequest で設定したバリデートも実行される（フォームリクエストバリデーション）

        // 入力値の取得
        $input = $request->input();

        // create メソッドで複数代入を実行する。
        // 対象テーブルのカラム名と配列のキー名が一致する場合、一致するカラムに一致するデータが入る
        $article = $this->article->create($input);

        // リダイレクトでフォーム画面に戻る
        // route ヘルパーでリダイレクト先を指定。ルートのエイリアスを使う場合は route ヘルパーを使う
        // with メソッドで、セッションに次のリクエスト限りのデータを保存する
        return redirect()->route('admin_form')->with('message', '記事を保存しました');
    }
}
```

いくつかポイントがありますが「依存注入」の詳細は[日本語ドキュメント](https://readouble.com/laravel/5.5/ja/controllers.html)の「依存注入とコントローラ」を参照してください。
また、DBへのデータ追加について、今回は「複数代入」を利用しましたが、他にも様々な方法がありますので、こちらも[日本語ドキュメント](https://readouble.com/laravel/5.5/ja/eloquent.html)を参照してください。
そして、リダイレクト先の指定に、ルートのエイリアスを使いました。ルートのエイリアスを設定しておくと、もし何らかに理由でURLを変更しなければならなくなっても、エイリアスに変更が無ければ、コントローラーなどでリダイレクトを設定している箇所を変更せずに済むという利点があります。



## View テンプレートの編集
バリデーションエラーと、リダイレクト時、セッションに保存されたデータを表示する処理を追加します。
`resources/views/admin_blog/form.blade.php` を開いて、`<body>` の中を下記のように編集します。

```html:resources/views/admin_blog/form.blade.php
<body>

<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <h2>ブログ記事投稿・編集</h2>

            {{--if 文による条件分岐--}}
            @if (session('message'))
                <div class="alert alert-success">
                    {{--セッションヘルパーを使ってキーを指定して、セッションに保存されたデータを取り出す--}}
                    {{ session('message') }}
                </div>
                <br>
            @endif

            {{--$errors は Illuminate\Support\MessageBag インスタンスで、エラーメッセージの操作に便利なメソッドを使うことができる--}}
            {{--バリデートエラーがあった場合は、自動的にエラー内容・メッセージが保存された状態で、元のアドレスにリダイレクトされる--}}
            @if ($errors->any())
                <div class="alert alert-danger">
                    <ul>
                        {{--foreach 文によるループ--}}
                        {{--エラーメッセージがあるなら、それを全て取り出して表示--}}
                        @foreach ($errors->all() as $error)
                            <li>{{ $error }}</li>
                        @endforeach
                    </ul>
                </div>
            @endif

            {{--変数を {{  }} で囲うと変数の内容が表示される。{{  }} の中にはPHPの関数を書くこともできる--}}
            {{--{{  }} で囲われると自動的に htmlspecialchars 関数が通されエスケープされる--}}
            <form method="POST" action="{{ route('admin_post') }}">
                <div class="form-group">
                    <label>日付</label>
                    <input class="form-control" name="post_date" size="20" value="" placeholder="日付を入力して下さい。">
                </div>

                <div class="form-group">
                    <label>タイトル</label>
                    <input class="form-control" name="title" value="" placeholder="タイトルを入力して下さい。">
                </div>

                <div class="form-group">
                    <label>本文</label>
                    <textarea class="form-control" rows="15" name="body" placeholder="本文を入力してください。"></textarea>
                </div>

                <input type="submit" class="btn btn-primary btn-sm" value="送信">
                {{--CSRFトークンが生成される--}}
                {{ csrf_field() }}
            </form>
        </div>
    </div>
</div>

</body>
```

途中、if文やforeach文といった制御構文が出てきますが、PHPとほぼ同じような感覚で使えることがわかると思います。

ここまでできたら、実際にフォームにデータを入力し、送信ボタンを押して、DBにデータが保存されるかどうか、バリデーションが効くかどうかを試してみてください。現時点で日付（post_date）は、DBに入れられるフォーマット `YYYY-MM-DD` にしないと、データが入らないので注意してください。

[次回は、既存ブログ記事の編集処理を実装していきます。](https://qiita.com/yumgoo17/items/57d3c1e4f56e15ec82f0)



## 参考資料
### Laravel 日本語ドキュメント
* [ルーティング](https://readouble.com/laravel/5.5/ja/routing.html)
* [バリデーション](https://readouble.com/laravel/5.5/ja/validation.html)
* [Eloquentの準備](https://readouble.com/laravel/5.5/ja/eloquent.html)
* [コントローラ](https://readouble.com/laravel/5.5/ja/controllers.html)
* [Bladeテンプレート](https://readouble.com/laravel/5.5/ja/blade.html)

### Carbon 
* [公式ドキュメント（英語）](https://carbon.nesbot.com/docs/)

### プログラムソース（github）
* [github yumgoo17/like_blog](https://github.com/yumgoo17/like_blog)