1年前に作成したミニマムチュートリアル(Laravel5.1)をLaravel5.3で書き直した際の補足資料まとめ

## 1. はじめに

2016年のLaravel Advent Calendarの12/25を担当する[fumiyasac](https://github.com/fumiyasac)(Fumiya Sakai)と申します。何卒よろしくお願い致します。

今年も昨年同様に一番最後の日を担当する形になりましたが、Laravelの機能に関して深掘りする感じではなく、昨年に作成したチュートリアルのサンプルをLaravel5.1からLaravel5.3に改めて起こし直したものを作成しましたので、共有できればと思います。

__昨年のLaravel AdventCalendarの記事はこちら:__

+ __[Laravelの環境設定＆開発体験自作Laravel5.1ミニマムチュートリアルまとめ](http://qiita.com/fumiyasac@github/items/78a335880f7abb1de8bf)__

__Laravel5.1でのチュートリアルサンプル:__

+ __[Laravel5.1でCRUDアプリケーションを作成する簡易チュートリアル](https://github.com/fumiyasac/laravel-minimum-tutorial)__

サンプルの基本的な部分に関しては上記のサンプルをほとんど踏襲した形にはなっていますが、今年に自分のQiita内でご紹介したライブラリも取り入れたり、処理部分やルーティングの設定についても若干書き直したところもあったりするので、上記のLaravel5.1の際の記事やサンプルとも併せて一緒にご活用頂けましたら幸いに思います。

## 2. サンプルの概要並びにMAMPの設定やインストールまでの準備

下記のコード及び画面キャプチャが今回のチュートリアルで作成したものになります。
画面のデザインに関しましては、Laravel5.1で作成したバージョンの物と全く同じものを使用しています。

__(新規作成) Laravel5.3でのチュートリアルサンプル:__

+ __[Laravel5.3でCRUDアプリケーションを作成する簡易チュートリアル](https://github.com/fumiyasac/laravel5.3-minimum-tutorial)__

__画面キャプチャその1（一覧画面）:__
![spa_capture_index.png](https://qiita-image-store.s3.amazonaws.com/0/17400/28527040-cd22-8bfe-8666-c221b83bc2e4.png)

__画面キャプチャその2（詳細画面）:__
![spa_capture_detail.png](https://qiita-image-store.s3.amazonaws.com/0/17400/42d7853c-243e-a56e-b947-ca7bf36648d6.png)

__画面キャプチャその3（データ入力画面）:__
![spa_capture_add.png](https://qiita-image-store.s3.amazonaws.com/0/17400/a0f5f6e6-6484-b96a-bf33-491637917eef.png)

__画面キャプチャその4（データ編集画面）:__
![spa_capture_edit.png](https://qiita-image-store.s3.amazonaws.com/0/17400/9e63c98d-4545-0775-ac43-8ea8ce42d9f2.png)

今回のチュートリアルは自分の開発環境(MAMP)を使用して実行並びに検証を行っています。下記の一覧が私がこのチュートリアルを作成するにあたって使用した環境になりますので、ご参考になれば幸いに思います。

+ MacOS：10.12.2 Sierra
+ MAMP：Ver.4.0.6（2016.12.23時点での最新版）
+ Laravel：5.3.28
+ PHP：7.0.12（MAMPのPHPVersionで選択できるものを使用）
+ MySQL：5.6.33（MAMPのものを使用）

またLaravel5.3への変更点に関する部分に関しましては、こちらの記事がとても参考になりました。

+ [リリース直前! Laravel 5.3 の変更点（アップグレード編）](http://tech.innovator.jp.net/entry/2016/08/02/111614)

★2-1. MAMPのバージョンをアップする際の注意点とPHPのバージョン変更等に関して

すでにMAMPをインストールして頂いている方でMAMPのアップグレードを行う場合は、アップグレードの前に、以前に設定していたバーチャルホストの設定やMAMP内で使用しているMySQLのデータのバックアップを行うようにしてください。

私の場合にはなりますが、MAMPを使用していたバージョンから現在の最新版にアップグレードを行なった際に、バーチャルホストの設定が消えてしまったのでちょっと冷や汗をかきました。。。
（バックアップ取って追いてよかった...）

またMAMPで提供されているPHPのバージョンを指定したい際には、下記の`$ which php`コマンドにて使用されているPHPのバージョンを確認した上で、`~/.bash_profile`にPHPのパスを指定した後にターミナルを再起動して設定の反映を行えばOKです。

```
$ which php
/opt/local/sbin/php //現在のphpのパス
$ sudo vim ~/.bash_profile
export PATH=/Applications/MAMP/bin/php/php7.0.12/bin:$PATH //こちらを追記
```

※ 今回のチュートリアル内では特にバージョンに起因するような処理はないので、Laravel5.3が動くバージョンであれば問題ないかと思います。

★2-2. composer経由でのLaravel5.3のインストールとcomposer.jsonで定義するライブラリに関して

今回のサンプルでは`/usr/local/bin/composer`に`composer.phar`ファイルを移動することができなかった関係で下記のような手順でインストールを行なっています。

```
$ cd /Applications/Applications/MAMP/htdocs/
$ curl -sS https://getcomposer.org/installer | php
$ sudo php composer.phar create-project laravel/laravel <app name on my own.> --prefer-dist
$ sudo chmod -R 775 <app name on my own.>
$ sudo chmod -R 777 <app name on my own.>/storage
```

上記の手順にてLaravel5.3のインストールと作成されたプロジェクトファイルや`storage`ディレクトリへの書き込み権限の付与を行なった後に、手動で`composer.phar`ファイルを作成したプロジェクト内へ移動させました。

そして、本サンプル内で使用するライブラリは下記の3つになります。こちらの3つのライブラリに関しては、今回の__「Laravel5.3にも対応をしている点」__や__「Basic認証・ファイルアップロード機能」__といった簡単やアプリケーションや表には出さない管理ツールであったとしても、予め実装をしておきたい局面が多いのではと感じたので今回は使用しました。

+ [Laravel Collective Forms & HTML](https://laravelcollective.com/docs/5.3/html) → ViewとなるbladeテンプレートのHTMLを扱いやすくする
+ [laravel-stapler](https://github.com/CodeSleeve/laravel-stapler) → ファイルのアップロード機能
+ [l5-very-basic-auth](https://github.com/olssonm/l5-very-basic-auth) → ステートレスなBasic認証

導入に関しては、下記のようにcomposer.jsonファイルに導入したいライブラリとバージョンを記載して下記のコマンドを実行して、ライブラリのインストールと設定ファイルをconfig配下に書き出せば準備OKです。

```
$ sudo php composer.phar update
$ php artisan vendor:publish
```

※1. Laravelはバージョンアップが結構頻繁に行われるフレームワークではあるので、Laravelのバージョンと導入するライブラリのバージョンの食い違いには注意してください。

※2. このサンプルではaws-sdk-php（Amazon Web Serviceとの連携用）も一緒にインストールを行なっていますが、自分のローカル環境のみで試す場合には導入しなくても問題ありません。

```json:composer.json
{
	・・・（省略）・・・
    "require": {
        "php": ">=5.6.4",
        "laravel/framework": "5.3.*",
        "laravelcollective/html": "^5.3.0",
        "codesleeve/laravel-stapler": "1.0.*",
        "aws/aws-sdk-php": "2.4.*",
        "olssonm/l5-very-basic-auth": "2.*"
    },
	・・・（省略）・・・
```

上記のライブラリのインストールが完了しましたら、今回作成したプロジェクト内の`config/app.php`内に下記のようにprovidersとaliasesの配列内にインストールしたライブラリのサービスプロバイダとエイリアスへの追加を忘れずに行うようにしてください。

```php:config/app.php
'providers' => [

    ・・・（省略）・・・

    //使用するライブラリのサービスプロバイダーを追加する（Collective\Html・LaravelStapler・VeryBasicAuth）
    Collective\Html\HtmlServiceProvider::class,
    Codesleeve\LaravelStapler\Providers\L5ServiceProvider::class,
    Olssonm\VeryBasicAuth\VeryBasicAuthServiceProvider::class,
],

'aliases' => [

    ・・・（省略）・・・

    //使用するライブラリのFacadeをエイリアスに追加する(Collective\Htmlのみ)
    'Form' => Collective\Html\FormFacade::class,
    'Html' => Collective\Html\HtmlFacade::class,
],
```

導入手順に関しましては、以前に作成したLaravel5.1で作成したチュートリアルの導入手順とさほど変わらない部分ではありますが、導入するライブラリのバージョンが異なっているものがありましたので、今回は該当部分を改めて追記した形になります。

今回はチュートリアル作成と環境構築の際にMAMPのアップグレードとPHP7を使うように設定したので、その部分に関する手順やcomposerでのインストールに関してピックアップをしましたが、MySQLの設定やMAMPでのバーチャルホストの設定に関しては、以前に書いたLaravel5.1でのチュートリアルの解説と相違はないので併せてご確認頂ければと思います。

※1. すでにデータが追加された状態でサンプルを動かしたい場合は、新規テーブル追加のマイグレーション実行後に`sample_data.sql`に記載されているSQL文を実行してお試し下さい（画像ファイルはすでに`/public`配下に設置済みです）

※2. 「Laravel Collective Forms & HTML」・「laravel-stapler」・「l5-very-basic-auth」を利用した書き方に関しては、それほどバージョンによって実装が思い切り異なるということはないので、Laravel5.1で作成したチュートリアルや下記の記事等も併せて参考にしていただければと思います。

__「laravel-stapler」の導入と活用について__

+ [Laravel5.1で作るファイルアップロードライブラリLaravel-Staplerの利用方法＆AWS S3との連携Tips](http://qiita.com/fumiyasac@github/items/a73d3598d75e749ba443)

__「l5-very-basic-auth」の導入と活用について__

+ [Laravel本体の認証機能とは別にDB情報を使わないbasic認証を実装するサンプル](http://qiita.com/fumiyasac@github/items/cdf854008e7487148a67)

## 3. 今回のLaravel5.3で書き直したチュートリアルについて

今回のLaravel5.3で書き直したチュートリアルに関しては、コード自体はLaravel5.1で作成をしたものとはあまり差はないのですが、ルーティングに関する部分やLaravel5.1でのコードと差のある部分についてはかいつまんでにはなりますが、簡単に解説を行なっていければと思います。

★3-1. ルーティングに関する変更点と注意ポイント

Laravel5.3からは下記のような形で`routes`ディレクトリ内に下記の3つのフォルダが作成されるような形に変更されています。

+ api.php (API用)
+ console.php (コンソール用)
+ web.php (Web画面用)

今回はWebアプリケーションでのサンプルになるので、`web.php`に今回のルーティングを下記のように設定し、また今回は追加・変更・削除の画面にbasic認証をつけてID/PASSでの認証を通過しないとデータを扱えないような形にするために、グループ設定`Route::group(['middleware' => 'auth.very_basic', 'prefix' => ''], …`の中に追加・変更・削除に関連するアクションへのルーティングを入れるようにします。

```php:routes/web.php
<?php
//全トピック表示画面
Route::get('/', 'TopicsController@index');

//任意idのトピック表示
Route::get('/topics/show/{id}', 'TopicsController@show');

Route::group(['middleware' => 'auth.very_basic', 'prefix' => ''], function() {

    //トピックの新規追加画面
    Route::get('/topics/add', 'TopicsController@add');
    Route::post('/topics/create', 'TopicsController@create');

    //任意idのトピック編集
    Route::get('/topics/edit/{id}', 'TopicsController@edit');
    Route::post('/topics/update/{id}', 'TopicsController@update');

    //任意idのトピック削除
    Route::post('/topics/delete', 'TopicsController@delete');
});
```

また、config配下に作成した`l5_very_basic_auth.php`の中にBasic認証に関する設定がありますのでその部分も併せて設定をします。

```php:config/l5_very_basic_auth.php
<?php

    /**
     * Configuration for the "HTTP Very Basic Auth"-middleware
     */
    return [
        // Username
        'user' => 'admin',

        // Password
        'password' => 'test_ha_kanpeki',

        // Environments where the middleware is active
        'envs' => [
            'local',
            'development',
            'testing'
        ],

        // Message to display if the user "opts out"/clicks "cancel"
        'error_message' => 'こちらのページは管理者のみアクセスができます。正しいID/PASSWORDを入力した上で再度アクセスをお願い致します。',

        // If you prefer to use a view with your error message you can uncomment "error_view".
        // This will superseed your default response message
        // 'error_view'        => 'very_basic_auth::default'
    ];
```

ルーティングに関する設定についてはそれぞれの用途に応じたルーティングの設定ができるようになっています。この部分はLaravel5.3になって大きく変更があった点ではありますが書き方に関しての大きな変更は特にないので、下記のようにルーティング設定ができていればOKです。

```
$ php artisan route:list
+--------+----------+--------------------+------+----------------------------------------------+---------------------+
| Domain | Method   | URI                | Name | Action                                       | Middleware          |
+--------+----------+--------------------+------+----------------------------------------------+---------------------+
|        | GET|HEAD | /                  |      | App\Http\Controllers\TopicsController@index  | web                 |
|        | GET|HEAD | topics/add         |      | App\Http\Controllers\TopicsController@add    | web,auth.very_basic |
|        | POST     | topics/create      |      | App\Http\Controllers\TopicsController@create | web,auth.very_basic |
|        | POST     | topics/delete      |      | App\Http\Controllers\TopicsController@delete | web,auth.very_basic |
|        | GET|HEAD | topics/edit/{id}   |      | App\Http\Controllers\TopicsController@edit   | web,auth.very_basic |
|        | GET|HEAD | topics/show/{id}   |      | App\Http\Controllers\TopicsController@show   | web                 |
|        | POST     | topics/update/{id} |      | App\Http\Controllers\TopicsController@update | web,auth.very_basic |
+--------+----------+--------------------+------+----------------------------------------------+---------------------+
```

★3-2. マイグレーションファイルに関する部分

マイグレーションファイルに関しても、Laravel5.1でのチュートリアルでのコードと特に大きな差はありませんがLaravel5.1と同じDB定義のマイグレーションファイルを用いてマイグレーション実行した際に下記のような例外が自分の環境で出てしまいました。

```
$ php artisan migrate

Migration table created successfully.

[Illuminate\Database\QueryException]
  SQLSTATE[42000]: Syntax error or access violation: 1067 Invalid default value for 'eyecatch_updated_at' (SQL: create table `topics` (`id` int unsigned not null auto_increment primary key
  , `title` varchar(255) not null, `body` text not null, `published` date not null, `created_at` timestamp null, `updated_at` timestamp null, `eyecatch_file_name` varchar(256) not null, `e
  yecatch_file_size` int not null, `eyecatch_content_type` varchar(256) not null, `eyecatch_updated_at` timestamp not null) default character set utf8 collate utf8_unicode_ci)

[PDOException]
  SQLSTATE[42000]: Syntax error or access violation: 1067 Invalid default value for 'eyecatch_updated_at'
```

原因としては、laravel-staplerで使用するカラム`eyecatch_updated_at (アイキャッチ画像の更新日時)`のデフォルト値の設定をしていないために起こったものなので、下記のように該当のカラムに対して`nullable()`の制約を追加して対応しました。

```php:database/migrations/2016_12_23_175936_create_topics_table.php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateTopicsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        //topicsテーブルを生成する
        Schema::create('topics', function(Blueprint $table)
        {
            $table->increments('id');
            $table->string('title');
            $table->text('body');
            $table->date('published');
            $table->timestamps();

            //Add columns for Laravel Stapler
            $table->string('eyecatch_file_name', 256);
            $table->integer('eyecatch_file_size');
            $table->string('eyecatch_content_type', 256);
            $table->timestamp('eyecatch_updated_at')->nullable();

            /**
             * JFYI:laravel-stapler用のカラム追加
             * カラム名：eyecatch_file_name => varchar(256)
             * カラム名：eyecatch_file_size => int(11)
             * カラム名：eyecatch_comtent_type => varchar(256)
             * カラム名：eyecatch_updated_at => timestamp
             */

            /**
             * JFYI:カラムの詳細設定をする場合はこちらを参考にしてみてください
             * http://laravel.com/docs/5.1/migrations#writing-migrations
             */
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        //topicsテーブルを削除する
        Schema::drop('topics');
    }
}
```

この部分に関しては、以前に作成した時（Laravel5.1でチュートリアルを作成した時）には特に問題なく実行できた部分ではあるのですが、デフォルト値に関する設定はあらかじめつけておくようにすると良いのかなと感じました。

★3-3. コントローラーに関する部分

コントローラーに関する部分については、特に以前の処理と大きな変更はありませんが、今回のLaravel5.3の処理ではデータを取得する際に`findOrFail`メソッドではなく`find`メソッドに書き換えて取得結果の有無に応じて遷移する画面や追加・変更・削除処理に関するハンドリングをいます。

```php:app/Http/Controllers/TopicsController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;

//モデルの宣言
use App\Topic;

class TopicsController extends Controller
{
    //バリデーションルールを定める（required|min:3は必須で最低3文字以上の意味）
    //バリデーションメッセージは resources/lang/ja/validation.phpを参照
    private $rule = [
        'title' => 'required|min:3',
        'body' => 'required',
        'eyecatch' => 'image|max:2000',
        'published' => 'required|date',
    ];

    //一覧
    public function index() {

        //Topicsテーブルのデータをすべて取得する
        $topics = Topic::all();
        return view('topics.index', compact('topics'));
    }

    //表示
    public function show($id) {

        //渡されたIDに紐づくTopicsテーブルのデータを取得する
        $topic = Topic::find($id);
        if ($topic) {
            return view('topics.show', compact('topic'));
        } else {
            \Session::flash('flash_message', 'ID:' . $id . 'に該当するデータはありませんでした。');
            return redirect('/');
        }
    }

    //追加
    public function add() {
        return view('topics.add');
    }

    public function create(Request $request) {

        //バリデーションを行う（正常：新規データ1件登録して一覧画面へ, 異常：メッセージを表示して入力画面へ）
        $this->validate($request, $this->rule);

        //新規データ1件登録
        Topic::create($request->all());

        //一覧画面へリダイレクト
        \Session::flash('flash_message', '新規データが正常に1件追加されました。');
        return redirect('/');
    }

    //編集
    public function edit($id) {

        //$idに該当するデータを1件取得する
        $topic = Topic::find($id);
        if ($topic) {
            return view('topics.edit', compact('topic'));
        } else {
            \Session::flash('flash_message', 'ID:' . $id . 'に該当するデータはありませんでした。');
            return redirect('/');
        }
    }

    public function update($id, Request $request) {

        //$idに該当するデータを1件取得する
        $topic = Topic::find($id);

        //バリデーションを行う（正常：IDに紐づくデータ1件を更新して一覧画面へ, 異常：メッセージを表示して入力画面へ）
        $this->validate($request, $this->rule);

        //既存データ1件更新
        $topic->fill($request->all())->save();

        //一覧画面へリダイレクト
        \Session::flash('flash_message', 'ID:' . $id . 'に該当するデータが1件更新されました。');
        return redirect('/');
    }

    //削除
    public function delete(Request $request) {

        //削除対象の$idを取得する
        $target_id = $request->id;

        //$idの形式が正しいかのチェック（注意：この部分は実際に使う場合はより厳密なチェックが必要）
        if ($target_id && is_numeric($target_id)) {

            //既存データ1件削除
            $topic = Topic::find($target_id);
            if ($topic) {
                $topic->delete();
            } else {
                \Session::flash('flash_message', 'ID:' . $target_id . 'に該当するデータはありませんでした。');
                return redirect('/');
            }

            //削除成功時のメッセージを表示
            \Session::flash('flash_message', 'ID:' . $target_id . 'に該当するデータが1件削除されました。');

        } else {

            //削除失敗時のメッセージを表示
            \Session::flash('flash_message', '削除処理が実行されませんでした。該当データをご確認ください。');
        }

        //一覧画面へリダイレクト
        return redirect('/');
    }
}
```

今回の部分に関しては、該当のIDに紐づくデータが取得できなかった（間違ったIDを指定した場合等）の画面遷移に関する処理を施した形になります。また、入力データがバリデーションに引っかかってしまった場合に表示されるエラーメッセージの日本語化については、

+ `config/app.php`内の言語設定を「`'locale' => 'ja'`」に設定する
+ `resource/lang`ディレクトリ内に「`ja/validation.php`」を作成して日本語のエラーメッセージを設定する

という対応でOKです。

★3-4. モデルに関する部分

この部分に関しては、Laravel5.1で作成したものと変わらない部分になります。

heroku等のクラウドサーバーとAmazon S3等の外部ストレージを併用して「アプリケーション本体をクラウドサーバーで運用して画像等のファイルに関しては外部ストレージで管理する構成」を行う場合には、前述の「Laravel-stapler」に関する記事や下記のモデルファイル内に記載したコメントを参考にして実装をして頂ければと思います。

```php:app/Topic.php
<?php
namespace App;
use Codesleeve\Stapler\ORM\StaplerableInterface;
use Codesleeve\Stapler\ORM\EloquentTrait;
use Illuminate\Database\Eloquent\Model;

class Topic extends Model implements StaplerableInterface {

    //Staplerの読み込み
    use EloquentTrait;

    //データ追加対象の定義
    protected $fillable = array('title', 'body', 'published', 'eyecatch');

    //コンストラクタ
    public function __construct(array $attributes = array()) {

        //画像の投稿設定
        $this->hasAttachedFile('eyecatch', [

            //画像の切り取るサイズのパターン
            'styles' => [
                'large' => '640x480#',
                'medium' => '300x200#',
                'thumb' => '100x75#'
            ],

            //格納ディレクトリ(public配下からのパス)
            'url' => '/uploads/topics/:id/:style/:filename'
            /**
             * JFYI：S3を使用する際の設定のテンプレート的なもの
        	   * (注意事項)
        	   * [その1: configファイル] config/laravel-stapler/s3.phpにAWS S3の設定を記載 ※'storage' => 's3'を設定してあげる
        	   * [その2: s3内のパス設定] 'path' => '/uploads/topics/:id/:style/:filename'
             *
             * 's3_client_config' => [
             *     'key' => 'xxxx',
             *     'secret' => 'xxxx',
             *     'region' => 'xxxx'
             * ],
             * 's3_object_config' => [
             *     'Bucket' => 'xxxx',
             *     'ACL' => 'xxxx'
             * ],
             * 'storage' => 's3',
             */
        ]);
        parent::__construct($attributes);
    }
}
```

※ 外部ストレージ等を活用したファイルアップロード機能を実装する際に関しては、AWSのSecret Key等の扱いに関しては十分にご注意ください。

★3-5. ビューに関する部分

この部分に関しても、Laravel5.1で作成したものと変わらない部分になりますので、今回は必要な.blade.phpファイルの記載のみ行なっています。

Form部分をはじめ、その他`{!! …  !!}`を用いて書かれている部分が「laravelcollective/html」を使用して書かれている部分になります。

__1. 大枠のテンプレート部分:__

```php:resources/views/layouts/app.blade.php
<!DOCTYPE HTML>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>My Christmas Dinner List</title>
     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
  </head>
  <body style="padding-top:50px;" role="document">
  <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
    <div class="container">
      <div class="navbar-header">
        <a class="navbar-brand" href="/">My Christmas Dinner List</a>
      </div>
    </div>
  </div>
  <div class="container">
    @yield('content')
  </div>
  <hr>
    <footer class="footer">
      <div class="container">
        <p>Copyright &copy; 2016 just1factory All Rights Reserverd.</p>
      </div>
    </footer>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
  </body>
</html>
```

__2. データ一覧画面:__

```php:resources/views/topics/index.blade.php
<!-- resouces/views/topics/index.blade.php -->
@extends('layouts.app')
  @section('content')
  <h1>クリスマスに食べたい・作りたい料理のリスト</h1>
  <hr>

  @if(Session::has('flash_message'))
    <div class="alert alert-success">
      {{ Session::get('flash_message') }}
    </div>
    <hr>
  @endif

  @foreach($topics as $topic)
    <article>
      <table class="table table-bordered">
        <tr class="info">
          <th colspan="3"><span>[ID:{{ $topic->id }}]</span> <a href="{{ url('topics/show', $topic->id) }}">{{ $topic->title }}</a></th>
        </tr>
        <tr>
          <td width="100"><b>サムネイル画像</b></td>
          <td><b>本文</b></td>
          <td width="100"><b>日付</b></td>
        </tr>
        <tr>
          <td width="100"><img src="{{ $topic->eyecatch->url('thumb') }}"></td>
          <td><div class="body">{{ str_limit($topic->body, 300, $end = '... [If you show more, please click title.]') }}</div></td>
          <td width="100">{{ $topic->published }}</td>
        </tr>
      </table>
    </article>
    <hr>
  @endforeach

  {!! link_to('topics/add', '新規作成', ['class' => 'btn btn-primary']) !!}
@endsection
```

__3. データ詳細画面:__

```php:resources/views/topics/index.blade.php
<!-- resouces/views/topics/show.blade.php -->
@extends('layouts.app')
@section('content')
<article>
  <h1>{{ $topic->title }}</h1>
  <hr>
  <section style="padding-bottom: 50px;">
    <img src="{{ $topic->eyecatch->url('large') }}">
    <hr>
    <p class="lead">{{ $topic->body }}</p>
    <hr>
    <p class="lead">{{ $topic->published }}</p>
    <hr>
    <div style="margin-bottom: 75px;">
      <div class="pull-left">
        {!! link_to('/', '一覧へ戻る', ['class' => 'btn btn-info']) !!}
        {!! link_to('topics/edit/' . $topic->id, '編集する', ['class' => 'btn btn-primary']) !!}
      </div>
      <div class="pull-right">
        {!! Form::open(['url' => 'topics/delete']) !!}
          {!! Form::hidden('id', $topic->id, ['class' => 'form-control']) !!}
	        {!! Form::submit('削除する', ['class' => 'btn btn-danger']) !!}	
        {!! Form::close() !!}
      </div>
    </div>
    <hr style="clear:both;" />
  </section>
</article>
@endsection
```

__4. データ新規追加画面:__

```php:resources/views/topics/add.blade.php
<!-- resouces/views/topics/add.blade.php -->
@extends('layouts.app')
@section('content')
  <h1>料理のリストを新しく追加する</h1>
  <hr>

  @if ($errors->any())
    <div class="alert alert-danger">
      <ul>
        @foreach ($errors->all() as $error)
          <li>{{ $error }}</li>
        @endforeach
      </ul>
    </div>
  @endif

  {!! Form::open(['url' => 'topics/create', 'files' => true]) !!}
    <div class="form-group">
      {!! Form::label('title', 'Title:') !!}
      {!! Form::text('title', null, ['class' => 'form-control']) !!}
    </div>
    <div class="form-group">
      {!! Form::label('body', 'Body:') !!}
      {!! Form::textarea('body', null, ['class' => 'form-control']) !!}
    </div>
    <div class="form-group">
      {!! Form::label('eyecatch', 'Eyecatch:') !!}
      {!! Form::file('eyecatch', null, ['class' => 'form-control']) !!}
    </div>
    <div class="form-group">
      {!! Form::label('published', 'Publish On:') !!}
      {!! Form::input('date', 'published', date('Y-m-d'), ['class' => 'form-control']) !!}
    </div>    
    <div class="form-group">
      {!! Form::submit('Add Topic', ['class' => 'btn btn-primary form-control']) !!}
    </div>
  {!! Form::close() !!}
@endsection
```

__5. データ編集画面:__

```php:resources/views/topics/edit.blade.php
<!-- resouces/views/topics/edit.blade.php -->
@extends('layouts.app')
@section('content')
  <h1>"{{ $topic->title }}"の内容を編集</h1>
  <hr>

  @if ($errors->any())
    <div class="alert alert-danger">
      <ul>
        @foreach ($errors->all() as $error)
          <li>{{ $error }}</li>
        @endforeach
      </ul>
    </div>
  @endif

  {!! Form::open(['url' => 'topics/update/' . $topic->id , 'files' => true]) !!}
    <div class="form-group">
      {!! Form::label('title', 'Title:') !!}
      {!! Form::text('title', $topic->title, ['class' => 'form-control']) !!}
    </div>
    <div class="form-group">
      {!! Form::label('body', 'Body:') !!}
      {!! Form::textarea('body', $topic->body, ['class' => 'form-control']) !!}
    </div>
    <div class="form-group">
      {!! Form::label('eyecatch', 'Eyecatch:') !!}
      {!! Form::file('eyecatch', null, ['class' => 'form-control']) !!}
    </div>
    <div class="form-group">
      {!! Form::label('published', 'Publish On:') !!}
      {!! Form::input('date', 'published', date('Y-m-d'), ['class' => 'form-control']) !!}
    </div>    
    <div class="form-group">
      {!! Form::submit('Update Topic', ['class' => 'btn btn-primary form-control']) !!}
    </div>
  {!! Form::close() !!}
@endsection
```

Laravel自体はMVCパターンにとらわれないようなディレクトリ構成となっていますが、このチュートリアルのサンプルに関してはModel / View / Controllerをそれぞれ作成した形を取っています。これは私が今までMVCパターンのフレームワークを業務で扱うことが多かったので、このような形を取っています。

もしこのサンプルを元にカスタマイズ等の機能拡張を行うような場合については、必ずしもこの流れに則る必要性はないと思いますので、実装しやすい構成やスタイルで機能の追加やカスタマイズを行なって頂ければと思います。

## 4. まとめ

私自身がLaravelを始めた際はバージョンが4.xの時からでちょうど2014年の夏頃からでした。残念ながら業務の中で扱う機会はなかったのですが、新しいフレームワークということもあったので個人的に追っかけて簡単なサンプルを作ったり、「laravel-stapler」や「l5-very-basic-auth」に関しても実際に試して、その知見を資料にまとめたり等をしていました。

そして昨年のAdventCalenderで作成した、最低限の機能を作成するチュートリアルの記事やサンプルに対して良い反響を頂けた際には本当に驚きと同時に嬉しい気持ちでいっぱいになりました。

（実はその記事が私の数あるQiita記事の中でもぶっちぎりのView数がありました）

今回はそういった経緯もあったので、せっかく作ったチュートリアルなのにLaravelのバージョンアップに追従していないのは、よろしくないなと個人的にも感じた部分ではあったので今回このような形で改めてLaravel5.3で書き直した次第です。そして手順やコードに関しては書き直した部分やバージョンアップで異なった部分はあれども、大きく外れる点が少なかったので、今回は補足のような形でまとめました。

ここから先は余談になってしまいますが、今年お世話になった現場の中のソースコードに関してですが、Laravelではなかったのですが、Laravelの中でもよく用いられる__「Facadeパターン」__・__「Dependency Injection」__・__「Symfony Component」__を活用した実装をしていたので、Laravelに取り組む課程の中で身につけた知識や知見が、実際の業務をする中でものすごく役に立ったのは本当に嬉しく感じましたし、Laravelやっていて本当に良かったと思いました。

+ 補足や解説の充実 orもっと深掘りした説明
+ サンプルの中の機能の追加に関する要望
+ Pull Request や Issue等既存コードに関するもの

に関しては、ご遠慮なくお申し付け頂ければ嬉しく思います。

そしてLaravelを体験してみたいと感じている方やLaravelを使った業務にこれから携わるので予行演習をざっくりとしておきたいという方の手助けになることができれば、非常に嬉しく思います。