Edited at

Laravel入門 - 使い方チュートリアル -

Laravelの使い方をチュートリアル形式でまとめています。

バージョンは5.7ですが、他のバージョンでも大きくは変わらないと思います。

解説をしながら、CRUD処理(登録、取得、更新、削除)を実装していきます。


Laravelのインストール

Laravelのインストールには様々な方法がありますが、ここではComposerを使用します。

Composerについてはこちら

create-projectコマンドを使用することで、インストールと同時にプロジェクトを作成することができます。

% composer create-project laravel/laravel sample

sampleというディレクトリが作成されるので、ディレクトリを移動しましょう。

% cd sample

以降はsampleディレクトリで作業をしていきます。


Laravelプロジェクトのディレクトリ構成

Laravelプロジェクトを作成すると、以下のようなディレクトリを構成します。

─── sample

├── app ・・・アプリケーションのロジック
├── bootstrap ・・・laravelフレームワークの起動コード
├── config ・・・設定ファイル
├── database ・・・MigrationファイルなどDB関連
├── public ・・・Webサーバのドキュメントルート
├── resources ・・・ビューや言語変換用ファイルなど
├── routes ・・・ルーティング用ファイル
├── storage ・・・フレームワークが使用するファイル
├── tests ・・・テストコード
└── vendor ・・・Composerでインストールしたライブラリ


Laravelのバージョンによって構成が異なる場合があります。


この構成に従ってWebアプリケーションを作っていくことになります。

いくつか重要なファイルがあるので、実際に使用する前に簡単に紹介します。


.envファイル

sampleディレクトリ直下に「.env」というファイルがあります。

アプリケーションの環境設定情報を記述するファイルです。

具体的には暗号化キーやデータベースの接続情報を記述します。


GitHubなどPublicなリポジトリを利用される場合は扱いに注意しましょう。



artisanファイル

sampleディレクトリ直下に「artisan」というファイルがあります。

artisanファイルはLaravelのコマンドラインツールであるArtisanの実行ファイルです。

LaravelではArtisanコマンドを使用して様々な操作を行います。

主なものを紹介します。


serveコマンド

PHPの組み込みサーバーでLaravelプロジェクトを動作させます。

% php artisan serve --host=localhost --port=8000

Laravel development server started: <http://localhost:8000>


--hostオプションと--portオプションは省略できます。



route:listコマンド

ルーティング定義の一覧を表示します。

% php artisan route:list

+--------+----------+----------+------+---------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+----------+------+---------+--------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
+--------+----------+----------+------+---------+--------------+

例えば1行目は、ルート(http://localhost:8000 )でGETリクエストを受け付けていることを表しています。

詳しくは後ほど使いながら見てみましょう。


tinkerコマンド

プロジェクトをREPL(Read Eval Print Loop)で起動します。

REPLとは、打ち込んだコードが即時に結果を返してくれる仕組みのことを言います。

% php artisan tinker

Psy Shell v0.9.9 (PHP 7.1.16 — cli) by Justin Hileman
>>> $name = 'Laravel';
=> "Laravel"
>>> echo 'Hello ' . $name;
Hello Laravel⏎
>>>

プロジェクト内のオブジェクトがどんなメソッドを持っていたかなど簡単に確認できます。

tinkerはデバッグ時に力を発揮します。「ビューの作成」の章で説明します。


データベース設定

Laravelプロジェクトを作成したら、まずデータベースの設定をします。

最初は以下の作業が必要になります。


  • 接続設定

  • マイグレーション

  • モデル作成

  • シーディング


接続設定

データベースへの接続情報は.envファイルに記述します。

初期値では以下のようになっています。自身の環境に合わせて設定してください。

DB_CONNECTION=mysql

DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret


上記の場合、ローカルにMySQLをインストールし、homesteadデータベースを作成、homeseteadというユーザーをsecretというパスワードで作成すればOKです。



マイグレーション

マイグレーションとは、テーブルの作成や編集などをSQLによって行うのではなく、PHPのソースで管理する仕組みです。

次の手順で行います。


  1. マイグレーションファイルの作成

  2. マイグレーションの実行


1.マイグレーションファイルの作成

マイグレーションファイルの作成にはArtisanのmake:migrationコマンドを使用します。

ファイル名は何でも構いませんが、どのような操作を行ったのかわかる名前をつけると良いでしょう。

Booksテーブルを作成するマイグレーションファイルを作成します。

% php artisan make:migration create_books_table --create=books

Created Migration: 2018_10_30_085034_create_books_table

マイグレーションファイルはdatabase/migrationsディレクトリに作成されます。


database/migrations/2018_10_30_085034_create_books_table.php

<?php

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

class CreateBooksTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/

public function up()
{
Schema::create('books', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
});
}

/**
* Reverse the migrations.
*
* @return void
*/

public function down()
{
Schema::dropIfExists('books');
}
}


マイグレーションファイルではupメソッドにテーブル作成時の情報、downメソッドにマイグレーションを取り消す際の情報を記述します。

現在の内容は、列IDとタイムスタンプを持つBooksテーブルを作成、取り消し時はBooksテーブルを削除するという内容になっています。

以下のようなテーブルを作成したいとしましょう。



制約

id
AUTO_INCREMENT
PRIMARY KEY

name
VARCHAR(50)
NOT NULL

price
INT
NOT NULL

author
VARCHAR(50)

created_at
timestamp

updated_at
timestamp

この場合、upメソッドを以下のように書き換えます。


database/migrations/2018_10_30_085034_create_books_table.php

public function up()

{
Schema::create('books', function (Blueprint $table) {
$table->increments('id');
$table->string('name', 50);
$table->integer('price');
$table->string('author', 50)->nullable();
$table->timestamps();
});
}

この記述方法はスキーマビルダと呼ばれています。

詳細は公式ドキュメントを参考にしてください。

https://readouble.com/laravel/5.0/ja/schema.html

これでマイグレーションファイルの作成が完了しました。


usersとpassword_resetsのマイグレーションファイルはプロジェクト作成時に自動的に作成されたものです。今回は無視します。



2.マイグレーションの実行

マイグレーションの実行にはArtisanのmigrateコマンドを実行します。

% php artisan migrate

Migrating: 2018_10_30_085034_create_books_table
Migrated: 2018_10_30_085034_create_books_table

これで、MySQLサーバー上にテーブルが作成されます。


MySQL

mysql> show tables;

+-------------------+
| Tables_in_laravel |
+-------------------+
| books |
| migrations |
+-------------------+
2 rows in set (0.00 sec)

booksテーブルが指定された通りに作成されているか確認しましょう。


MySQL

mysql> desc books;

+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| name | varchar(50) | NO | | NULL | |
| price | int(11) | NO | | NULL | |
| author | varchar(50) | YES | | NULL | |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+------------+------------------+------+-----+---------+----------------+
6 rows in set (0.01 sec)


migrationsテーブルにはマイグレーションの情報が保存されます。


マイグレーションを取り消したい場合はmigrate:rollbackコマンド、またはmigrate:resetコマンドを使用します。

migrate:rollbackコマンドは直前のマイグレーションのみ取り消します。

% php artisan migrate:rollback

Rolling back: 2018_10_30_085034_create_books_table
Rolled back: 2018_10_30_085034_create_books_table

migrate:resetは全てのマイグレーションを取り消します。

% php artisan migrate:reset

Rolling back: 2018_10_30_085034_create_books_table
Rolled back: 2018_10_30_085034_create_books_table


モデルの作成

モデルはテーブルとマッピングされたオブジェクトです。

DB操作を行うためのクラスになります。

詳しくは「Eloquent ORM」の章で説明します。

モデルはArtisanのmake:modelコマンドで作成します。

% php artisan make:model Book

Model created successfully.

モデルはApp直下に作成されます。


App/Book.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Book extends Model
{
//
}


中身は空ですが、このままで構いません。


モデルの命名規則

モデルは命名規則によってテーブルとマッピングされます。

テーブル名の単数形を名前につけることで、自動的にそのテーブルとマッピングします。


シーディング

シーディングはテストデータやマスタデータなどのアプリケーション起動時に必要なレコードをコマンドで登録する仕組みです。

次の手順で実行します。


  1. シーダーファイルの作成

  2. シーディングの実行


1.シーダーファイルの作成

シーダーファイルはArtisanのmake:seederコマンドを使用して作成します。

% php artisan make:seeder BooksTableSeeder

Seeder created successfully.

database/seedsディレクトリにBooksTableSeeder.phpが作成されます。


database/seeds/BooksTableSeeder.php

<?php

use Illuminate\Database\Seeder;

class BooksTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/

public function run()
{
//
}
}


Booksテーブルに以下のレコードを登録したいとしましょう。

ID
NAME
PRICE
AUTHOR
CREATED_AT
UPDATED_AT

1
PHP Book
2000
PHPER
現在時刻
現在時刻

2
Laravel Book
3000
NULL
現在時刻
現在時刻

3
Ruby Book
2500
Rubyist
現在時刻
現在時刻

その場合は、シーダーファイルのrunメソッドを以下のように修正します。


database/seeds/BooksTableSeeder.php

public function run()

{
// テーブルのクリア
DB::table('books')->truncate();

// 初期データ用意(列名をキーとする連想配列)
$books = [
['name' => 'PHP Book',
'price' => 2000,
'author' => 'PHPER'],
['name' => 'Laravel Book',
'price' => 3000,
'author' => null],
['name' => 'Ruby Book',
'price' => 2500,
'author' => 'Rubyist']
];

// 登録
foreach($books as $book) {
\App\Book::create($book);
}
}


さらにDatabaseSeeder.phpのrunメソッドを以下のように修正します。runメソッド内でcallしたクラスが、シーディングコマンドで実行されるようになります。


database/seeds/DatabaseSeeder.php

public function run()

{
// BooksTableSeederを読み込むように指定
$this->call(BooksTableSeeder::class);
}


2.シーディングの実行

シーディングを実行するにはArtisanのdb:seedコマンドを実行します。

% php artisan db:seed

Seeding: BooksTableSeeder
Database seeding completed successfully.

完了したらテーブルを確認しましょう。


MySQL

mysql> select * from books;

+----+--------------+-------+---------+---------------------+---------------------+
| id | name | price | author | created_at | updated_at |
+----+--------------+-------+---------+---------------------+---------------------+
| 1 | PHP Book | 2000 | PHPER | 2018-10-30 10:11:33 | 2018-10-30 10:11:33 |
| 2 | Laravel Book | 3000 | NULL | 2018-10-30 10:11:33 | 2018-10-30 10:11:33 |
| 3 | Ruby Book | 2500 | Rubyist | 2018-10-30 10:11:33 | 2018-10-30 10:11:33 |
+----+--------------+-------+---------+---------------------+---------------------+
3 rows in set (0.00 sec)

以上でデータベース関連の設定は完了です。

作業量が多かったかもしれませんが一度作ってしまえば、プロジェクトを共有しているユーザー全てがmigratedb:seedの2つのコマンドのみで、DBの初期化を行うことができます。


ルーティング

データベースの準備が整ったらアプリケーションを作成します。

まずはルーティングから行なっていきましょう。

ルーティングとは、クライアントからのリクエストを受け付け、その内容によって処理を振り分けることです。

ルーティング情報はroutes/web.phpファイルに記述します。


routes/web.php

<?php

Route::get('/', function () {
return view('welcome');
});

すでに一つルーティングが定義されています。

これは、ルート(/)にGETリクエストがきた場合に、welcomeというビューをレスポンスとして返すという意味です。

welcomeはresources/views/welcome.blade.phpのことです。


Laravelはview関数を呼び出すと、resources/views/ディレクトリを探しにいきます。

その中からファイル名(拡張子除く)が一致するものを返す仕組みとなっています。


Artisanコマンドでサーバーを立ち上げ、http://localhost:8000/ にアクセスしてみるとwelcome.blade.phpの内容が表示されます。


直接ビューを返すのではなく、コントローラを経由するようにルーティングするには以下のように記述します。


routes/web.php

<?php

Route::get('book', 'BookController@index');

上記のように記述した場合、「localhost:8000/bookにGETリクエストがきたら、BookControllerのindexメソッドに処理を振り分けて」という意味になります。この段階ではまだBookControllerを作成していないので正常に動作しません。


BookControllerは「Controllerの作成」の章で作成します。



ルートパラメータ

コントローラを作る前にルートパラメータについても知っておきましょう。


routes/web.php

<?php

Route::get('book/{id}', 'BookController@show');

上記のように記述した場合、「localhost:8000/book/1にGETリクエストがきたら、BookControllerのshowメソッドに処理を振り分けて」という意味になります。

BookControllerではshowメソッドに引数を持たせるようにします。


app/Http/Controllers/BookController.php(未作成)

public function show($id)

{
return view('book', ['book' => Book::findOrFail($id)]);
}

URIに指定された「1」という数字が$idという引数の中に代入されます。


Restfulリソースコントローラ

Route::resourceメソッドを使用することで、Restfulなルーティングを行うことができます。


routes/web.php

<?php

Route::resource('book', 'BookController');

この場合、以下のようにルーティングされます。

リクエストメソッド
URI
コントローラー
CRUD画面を作る際の主な用途

GET
/book
BookController@index
一覧画面の表示

GET
/book/{book}
BookController@show
詳細画面の表示

GET
/book/create
BookController@create
登録画面の表示

POST
/book
BookController@store
登録処理

GET
/book/{book}/edit
BookController@edit
編集画面の表示

PUT
/book/{book}
BookController@update
編集処理

DELETE
/book/{book}
BookController@destroy
削除処理


以降、resourceメソッドでルーティングしている前提でコントローラを作成します。



コントローラの作成

ルーティングを設定したら次はコントローラを作成します。

コントローラはルーティングされてきたリクエストを受け取り、レスポンスを作成する役割を果たします。

レスポンスとしてHTMLを返す場合はViewに処理を依頼します。

コントローラの作成にはArtisanのmake:controllerコマンドを使用します。

% php artisan make:controller BookController

Controller created successfully.

コントローラはapp/Http/Controllersディレクトリに作成されます。

以下のようにindexメソッドとeditメソッドを追加してください。

use App/Bookの記述を忘れないようにしてください。


app/Http/Controllers/BookController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Book;

class BookController extends Controller
{
public function index()
{
// DBよりBookテーブルの値を全て取得
$books = Book::all();

// 取得した値をビュー「book/index」に渡す
return view('book/index', compact('books'));
}

public function edit($id)
{
// DBよりURIパラメータと同じIDを持つBookの情報を取得
$book = Book::findOrFail($id);

// 取得した値をビュー「book/edit」に渡す
return view('book/edit', compact('book'));
}
}


コントローラのメソッドでビューを返したい場合、view関数を使用します。

view関数は第一引数にビューの名前、第二引数にビューに渡したい値を設定します。

第二引数は連想配列で渡すようにします。

compact関数を使うことで簡単に連想配列を渡すことができます。

例えば、edit関数のcompact('book')['book' => $book]としているのと同意です。

ルーティングされているメソッドのうち、とりあえずindexメソッドとeditメソッドを用意しました。

DBからの値取得はEloquentという仕組みを利用しています。

Eloquentに関しては「Eloquent ORM」の章で説明します。


ビューの作成

では最後にビューを作成しましょう。

ビューはresources/viewsディレクトリに作成します。

bookディレクトリを作成し、その中にindex.blade.phpを作成しましょう。


resources/views/book/index.blade.php

<head>

<title>Laravel Sample</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<div class="container ops-main">
<div class="row">
<div class="col-md-12">
<h3 class="ops-title">書籍一覧</h3>
</div>
</div>
<div class="row">
<div class="col-md-11 col-md-offset-1">
<table class="table text-center">
<tr>
<th class="text-center">ID</th>
<th class="text-center">書籍名</th>
<th class="text-center">価格</th>
<th class="text-center">著者</th>
<th class="text-center">削除</th>
</tr>
@foreach($books as $book)
<tr>
<td>
<a href="/book/{{ $book->id }}/edit">{{ $book->id }}</a>
</td>
<td>{{ $book->name }}</td>
<td>{{ $book->price }}</td>
<td>{{ $book->author }}</td>
<td>
<form action="/book/{{ $book->id }}" method="post">
<input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<button type="submit" class="btn btn-xs btn-danger" aria-label="Left Align"><span class="glyphicon glyphicon-trash"></span></button>
</form>
</td>
</tr>
@endforeach
</table>
<div><a href="/book/create" class="btn btn-default">新規作成</a></div>
</div>
</div>

Laravelではビューの作成にBladeというテンプレートエンジンを用いています。

Bladeでは{{ $variables }}とすることで、コントローラから受け取った値を画面に出力することができます。

また@foreach($array as $elem)@if($bool)と記述することで制御構文を使用することもできます。

その場合、制御構文の終端は@endforeach@endifと記述します。

Bladeの詳細に関しては以下のサイトで確認してください。

https://readouble.com/laravel/5.7/ja/blade.html

ここまで完成したら、Artisanでサーバーを立ち上げ、http://localhost:8000/book にアクセスしてみましょう。

一覧画面が表示されるはずです。

ここで一度流れを整理しておきましょう。

route:listコマンドでルーティングを再確認します。

% php artisan route:list

+--------+-----------+------------------+--------------+---------------------------------------------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+-----------+------------------+--------------+---------------------------------------------+--------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
| | GET|HEAD | book | book.index | App\Http\Controllers\BookController@index | web |
| | POST | book | book.store | App\Http\Controllers\BookController@store | web |
| | GET|HEAD | book/create | book.create | App\Http\Controllers\BookController@create | web |
| | GET|HEAD | book/{book} | book.show | App\Http\Controllers\BookController@show | web |
| | PUT|PATCH | book/{book} | book.update | App\Http\Controllers\BookController@update | web |
| | DELETE | book/{book} | book.destroy | App\Http\Controllers\BookController@destroy | web |
| | GET|HEAD | book/{book}/edit | book.edit | App\Http\Controllers\BookController@edit | web |
+--------+-----------+------------------+--------------+---------------------------------------------+--------------+

今、ブラウザからhttp://localhost:8000/book にアクセスしました。

つまりGETリクエストでbookにアクセスしたことになります。

その場合、BookControllerのindexメソッドが呼び出されるように設定されていました。

+--------+-----------+------------------+--------------+---------------------------------------------+--------------+

| Domain | Method | URI | Name | Action | Middleware |
+--------+-----------+------------------+--------------+---------------------------------------------+--------------+
| | GET|HEAD | book | book.index | App\Http\Controllers\BookController@index | web |
+--------+-----------+------------------+--------------+---------------------------------------------+--------------+

そのため、BookControllerのindexメソッドが呼び出されます。

indexメソッドでは、書籍の一覧情報を取得し、resources/views/book/index.blade.phpに渡しました。


app/Http/Controllers/BookController.php

public function index()

{
// DBよりBookテーブルの値を全て取得
$books = Book::all();

// 取得した値をビュー「book/index」に渡す
return view('book/index', compact('books'));
}


最後にindex.blade.phpでは受け取った値を元に画面を生成し、それが最終的にブラウザに表示されたというわけです。

では、次にedit.blade.phpを作成しましょう。今の流れを意識しながら作成してください。


resources/views/index.blade.php

<head>

<title>Laravel Sample</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<div class="container ops-main">
<div class="row">
<div class="col-md-6">
<h2>書籍登録</h2>
</div>
</div>
<div class="row">
<div class="col-md-8 col-md-offset-1">
<form action="/book/{{ $book->id }}" method="post">
<!-- updateメソッドにはPUTメソッドがルーティングされているのでPUTにする -->
<input type="hidden" name="_method" value="PUT">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<div class="form-group">
<label for="name">書籍名</label>
<input type="text" class="form-control" name="name" value="{{ $book->name }}">
</div>
<div class="form-group">
<label for="price">価格</label>
<input type="text" class="form-control" name="price" value="{{ $book->price }}">
</div>
<div class="form-group">
<label for="author">著者</label>
<input type="text" class="form-control" name="author" value="{{ $book->author }}">
</div>
<button type="submit" class="btn btn-default">登録</button>
<a href="/book">戻る</a>
</form>
</div>
</div>
</div>

これで編集画面が作成できました。書籍一覧のIDをクリックすると、編集画面に遷移します。


ビューの継承

ところで、ヘッダー部分は書籍一覧画面と書籍登録画面で同じですね。

こういう時、ビューの継承を利用すると効率的に作成することができます。

親ビューになるlayout.blade.phpを作成します。共通となる要素のみ記述します。


views/book/layout.blade.php

<head>

<title>Laravel Sample</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
@yield('content')

次にindex.blade.phpを書き換えます。

@extends('book/layout')

@section('content')
<div class="container ops-main">
<div class="row">
<div class="col-md-12">
<h3 class="ops-title">Books</h3>
</div>
</div>
<div class="row">
<!-- 中略 -->
</div>
@endsection

共通要素を消去し、代わりに@extends('book/layout')を記述します。

これで、layout.blade.phpを継承することを宣言したことになります。

すると、layoutの@yield('content')部分に@section('content')@endsectionで囲った部分が挿入されます。

では、edit.blade.phpにもlayoutを継承させましょう。

@extends('book/layout')

@section('content')
<div class="container ops-main">
<div class="row">
<div class="col-md-6">
<h2>書籍登録</h2>
</div>
</div>
<!-- 中略 -->
</div>
@endsection

共通のCSSの読み込みや、フッター、ヘッダーなどをlayout.blade.phpに記入することで、継承している全ての画面に反映させることができます。

次はDB操作の処理を実装していくことになりますが、その前にEloquentについて学びましょう。


Eloquent ORM

EloquentはLaravelでデータ操作をするための実装です。

Bookモデルというのを作成したのを思い出してください。

BookモデルはBookテーブルにマッピングされており、テーブルの登録や取得更新などの操作を持っています。

Bookモデルとした場合、特に指定しなければ命名規則によりbooksテーブルとマッピングされます。


レコードの取得

モデルにマッピングされたテーブルの全てのレコードを取得するにはallメソッドを利用します。

tinkerを立ち上げてDB操作してみましょう。

use \App\Book;を忘れないようにしてください。

% php artisan tinker

Psy Shell v0.9.9 (PHP 7.1.16 cli) by Justin Hileman
>>> use \App\Book;
>>> $books = Book::all();
=> Illuminate\Database\Eloquent\Collection {#2925
all: [
App\Book {#2926
id: 1,
name: "PHP Book",
price: 2000,
author: "PHPER",
created_at: "2018-10-30 10:33:14",
updated_at: "2018-10-30 10:33:14",
},
App\Book {#2927
id: 2,
name: "Laravel Book",
price: 3000,
author: null,
created_at: "2018-10-30 10:33:14",
updated_at: "2018-10-30 10:33:14",
},
App\Book {#2928
id: 3,
name: "Ruby Book",
price: 2500,
author: "Rubyist",
created_at: "2018-10-30 10:33:14",
updated_at: "2018-10-30 10:33:14",
},
],
}
>>>

countメソッドやforeachを利用することができます。

>>> $books->count();

=> 3


取得条件の設定

取得レコードに条件を設定する場合はwhereメソッドを使います。

>>> $expensiveBooks = Book::where('price', '>=', 2500)->get();

=> Illuminate\Database\Eloquent\Collection {#2931
all: [
App\Book {#2903
id: 2,
name: "Laravel Book",
price: 3000,
author: null,
created_at: "2018-10-30 10:33:14",
updated_at: "2018-10-30 10:33:14",
},
App\Book {#2929
id: 3,
name: "Ruby Book",
price: 2500,
author: "Rubyist",
created_at: "2018-10-30 10:33:14",
updated_at: "2018-10-30 10:33:14",
},
],
}
>>>

単一行の取得であれば、findメソッドを使うことで効率的に記述できます。

findメソッドは主キーによる検索を行います。

>>> $book = Book::find(2);

=> App\Book {#2923
id: 2,
name: "Laravel Book",
price: 3000,
author: null,
created_at: "2018-10-30 10:33:14",
updated_at: "2018-10-30 10:33:14",
}
>>>


レコードの登録

登録するにはモデルのインスタンスを作成し、saveメソッドを呼び出します。

>>> $newBook = new Book();

=> App\Book {#2934}
>>> $newBook->name = 'Python Book';
=> "Python Book"
>>> $newBook->price = 3500;
=> 3500
>>> $newBook->save();
=> true
>>> $pythonBook = Book::find(4);
=> App\Book {#2932
id: 4,
name: "Python Book",
price: 3500,
author: null,
created_at: "2018-10-31 01:51:38",
updated_at: "2018-10-31 01:51:38",
}


レコードの更新

更新はモデルを取得後、プロパティを変更してsaveメソッドを呼び出します。

>>> $pythonBook;

=> App\Book {#2932
id: 4,
name: "Python Book",
price: 3500,
author: null,
created_at: "2018-10-31 01:51:38",
updated_at: "2018-10-31 01:51:38",
}
>>> $pythonBook->author = 'Pythonista';
=> "Pythonista"
>>> $pythonBook->save();
=> true
>>> $pythonBook = Book::find(4);
=> App\Book {#2910
id: 4,
name: "Python Book",
price: 3500,
author: "Pythonista",
created_at: "2018-10-31 01:51:38",
updated_at: "2018-10-31 01:54:45",
}
>>>

複数件の更新も可能です。

>>> Book::where('price', '>=', 2500)->update(['price' => 2500]);

=> 3
>>> Book::all();
=> Illuminate\Database\Eloquent\Collection {#2912
all: [
App\Book {#2908
id: 1,
name: "PHP Book",
price: 2000,
author: "PHPER",
created_at: "2018-10-30 10:33:14",
updated_at: "2018-10-30 10:33:14",
},
App\Book {#2937
id: 2,
name: "Laravel Book",
price: 2500,
author: null,
created_at: "2018-10-30 10:33:14",
updated_at: "2018-10-31 01:57:27",
},
App\Book {#2935
id: 3,
name: "Ruby Book",
price: 2500,
author: "Rubyist",
created_at: "2018-10-30 10:33:14",
updated_at: "2018-10-31 01:57:27",
},
App\Book {#2932
id: 4,
name: "Python Book",
price: 2500,
author: "Pythonista",
created_at: "2018-10-31 01:51:38",
updated_at: "2018-10-31 01:57:27",
},
],
}
>>>


レコードの削除

削除にはdeleteメソッドを使います。

>>> $pythonBook;

=> App\Book {#2910
id: 4,
name: "Python Book",
price: 3500,
author: "Pythonista",
created_at: "2018-10-31 01:51:38",
updated_at: "2018-10-31 01:54:45",
}
>>> $pythonBook->delete();
=> true
>>> $pythonBook = Book::find(4);
=> null
>>>


CRUD機能の実装

では、残していた更新、登録、削除の機能を実装していきましょう。

まずは、更新を作成します。


更新処理

編集画面はできているので、コントローラにupdateメソッドを定義します。


app/Http/Controllers/BookController.php

public function update(Request $request, $id)

{
$book = Book::findOrFail($id);
$book->name = $request->name;
$book->price = $request->price;
$book->author = $request->author;
$book->save();

return redirect("/book");
}


updateメソッドの$idにはURIbook/{book}の{book}の部分の値が代入されます。

つまり、http://localhost:8000/book/3にPUTメソッドでアクセスしたら、updateメソッドが呼び出され、$idに3が代入されます。

$requestはクライアントからのリクエスト情報が入っています。

クライアントが入力した値を取得する場合は$request->[要素名]で取得します。


削除処理

書籍一覧画面にすでに削除ボタンは作成しているので、コントローラにdestroyメソッドを定義します。


app/Http/Controllers/BookController.php

public function destroy($id)

{
$book = Book::findOrFail($id);
$book->delete();

return redirect("/book");
}



登録処理

最後に登録処理ですが、画面はedit.blade.phpとほぼ同じなので再利用するようにしましょう。

まずviews/bookディレクトリに共通部分を記述するform.blade.phpを作成します。

edit.blade.phpの@section内部を全て移動させます。


resources/views/book/form.blade.php

<div class="container ops-main">

<div class="row">
<div class="col-md-6">
<h2>書籍登録</h2>
</div>
</div>
<div class="row">
<div class="col-md-8 col-md-offset-1">
<form action="/book/{{ $book->id }}" method="post">
<!-- updateメソッドにはPUTメソッドがルーティングされているのでPUTにする -->
<input type="hidden" name="_method" value="PUT">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<div class="form-group">
<label for="name">書籍名</label>
<input type="text" class="form-control" name="name" value="{{ $book->name }}">
</div>
<div class="form-group">
<label for="price">価格</label>
<input type="text" class="form-control" name="price" value="{{ $book->price }}">
</div>
<div class="form-group">
<label for="author">著者</label>
<input type="text" class="form-control" name="author" value="{{ $book->author }}">
</div>
<button type="submit" class="btn btn-default">登録</button>
<a href="/book">戻る</a>
</form>
</div>
</div>
</div>

次に新規登録時と更新時で異なる部分だけ@ifを使用して分岐させます。

今回の場合、formタグ部分だけ書き換えればOKです。


resources/views/book/form.blade.php

<div class="container ops-main">

<div class="row">
<div class="col-md-6">
<h2>書籍登録</h2>
</div>
</div>
<div class="row">
<div class="col-md-8 col-md-offset-1">
@if($target == 'store')
<form action="/book" method="post">
@elseif($target == 'update')
<form action="/book/{{ $book->id }}" method="post">
<input type="hidden" name="_method" value="PUT">
@endif
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<div class="form-group">
<label for="name">書籍名</label>
<input type="text" class="form-control" name="name" value="{{ $book->name }}">
</div>
<div class="form-group">
<label for="price">価格</label>
<input type="text" class="form-control" name="price" value="{{ $book->price }}">
</div>
<div class="form-group">
<label for="author">著者</label>
<input type="text" class="form-control" name="author" value="{{ $book->author }}">
</div>
<button type="submit" class="btn btn-default">登録</button>
<a href="/book">戻る</a>
</form>
</div>
</div>
</div>

次はedit.blade.phpを以下のように書き換えます。


resources/views/book/edit.blade.php

@extends('book/layout')

@section('content')
@include('book/form', ['target' => 'update'])
@endsection

@includeを使用して、form.blade.phpを@section内に挿入します。

その際、target変数にupdateという値を渡します。

では、新しくviews/book/ディレクトリにcreate.blade.phpを作成しましょう。


resources/views/book/create.blade.php

@extends('book/layout')

@section('content')
@include('book/form', ['target' => 'store'])
@endsection

targetをstoreに書き換えるだけです。

これで、新規登録の時と編集の時で、formの内容が分岐される仕組みができます。

登録時のコントローラが未作成ですのでcreateメソッドとstoreメソッドを追加しましょう。


app/Http/Controllers/BookController.php

public function create()

{
// 空の$bookを渡す
$book = new Book();
return view('book/create', compact('book'));
}

public function store(Request $request)
{
$book = new Book();
$book->name = $request->name;
$book->price = $request->price;
$book->author = $request->author;
$book->save();

return redirect("/book");
}


create.blade.phpでは$bookを受け取るようになっています。

そこで、createメソッドでは空のインスタンスを渡すようにします。

画面には空の状態で出力されるようになります。


変数を渡さないとUndefined Variableエラーになってしまいます。


以上でCRUD処理全てが実装できました。

ルーティングと流れを意識しながら色々動かして見てください。


バリデーション

最後にバリデーションを作成しましょう。

バリデーションとは入力内容のチェックのことです。

いくつか方法がありますが、フォームリクエストを作成する方法を紹介します。

フォームリクエストを作成するにはArtisanのmake:requestを使用します。

% php artisan make:request BookRequest

Request created successfully.

app/Http/Requestsディレクトリが作成され、そこにBookRequest.phpが作成されます。

BookRequest.phpのrulesメソッドを以下のように修正します。


app/Http/Requests/BookRequest.php

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class BookRequest extends FormRequest
{
public function rules()
{
return [
'name' => 'required|string|max:50',
'price' => 'required|integer',
'author' => 'nullable|string|max:50',
];
}
}


連想配列の値のはバリデーションルールを記述します。

名前
ルール

required
必須入力

string
文字列のみ

max:50
50文字以下

integer
数値のみ

nullable
空もOK

バリデーションルールの詳細に関しては公式サイトを参考にしてください。

https://readouble.com/laravel/5.7/ja/validation.html

次にBookController.phpのstoreメソッドとupdateメソッドの引数の型指定をRequestからBookRequestに書き換えます。

use App\Http\Requests\BookRequest;を忘れないようにしてください。


app/Http/Contorllers/BookController.php

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Http\Requests\BookRequest;
use App\Book;

class BookController extends Controller
{
// 中略

public function store(BookRequest $request)
{
$book = new Book();
$book->name = $request->name;
$book->price = $request->price;
$book->author = $request->author;
$book->save();

return redirect("/book");
}

public function update(BookRequest $request, $id)
{
$book = Book::findOrFail($id);
$book->name = $request->name;
$book->price = $request->price;
$book->author = $request->author;
$book->save();

return redirect("/book");
}

// 中略
}


これで書籍名を空にしたまま登録や編集しようとすると、同じ画面にリダイレクトするようになります。

このままでは何が起きているのかわからないので、エラーメッセージを表示するようにしましょう。

views/bookディレクトリにmessage.blade.phpを作成します。

<div class="row">

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

これをform.blade.phpに挿入します。

<div class="container ops-main">

<div class="row">
<div class="col-md-6">
<h2>書籍登録</h2>
</div>
</div>
<div class="row">
<div class="col-md-8 col-md-offset-1">

@include('book/message')

<!-- 中略 -->
</div>
</div>
</div>

これでエラーメッセージが表示されるようになります。

以上でCRUD機能を持つWebアプリケーションが完成しました。

以降は認証機能などを追加するとさらに理解が深まると思います。

最後に補足としてPsyによるデバッグの方法を載せておきます。


(補足)Psyによるデバッグ

Psyを使用して効率的にデバッグすることができます。

BookControllerのeditメソッドを以下のように書き換えてみましょう。

public function edit($id)

{
// DBよりURIパラメータと同じIDを持つBookの情報を取得
$book = Book::findOrFail($id);

// tinkerによるデバッグ
eval(\Psy\sh());

// 取得した値をView「book/edit」に渡す
return view('book/edit', compact('book'));
}

サーバーを起動して、書籍編集画面にアクセスしてみましょう。

コンソールを見てみると処理がeval(\Psy\sh());を記述した箇所で処理が停止しています。

sy Shell v0.9.9 (PHP 7.1.16 — cli-server) by Justin Hileman

23|
24| // tinkerによるデバッグ
> 25| eval(\Psy\sh());
26|
27| // 取得した値をView「book/edit」に渡す

この状態でecho $book;とタイプすると$book変数の中身が表示されます。

echo $book;

{"id":1,"name":"PHP Book","price":2000,"author":"PHPER","created_at":"2018-10-30 10:33:14","updated_at":"2018-10-30 10:33:14"}⏎

また、lsコマンドで現在参照可能な変数の一覧を表示させることができます。

ls

Variables: $book, $id, $this