【Laravel入門】CRUD実装〜新規登録

# CRUDとは

Wikipediaより（一部抜粋）

>CRUD（クラッド）とは、ほとんど全てのコンピュータソフトウェアが持つ永続性[1]の4つの基本機能のイニシャルを並べた用語。その4つとは、Create（生成）、Read（読み取り）、Update（更新）、Delete（削除）である。ユーザインタフェースが備えるべき機能（情報の参照/検索/更新）を指す用語としても使われる。

どのようなアプリを作ろうとも必須になる機能だ。これから投稿機能を実装していくが、CRUDに沿って機能を追加していく。今回作成する機能は``Create（生成）``だ。

なお、Userとの連携（リレーション）については後で作業を行う。

# テーブル作成

作成するアプリは掲示板形式にしたい。まず、投稿記事を格納する``articlesテーブル``を作成する。

まずはマイグレーションファイルを作成する。

```
$ php artisan make:migration create_articles_table
Created Migration: 2018_05_03_141933_create_articles_table
```

次にマイグレーションファイルを編集する。デフォルトで作成されるidカラムとtimestamps()はそのままにし、以下のカラムを追記する。

- user_id（後にUserとの連携で必要になる。）
- title
- content

```2018_05_03_141933_create_articles_table.php
public function up()
{
    Schema::create('articles', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('user_id');
        $table->string('title');
        $table->string('content');
        $table->timestamps();
    });
}
   
public function down()
{
    Schema::dropIfExists('articles');
}
```

最後にマイグレーションを行う。これでarticlesテーブルが作成された。

```
$ php artisan migrate
Migrating: 2018_05_03_141933_create_articles_table
Migrated:  2018_05_03_141933_create_articles_table
```

#コントローラ作成

``Articleコントローラ``を作成する。

```
$ php artisan make:controller ArticleController
Controller created successfully.
```

これを下記の内容に編集する。

```ArticleController.php
public function add()
{
    return view('article.add');
}

public function create(Request $request)
{
    $article = new Article;
    $article->user_id = $request->user_id;
    $article->title = $request->title;
    $article->content = $request->content;
    $article->save();
    return redirect('/');
}
```

今回実装する機能はCRUDの``Create（生成）``つまり``articleの新規登録機能``だ。しかし、１つの機能が``add``と``create``の２つのアクションに別れている。これを説明していく。

## addアクション

- addアクションは``articleの新規登録画面を表示``させる。そのため``HTTPメソッド``は``GET``となる。
- ``GET``のため、アクション側で受け取る情報（ユーザーが作成した記事）、つまり``$request``に代入すべき情報がない。そのため引数は必要ない。
- 入力画面``'article.add'ビュー``を表示する。

## createアクション

- createアクションは``article作成に必要な項目を入力後、送信ボタンをクリックしその内容を送信した後``に``articlesテーブルにデータを格納する``ために行われる。そのため``HTTPメソッド``は``POST``となる。
- 引数に``Request $request``を指定すると、``Requestクラス``により作成された``HTTPリクエストインスタンス``が``$request``に代入される。当然``HTTPメソッド``も``HTTPリクエストインスタンス``に含まれている。
- そのため、``$request``には``POSTで受け取った情報（ユーザーが作成した記事）``が入っている。
- ``new Article``でインスタンスを新規作成し、``$article``へ代入する。
- ``$request``から``各プロパティ``を呼び出す。
- プロパティの呼び出しには``->（アロー演算子）``が使われている。
- 呼び出されたプロパティを``$articleの各プロパティとして代入``する。
- モデルの``saveメソッド``を実行し、内容をデータベースに書き込む。
- 上記の作業が終わったら、``'/（トップページ）'ビュー``へリダイレクトする。

createアクションは、下記のような書き方もできる。

```ArticleController.php
$article = new Article;
$form = $request->all();
unset($form['_token']);
$article->fill($form)->save();
return redirect('/');
```

- ``new Article``でインスタンスを生成し、``$article``へ代入する。
- モデルの``allメソッド``で``$request``から全てのプロパティを呼び出し、``$form``に代入する。
- 全てのプロパティを取得すると必要のない``_tokenプロパティ``まで取得してしまうので、モデルの``unsetメソッド``でこれを削除する。
- ``_tokenプロパティ``はセキュリティ対策の一環として作成されてる。
- モデルの``fillメソッド``で``$articleインスタンスのプロパティ``を$formの内容に指定する。
- モデルの``saveメソッド``を実行し、内容をデータベースに書き込む。
- ``'/（トップページ）'ビュー``へリダイレクトする。

ちなみに``fillメソッド``のプロパティ指定は、``$article->fill(['title' => 'test']);``のように配列形式で行うことが可能だ。

ここでモデルのメソッドが頻繁に使用されているが、これは後に作成するモデル``Article.php``の中で、``Articleクラス``が``class Article extends Model``の記述で``Modelクラス``を継承しているため、使用できるのだ。

# ルーティング設定

下記の内容を追記する。

同じ``'/article/add'``アドレスへアクセスしても、HTTPメソッドの違いでアクションを振り分けている。

```web.php
Route::get('/article/add', 'ArticleController@add')->name('article_add');
Route::post('/article/add', 'ArticleController@create')->name('article_create');
```

# ビュー作成

``resources/viewsディレクトリ``に``articleディレクトリ``を作成し、必要なファイルを作成する。正直、レイアウトは最低限以下だ。これは動作確認だと言い訳をしつつ、先に進む。

一見通常の入力フォームに見えるが、見慣れない一文がある。``{{ csrf_field() }}``とは何だろうか？詳細は公式ドキュメントで・・・

[Laravel 5.5 CSRF保護]
(https://readouble.com/laravel/5.5/ja/csrf.html)

公式ドキュメントより（一部抜粋）

>Laravelでは、クロス・サイト・リクエスト・フォージェリ(CSRF)からアプリケーションを簡単に守れます。クロス・サイト・リクエスト・フォージェリは悪意のあるエクスプロイトの一種であり、信頼できるユーザーになり代わり、認められていないコマンドを実行します。

>Laravelは、アプリケーションにより管理されているアクティブなユーザーの各セッションごとに、CSRF「トークン」を自動的に生成しています。このトークンを認証済みのユーザーが、実装にアプリケーションに対してリクエストを送信しているのかを確認するために利用します。

>アプリケーションでHTMLフォームを定義する場合はいつでも、隠しCSRFトークンフィールドをフォームに埋め込み、CSRF保護ミドルウェアがリクエストの有効性をチェックできるようにしなければなりません。トークン隠しフィールドを生成するには、csrf_fieldヘルパ関数を使ってください。

以下の記事もCSRFについてわかりやすく説明されている。しかも、例として挙げられているのはLaravelだ。

[PHPでCSRF対策の話](https://qiita.com/togana/items/ffc22d7ae4f221d1b3be)

先ほど作成された``_tokenプロパティ``はこの「トークン」のことだったのだ。

``{{ csrf_field() }}``のおかげで作成された``_tokenプロパティ``と、各フィールド（入力画面）に記述された内容が``titleプロパティ``、``contentプロパティ``の内容になり、POSTされてcreateアクションで処理されるのだ。

```resources/views/article/add.blade.php
@extends('...layouts.layout')

@section('article_add')
    投稿作成<br>

    <form action='{{ route('article_create') }}' method='post'>
        {{ csrf_field() }}
            ユーザーID：<input type='text' name='user_id'><br>
            タイトル：<input type='text' name='title'><br>
            内容：<input type='text' name='content'><br>
            <input type='submit' value='投稿'>
    </form>
@endsection
```

こちらの記述も忘れずに。

```resources/views/layouts/layout.blade.php
@yield('article_add')
```

# モデル作成

``Articleモデル``を作成する。

```
$ php artisan make:model Article
Model created successfully.
```

下記の内容に編集する。

```Article.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    protected $fillable = ['user_id', 'title', 'content'];
}

```

``protected $fillable = ['user_id', 'title', 'content'];``を記述しなくてもアプリは動くのだが、セキュリティのために記述しなければならない。管理者側からすれば、ユーザーから編集されては困る項目（プロパティ）もある。それらの項目を、あらかじめホワイトリストやブラックリストとして指定する機能だ。

##  ホワイトリストによる指定

- 入力できる項目を限定する。
- モデルの``$fillableプロパティ``を使用する。
- ``protected $fillable = ['user_id', 'title', 'content'];``と記述した場合、``Articleモデル``の``user_id属性、title属性、content属性のみ入力可能``となる。

## ブラックリストによる指定

- 入力できない項目を指定する。
- モデルの``$guardedプロパティ``を使用する。
- ``protected $guarded = ['id'];``と記述した場合、``Articleモデル``の``id属性の入力が不可能``となる。

ちなみに``protected``は``アクセス修飾子``と呼ばれるものだ。protected以下に記述したコードは、Articleクラスとサブクラスにのみ適用される。これはPHPの公式サイトで説明されている。

[PHP：アクセス権](http://php.net/manual/ja/language.oop5.visibility.php)

``protected $fillable``の``'user_id'``は、Userと連携させた後は必要がなくなる。今は値を空にすることができないため、とりあえず入力必須と設定しておく。

今回はここまで。