PHPフレームワーク、Laravel5.5で簡単なシステムを作ってみます。CRUDを一回り作ってみるチュートリアルです。

前提

PHP、Laravelはすでにインストール済みでblogというプロジェクトが生成されている状態から始めます。まだインストールが済んでいない場合は、「PHPフレームワークLaravel 5.5をインストールする」を参考に作業を進めておいてください。

データベースを作成する

今回作成するblogシステム用のデータベースを作成します。データベース名をblogとしてデータベースを作成してください。また、データベースにアクセスできるユーザも合わせて作成します。
PHPフレームワークLaravel 5.5をインストールする」の手順でインストールした場合は、phpMyAdminを使ってデータベースを作成するのが簡単だと思います。
XAMPP Control PanelのMySQLのAdminボタンをクリックするとphpMyAdminを起動できます。

phpMyAdminの起動

データベース接続の設定をする

Laravelにデータベースの設定をします。
データベースの設定はconfig\database.phpにあります。

        'mysql' => [
            'driver' => 'mysql',
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'forge'),
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'strict' => true,
            'engine' => null,
        ],

28行目のdatabaseでデータベース名、
29行目のusernameでデータベースに接続するユーザ名、
30行目のpasswordでデータベースに接続するユーザ名のパスワード
を設定します。
環境によってはその他の設定値も変更する必要があるかもしれません。
ここで少し注意が必要なのが、env('DB_DATABASE', 'forge')の記述です。env関数は.envファイルの設定値を読み込む関数で、第1引数に指定された設定値を読み込みます。設定値が未設定の場合には第2引数で指定された値が設定値となります。したがって、'database' => env('DB_DATABASE', 'hogehoge'),と変更しても.envでDB_DATABASEが設定されている場合は.envの設定の方が優先され、データベース名がhogehogeに変更されません。'database' => 'hogehoge',と変更するか、.env側を変更しましょう。ここでは.envを変更することにします。

APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:dag799X3WBLcfgodouXDJFg0PFD3J+MmPYtBJVG3L3w=
APP_DEBUG=true
APP_LOG_LEVEL=debug
APP_URL=http://localhost

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=blog
DB_USERNAME=username
DB_PASSWORD=password

テーブルを作成する

テーブルを作成するにはデータベースを作成したのと同様にphpMyAdminなどで直接作成しても構いませんが、Laravelにはテーブルを編集するためのマイグレーションと呼ばれる機能があります。ここではこの機能を使ってテーブルを作成します。

マイグレーションを生成する

make:migration Artisanコマンドを使ってマイグレーションを生成します。ArtisanコマンドはLaravelのコマンドでLaravelプロジェクトの直下でphp artisan make:migration create_articles_tableコマンドを実行してください。

>cd \xampp\htdocs\blog
>php artisan make:migration create_articles_table
Created Migration: xxxx_xx_xx_xxxxxx_create_articles_table

マイグレーションファイルを編集する

マイグレーションを生成するとdatabase\migrations\xxxx_xx_xx_xxxxxx_create_articles_table.phpファイルが作成されます。xxの部分は日時が入ります。このファイルを以下のように編集します。

<?php

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

class CreateArticlesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->increments('id');
            $table->text('title');
            $table->text('body');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('articles');
    }
}

元の記述から変更したのは18行目と19行目だけです。16~21行目でarticlesテーブルを作成しています。テーブルのフィールドの設定が17~20行目です。17行目は主キーのidフィールド、18行目はブログタイトルのtitleフィールド、19行目はブログ本文のbodyフィールド、20行目はcreated_atフィールド・updated_atフィールドを設定しています。
incrementsやtextメソッドは作成されるフィールドの型を指定しています。指定できる型は公式ドキュメントを参考にすると良いでしょう。

不要なマイグレーションファイルを削除する

編集したマイグレーションファイルと同じディレクトリにデフォルトで他に2つのファイルがあります。このファイルは認証機能用のマイグレーションファイルで今回は不要ですので削除しておきます。
database\migrations\2014_10_12_000000_create_users_table.php
database\migrations\2014_10_12_100000_create_password_resets_table.php

マイグレーションを実行する

migrate Artisanコマンドを使用します。php artisan migrateコマンドを実行します。

>php artisan migrate
Migration table created successfully.
Migrating: xxxx_xx_xx_xxxxxx_create_articles_table
Migrated:  xxxx_xx_xx_xxxxxx_create_articles_table

SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytesエラーが出る場合

Laravel5.5で文字セットのデフォルトが変更されたことが原因です。詳細は記事「php artisan migrateでSyntax errorになる」に説明していますので一読ください。

モデルを作成する

articlesテーブルに紐付いたArticleモデルを作成します。make:model Artisanコマンドを使用します。php artisan make:model Articleコマンドを実行してください。

>php artisan make:model Article
Model created successfully.

テーブルに初期データを入れる

テーブルには最初何もデータが入っていません。アプリケーションを作成する際に何かデータがあったほうが都合が良いことが多く、Laraveldehaにはテーブルに初期データを入れるためのシーディングと呼ばれる機能があります。ここではこの機能を使ってテーブルの初期データを入れてみます。

シーダを生成する

make:seeder Artisanコマンドを使ってシーダを生成します。php artisan make:seeder ArticlesTableSeederコマンドを実行してください。

>php artisan make:seeder ArticlesTableSeeder
Seeder created successfully.

シーダーファイルを編集する

マイグレーションを生成するとdatabase\seeds\ArticlesTableSeeder.phpファイルが作成されます。このファイルを以下のように編集します。

<?php

use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
use App\Article;

class ArticlesTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        for ($i = 1; $i <= 10;$i++) {
            $article = new Article;
            $article->title = '記事タイトル' . $i;
            $article->body = '記事本文' . $i;
            $article->save();
        }
    }
}

5行目で先程作ったArticleモデルをuseします。シーダーが実行されるとrunメソッドが実行されます。16行目から21行目まででarticlesテーブルにデータを挿入しています。

シーダを実行する

db:seed Artisanコマンドを使用します。php artisan db:seed --class=ArticlesTableSeederコマンドを実行します。

>php artisan db:seed --class=ArticlesTableSeeder

データベースの中身を見るとデータが追加されていることがわかります。

seedデータの確認

一覧表示画面(R)を作成する

CRUDのRead、つまり一覧画面を作成します。ブログのタイトルと内容をデータベースから抽出しHTMLに出力します。

コントローラを作成する

まずはコントローラを作成します。Articleコントローラを作成します。
make:controller Artisanコマンドを使用します。php artisan make:controller ArticleControllerコマンドを実行してください。

>php artisan make:controller ArticleController
Controller created successfully.

コントローラを編集する

前の手順でapp\Http\Controllers\ArticleController.phpが作成されますので、これを以下のコードに修正します。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Article;

class ArticleController extends Controller
{
    public function index() {
        $articles = Article::all();
        return view('article.index', ['articles' => $articles]);
    }
}

6行目でArticleモデルをuseします。
11行目でarticlesテーブルからデータを抽出します。
12行目でビューを表示します。この時、変数$articlesをビューに渡しています。

ルーティングを設定する

先の手順で作成したコントローラ、メソッドにルーティングを設定します。
ここでは「インストールしたURL/」のアクセスをArticleコントローラのindexメソッドに割り当てます。
routes\web.phpを以下のコードに修正します。

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', 'ArticleController@index');

14行目がルーティングの設定になります。それ以外はコメントです。

ビューを作成する

resources\views\article\index.blade.phpファイルを作成します。このとき、articleディレクトリもありませんので作成してください。
コントローラでview('article.index'としているのは、ビューファイルとしてresources\views\article\index.blade.phpファイルを指定していることになります。
このファイルを下記のように作成します。

<!doctype html>
<html lang="ja">
  <head>
    <title>Laravelチュートリアル</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
  </head>
  <body class="p-3">
    <h1>ブログ一覧</h1>

    @foreach ($articles as $article)
    <div class="card mb-2">
      <div class="card-body">
        <h4 class="card-title">{{ $article->title }}</h4>
        <h6 class="card-subtitle mb-2 text-muted">{{ $article->updated_at }}</h6>
        <p class="card-text">{{ $article->body }}</p>
      </div>
    </div>
    @endforeach

    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
  </body>
</html>

ビューファイルですので、内容はほぼHTMLです。デザインの簡易化のためにBootstrapを使用しています。
注目すべきは12行目と20行目で、@から始まる文法はLaravelのテンプレートエンジンBladeの記法になります。記号として@を使うくらいでほぼPHPの記法と同じになるので、それほど難しくはないでしょう。
この例では変数$articlesを12行目から20行目まで繰り返しています。繰り返したデータは15行目の{{ $article->title }}で表示しています。繰り返しで変数$articleにデータが1つずつ取り出され、titleプロパティを参照するとデータベースから抽出したtitleフィールドの値を表示できます。{{ }}はBladeの記法でPHPで変数の内容をechoするのと同様の動作をします。(厳密にはXSS対策としてのエスケープも行います)

動作を確認する

http://localhost/にアクセスして動作を確認します。
一覧画面の動作確認

データベースの内容が抽出されて表示されていることがわかります。

新規追加画面(C)を作成する

CRUDのCreate、つまり新規追加画面を作成します。ブログのタイトルと内容をデータベースに挿入します。

コントローラを編集する

app\Http\Controllers\ArticleController.phpに新規追加用のメソッドを追加します。新規追加用のメソッドは新規追加のフォームを表示するメソッド(create)とフォームから送られたデータをデータベースに挿入するメソッド(store)の2つを追加します。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Article;

class ArticleController extends Controller
{
    public function index() {
        $articles = Article::all();
        return view('article.index', ['articles' => $articles]);
    }

    public function create() {
        return view('article.create');
    }

    public function store(Request $request) {
        $article = new Article;
        $article->title = $request->title;
        $article->body = $request->body;
        $article->save();

        return view('article.store');
    }
}

createメソッドは新規追加のフォームを表示しているだけです。
storeメソッドはフォームから送られたデータを変数$requestで受け取り、データベースに書き込んでいます。データベースの操作はEloquent ORMを使用しています。データベースに書き込むためのモデルのインスタンスを生成し、それに書き込むデータをセットしていきます。save()メソッドで実際にデータベースへの書き込みを実行します。

ルーティングを設定する

「インストールしたURL/create」にアクセスした時はをArticleコントローラのcreateメソッドに割り当て、「インストールしたURL/create」にフォームのデータをPOSTした時はArticleコントローラのstoreメソッドに割り当てます。Laravelは同一URLでもmethodによって処理をルーティングで変えることができます。
routes\web.phpを以下のコードに修正します。

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', 'ArticleController@index');
Route::get('create', 'ArticleController@create');
Route::post('create', 'ArticleController@store');

ビューを作成する

一覧画面のresources\views\article\index.blade.phpファイルに新規追加用のフォーム画面へのリンクを追加します。

<!doctype html>
<html lang="ja">
  <head>
    <title>Laravelチュートリアル</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
  </head>
  <body class="p-3">
    <h1>ブログ一覧</h1>
    <p><a href="/create" class="btn btn-primary">新規追加</a></p>

    @foreach ($articles as $article)
    <div class="card mb-2">
      <div class="card-body">
        <h4 class="card-title">{{ $article->title }}</h4>
        <h6 class="card-subtitle mb-2 text-muted">{{ $article->updated_at }}</h6>
        <p class="card-text">{{ $article->body }}</p>
      </div>
    </div>
    @endforeach

    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
  </body>
</html>

11行目のリンクを追加しているだけです。

新規追加用のフォーム画面resources\views\article\create.blade.phpファイルと、新規追加完了の表示画面resources\views\article\store.blade.phpファイルを作成します。

<!doctype html>
<html lang="ja">
  <head>
    <title>Laravelチュートリアル</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
  </head>
  <body class="p-3">
    <h1>ブログ新規追加</h1>

    <form method="post" action="/create">
      {{ csrf_field() }}
      <div class="form-group">
        <label for="titleInput">タイトル</label>
        <input type="text" class="form-control" id="titleInput" name="title">
      </div>
      <div class="form-group">
        <label for="bodyInput">内容</label>
        <textarea class="form-control" id="bodyInput" rows="3" name="body"></textarea>
      </div>
      <button type="submit" class="btn btn-primary">新規追加</button>
    </form>

    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
  </body>
</html>

formタグのaction属性は「/create」を指定します。直接URLにリンクしたのか、フォームからPOSTしたのかは、ルーティングが振り分けてくれます。

<!doctype html>
<html lang="ja">
  <head>
    <title>Laravelチュートリアル</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
  </head>
  <body class="p-3">
    <h1>ブログ新規追加完了</h1>

    <div class="alert alert-primary" role="alert">
      新規追加しました。
      <a href="/" class="btn btn-primary">一覧に戻る</a>
    </div>

    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
  </body>
</html>

動作を確認する

一覧画面から新規追加ボタンをクリックして新規追加画面にリンクします。

新規追加画面

ブログ新規追加画面が表示されます。
必要事項を入力し、「新規追加」ボタンをクリックするとデータベースへの新規追加処理が行われます。

新規追加完了画面

データベースの中身を見ると確かに入力したデータが挿入されているのが確認できます。

新規追加データの確認

更新処理(U)を作成する

CRUDのUPDATE、つまり更新処理(修正)を作成します。すでに入力済みのブログのタイトルと内容を修正します。
一覧画面に修正用のリンクを追加して、このリンクが押されたら修正画面が表示されます。このとき既に入力されているデータが初期状態として入力された状態のフォームを表示します。フォームデータを修正し、修正ボタンをクリックすると修正処理されるようにします。

コントローラを編集する

app\Http\Controllers\ArticleController.phpに修正画面用のメソッドを追加します。修正画面用のメソッドは修正のフォームを表示するメソッド(edit)とフォームから送られたデータでデータベースを更新するメソッド(update)の2つを追加します。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Article;

class ArticleController extends Controller
{
    public function index() {
        $articles = Article::all();
        return view('article.index', ['articles' => $articles]);
    }

    public function create() {
        return view('article.create');
    }

    public function store(Request $request) {
        $article = new Article;
        $article->title = $request->title;
        $article->body = $request->body;
        $article->save();

        return view('article.store');
    }

    public function edit(Request $request, $id) {
        $article = Article::find($id);
        return view('article.edit', ['article' => $article]);
    }

    public function update(Request $request) {
        $article = Article::find($request->id);
        $article->title = $request->title;
        $article->body = $request->body;
        $article->save();

        return view('article.update');
    }
}

editメソッドは新規追加のときのようにただ単にビューを表示するのではなく、修正すべきデータのIDを引数で渡しています。具体的には変数$idで修正するデータの主キーを受け取ります。この$idでデータベースのデータを抽出し、抽出したデータをビューに渡しています。
データベースの更新処理は新規のArticleインスタンスを作るのではなく、34行目のとおりすでにあるデータを元にArticleインスタンスを生成し、このインスタンスに新たなデータをセットしていくことで更新処理を行うことができます。使用するメソッドは新規追加の時と同様にsaveメソッドです。

ルーティングを設定する

「インストールしたURL/edit/XX」のアクセスをArticleコントローラのeditメソッドに割り当てていますが、割当るURLを’edit/{id}’と記述することで「/edit/XX」にアクセスしたときに引数としてXXを渡すことができます。ここでは修正する対象のデータのIDを渡す設計にしています。
routes\web.phpを以下のコードに修正します。

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', 'ArticleController@index');
Route::get('create', 'ArticleController@create');
Route::post('create', 'ArticleController@store');
Route::get('edit/{id}', 'ArticleController@edit');
Route::post('edit', 'ArticleController@update');

ビューを作成する

一覧画面のresources\views\article\index.blade.phpファイルに修正用のフォーム画面へのリンクを追加します。

<!doctype html>
<html lang="ja">
  <head>
    <title>Laravelチュートリアル</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
  </head>
  <body class="p-3">
    <h1>ブログ一覧</h1>
    <p><a href="/create" class="btn btn-primary">新規追加</a></p>

    @foreach ($articles as $article)
    <div class="card mb-2">
      <div class="card-body">
        <h4 class="card-title">{{ $article->title }}</h4>
        <h6 class="card-subtitle mb-2 text-muted">{{ $article->updated_at }}</h6>
        <p class="card-text">{{ $article->body }}</p>
        <a href="/edit/{{ $article->id }}" class="card-link">修正</a>
      </div>
    </div>
    @endforeach

    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
  </body>
</html>

19行目で修正画面へのリンクを追加しています。リンク先が「/edit/id」となるように生成しています。

修正用のフォーム画面resources\views\article\edit.blade.phpファイルと、修正完了の表示画面resources\views\article\update.blade.phpファイルを作成します。

<!doctype html>
<html lang="ja">
  <head>
    <title>Laravelチュートリアル</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
  </head>
  <body class="p-3">
    <h1>ブログ修正</h1>

    <form method="post" action="/edit">
      {{ csrf_field() }}
      <input type="hidden" class="form-control" name="id" value="{{ $article->id }}">
      <div class="form-group">
        <label for="titleInput">タイトル</label>
        <input type="text" class="form-control" id="titleInput" name="title" value="{{ $article->title }}">
      </div>
      <div class="form-group">
        <label for="bodyInput">内容</label>
        <textarea class="form-control" id="bodyInput" rows="3" name="body">{{ $article->body }}</textarea>
      </div>
      <button type="submit" class="btn btn-primary">修正</button>
    </form>

    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
  </body>
</html>

新規追加と異なる点として、初期状態でデータが表示されている必要があります。input要素に関してはvalue属性で初期値を設定し、textarea要素には要素の内容として初期値を設定しています。また、更新の場合は主キーのIDも必要になりますのでIDはhidden要素として保持します。

<!doctype html>
<html lang="ja">
  <head>
    <title>Laravelチュートリアル</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
  </head>
  <body class="p-3">
    <h1>ブログ修正完了</h1>

    <div class="alert alert-primary" role="alert">
      修正しました。
      <a href="/" class="btn btn-primary">一覧に戻る</a>
    </div>

    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
  </body>
</html>

動作を確認する

一覧画面から修正画面

一覧画面から修正リンクをクリックして修正画面にリンクします。

修正画面

初期データがすでに入力済みの状態になっています。これを一部修正して修正ボタンをクリックします。

修正完了画面

データベースの中身を見ると確かにデータが修正されているのが確認できます。

修正データの確認

削除処理(D)を作成する

CRUDのDelete、つまり削除処理を作成します。ブログのタイトルと内容をデータベースから削除します。
一覧画面に削除用のリンクを追加して、このリンクが押されると削除するデータが表示されます。この画面には削除ボタンも表示されていて、このボタンがクリックされると削除処理がされるようにします。

コントローラを編集する

app\Http\Controllers\ArticleController.phpに削除用のメソッドを追加します。削除用のメソッドは削除データを表示するメソッド(show)とデータベースからデータを削除するメソッド(delete)の2つを追加します。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Article;

class ArticleController extends Controller
{
    public function index() {
        $articles = Article::all();
        return view('article.index', ['articles' => $articles]);
    }

    public function create() {
        return view('article.create');
    }

    public function store(Request $request) {
        $article = new Article;
        $article->title = $request->title;
        $article->body = $request->body;
        $article->save();

        return view('article.store');
    }

    public function edit(Request $request, $id) {
        $article = Article::find($id);
        return view('article.edit', ['article' => $article]);
    }

    public function update(Request $request) {
        $article = Article::find($request->id);
        $article->title = $request->title;
        $article->body = $request->body;
        $article->save();

        return view('article.update');
    }

    public function show(Request $request, $id) {
        $article = Article::find($id);
        return view('article.show', ['article' => $article]);
    }

    public function delete(Request $request) {
        Article::destroy($request->id);
        return view('article.delete');
    }
}

showメソッドは修正のときと同様にidを受け取りこのデータを抽出してビューに渡します。
deleteメソッドはidを受け取って、そのidのデータをデータベースから削除しています。削除にはdestroyメソッドを使用しています。

ルーティングを設定する

「インストールしたURL/delete/XX」のアクセスをArticleコントローラのshowメソッドに割り当てています。これは修正のときと同じようにURLに指定されたデータをshowメソッドにidとして渡しています。
routes\web.phpを以下のコードに修正します。

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', 'ArticleController@index');
Route::get('create', 'ArticleController@create');
Route::post('create', 'ArticleController@store');
Route::get('edit/{id}', 'ArticleController@edit');
Route::post('edit', 'ArticleController@update');
Route::get('delete/{id}', 'ArticleController@show');
Route::post('delete', 'ArticleController@delete');

ビューを作成する

一覧画面のresources\views\article\index.blade.phpファイルに削除用のフォーム画面へのリンクを追加します。

<!doctype html>
<html lang="ja">
  <head>
    <title>Laravelチュートリアル</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
  </head>
  <body class="p-3">
    <h1>ブログ一覧</h1>
    <p><a href="/create" class="btn btn-primary">新規追加</a></p>

    @foreach ($articles as $article)
    <div class="card mb-2">
      <div class="card-body">
        <h4 class="card-title">{{ $article->title }}</h4>
        <h6 class="card-subtitle mb-2 text-muted">{{ $article->updated_at }}</h6>
        <p class="card-text">{{ $article->body }}</p>
        <a href="/edit/{{ $article->id }}" class="card-link">修正</a>
        <a href="/delete/{{ $article->id }}" class="card-link">削除</a>
      </div>
    </div>
    @endforeach

    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
  </body>
</html>

20行目で削除画面へのリンクを追加しています。リンク先が「/delete/id」となるように生成しています。

削除用の表示画面resources\views\article\show.blade.phpファイルと、削除完了の表示画面resources\views\article\delete.blade.phpファイルを作成します。

<!doctype html>
<html lang="ja">
  <head>
    <title>Laravelチュートリアル</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
  </head>
  <body class="p-3">
    <h1>ブログ削除</h1>

    <form method="post" action="/delete">
      {{ csrf_field() }}
      <input type="hidden" class="form-control" name="id" value="{{ $article->id }}">
      <div class="form-group">
        <label for="titleInput">タイトル</label>
        <input type="text" readonly class="form-control" id="titleInput" name="title" value="{{ $article->title }}">
      </div>
      <div class="form-group">
        <label for="bodyInput">内容</label>
        <textarea readonly class="form-control" id="bodyInput" rows="3" name="body">{{ $article->body }}</textarea>
      </div>
      <button type="submit" class="btn btn-primary">削除</button>
    </form>

    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
  </body>
</html>

データは単に表示させるだけですので、edit.blade.phpとは異なりreadonlyで修正できないようにしています。フォーム部品を使用せずに表示するだけでもよいですがここではedit.blade.phpを流用する形にしています。

<!doctype html>
<html lang="ja">
  <head>
    <title>Laravelチュートリアル</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
  </head>
  <body class="p-3">
    <h1>ブログ削除完了</h1>

    <div class="alert alert-primary" role="alert">
      削除しました。
      <a href="/" class="btn btn-primary">一覧に戻る</a>
    </div>

    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
  </body>
</html>

動作を確認する

一覧画面から削除画面

一覧画面から削除リンクをクリックして削除画面にリンクします。

削除画面

削除対象のデータが表示されます。確認して削除ボタンをクリックします。

削除完了画面

データベースの中身を見ると確かにデータが修正されているのが確認できます。

削除データの確認

改良ポイント

このチュートリアルはあくまで簡単にCRUDをつくってみるというスタンスですので、このままでは業務レベルのアプリケーションには当然なりえません。いくつか改良すべきポイントがあります。
改良すべきポイントは後日このブログで紹介していこうと思っていますので、ここではポイントを列挙するだけにとどめたいと思います。

認証機能

このままでは誰でもブログのデータをかけてしまいますので、ログインした人だけ書けるような機能が当然必要になります。

テンプレート

今回作成したビューファイルは全部で7ファイルありますが、その全てで共通の部分がかなりあります。例えばHTMLのhead部分などです。Laravelでは共通部分をテンプレートとして扱う便利な機能があり普通はこれを使うことが多いと思います。今回のチュートリアルでは触れていませんので、この機能も後日紹介していきたいと考えています。

バリデーション

今回のチュートリアルではフォーム入力値のチェックを行っていません。Webアプリとしては当然必要な機能になります。Laravelでは強力なバリデーション機能がありますのでこちらも後日紹介していきたいです。

ページング

登録データ数が増えると一覧画面の表示がいっぱいになってきます。通常はこういう場合ページ分けをします。Lavaelではページングの機能がありますのでこちらも後日紹介していきたいです。

その他

その他にも、新規追加と修正は同じくくりで扱えるようにしたほうが良いとか、登録完了時に一覧にリダイレクトしたほうがいいとか、データ検索は?とかいろいろ細かいところは唸るほどあると思います。
こうした方が、ということがありましたらコメントいただければブログのネタにさせていただきます。