はじめに
概要
Laravelで、開発環境構築から、Web APIの作成と、ページ遷移型CRUDアプリの作成をするまでをまとめてみました。モデル、マイグレーション、コントローラ、ルーティング、ファクトリ、シーダー、ユニットテスト、ビュー、バリデーションといった概念に一通り触れて、その他実用的なTipsも追記しています。
他のWebフレームワークの経験がある方向けに、Laravelではどうするのか、が概観できるようなチュートリアルを目指しました。
動作確認したバージョンは以下の通りです。
- Laravel 5.6
- Homestead 5.2
筆者の環境
- Windows 10 Pro 64ビット / macOS Sierra
- VirtualBox
- vagrant
- IntelliJ IDEA 2018
必要なソフトウェアのインストール
Vagrant Boxのダウンロード
ダウンロードに時間がかかるので、Laravel開発用仮想環境であるHomesteadのVagrant Boxのダウンロードを先に行っておきます。なお、ここで紹介する方法は、Homesteadをプロジェクト毎にインストールする方法となります。
$ vagrant box add laravel/homestead
ホストマシンへのPHPとComposerのインストール
コードの生成や開発用サーバの動作は仮想環境であるHomestead上で行うことになりますが、最初にプロジェクトを作成してHomesteadをインストールするまでの作業はホストマシンで行うため、PHP 7.1と、依存性管理ツールのComposerをホストマシンにインストールします。
- Windows
- Mac
Composerの高速化
Composerの高速化を行います。
プロジェクトの作成とブラウザからのアクセス
プロジェクトの作成
作成するプロジェクト名をhellolaravelとして説明します。親ディレクトリで以下を実行して、プロジェクトを作成します。プロジェクトを作成したら、hellolaravelディレクトリに入ってください。
$ composer create-project --prefer-dist laravel/laravel hellolaravel
$ cd hellolaravel
Homesteadのインストールと起動
Homesteadをインストールします。
$ composer require laravel/homestead --dev
以下を実行し、VagrantfileとHomestead.yamlを作成します。
Windowsの場合
> .\vendor\bin\homestead.bat make
Macの場合
$ php vendor/bin/homestead make
以下を実行し、仮想マシンを起動します。
$ vagrant up
ここで Check your Homestead.yaml file, the path to your private key does not exist.
とエラーが出た場合は、以下のように鍵ペアを作成してください。
Windowsの場合
各種記事をご参照ください。
Macの場合
$ ssh-keygen
HomesteadへのSSHログインとSSLサーバ証明書の取得
以下を実行し、仮想マシンのシェルに入ります。以降のコマンドライン操作は全てこのシェルから行います。
$ vagrant ssh
仮想マシン内で、ホストマシンのコードを指すディレクトリに移動します。
$ cd code
以下を実行し、HomesteadのSSLサーバ証明書をホストマシンにコピーします。
$ cp /etc/nginx/ssl/ca.*.crt ./
ホストマシン上で、 ca.*.crt
をダブルクリックし、信頼されたルート証明機関に追加します。Macの場合はキーチェーンアクセスが起動するので、追加した証明書をダブルクリックし、[信頼]-[SSL]を[常に信頼]にしてください。これにより、ブラウザからSSL/TLSでHomesteadにアクセスできるようになります。なお、万が一この証明書が悪意のある第三者によって作成されていた場合は、セキュリティ上の問題となりますのでご注意ください。
Homesteadへの接続
ホストマシンのhosts
ファイルを編集し、ホスト名homestead.test
からHomesteadにアクセスできるようにします。hosts
ファイルは、Macの場合/etc/hosts
、Windowsの場合C:¥Windows¥System32¥drivers¥etc¥hosts
です。
192.168.10.10 homestead.test
ブラウザから、https://homestead.test
にアクセスし、デフォルトのトップページが表示されることを確認します。
Composerの高速化
改めて、Homestead内で、Composerの高速化を行います。
IntelliJ IDEAの設定
プラグインのインストール
以下のプラグインをインストールします。
- PHP
- Laravel Plugin
- Blade Support
[Open]より、hellolaravelを開きます。
[Preferences(File-Settings)]-[Languages & Frameworks]-[PHP]-[Composer]より、以下の設定をします。
- [Path to composer.json]を設定。
- [Add packages as libraries], [Synchronize IDE Settings with composer.json]をチェック。
- [
composer
executable]を設定。- Windowsの場合: 例えば、
C:\ProgramData\ComposerSetup\bin\composer.bat
- Macの場合: 例えば、
/usr/local/bin/composer
- Windowsの場合: 例えば、
Laravel 5 IDE Helper Generatorのインストール
以下を実行します。
$ composer require barryvdh/laravel-ide-helper --dev
$ composer require doctrine/dbal --dev
config/app.php
のproviders
にBarryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class
を追加します。
'providers' => [
// ...
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
],
以下を実行し、コード補完用ファイルを生成します。これらは必要に応じて随時実行してください。
$ php artisan ide-helper:generate
$ php artisan ide-helper:models
$ php artisan ide-helper:meta
モデルとマイグレーションの追加
モデルの追加
このアプリで取り扱う対象物を、モデルとして作成します。このモデルがデータベース上ではあるテーブルに対応し、このモデルのインスタンスがデータベース上ではテーブルのレコードに対応します。また、モデルを作成すると同時に、データベース上でテーブルやカラムの構築や破棄を行うコードであるマイグレーションも作成します。(公式ドキュメント参照)
$ php artisan make:model Task --migration
上記例では、モデルとしてapp/Task.php
、マイグレーションとしてdatabase/migrations/yyyy_mm_dd_hhmmss_create_tasks_table.php
が生成されます。
モデルのクラスの実装は、Illuminate\Database\Eloquent\Model
を継承しているのみで、特に中身はありませんが、Laravel標準装備のORMであるEloquentが、Task
モデルとデータベース上のtasks
テーブルがマッピングされていることを認識しています。
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
//
}
テーブル名はデフォルトでモデル名の複数形となりますが、モデルに$table
プロパティを設定すれば、個別に設定することもできます。
マイグレーションの追加と実行
マイグレーションでは、以下のようにテーブルの作成と破棄を実装します。マイグレーションのファイル名に日付時刻がついているのは、マイグレーションの実行順序を決定するためです。以下では、文字列型(DB上では、通常varchar
等になります)のカラムであるdescription
を追加しています。カラムの種類については、公式ドキュメントをご参照ください。
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTasksTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->increments('id');
$table->string('description'); // この行を追加
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('tasks');
}
}
では、以下で実際にマイグレーションを実行してデータベース上にテーブルを作成してみます。Homestead環境では、標準でMySQLが動いていますので、特にデータベースサーバのセットアップは必要ありません。
$ php artisan migrate:refresh
以下のように、実際にtasks
テーブルが作成されたことを確認できます。
$ mysql -uhomestead -psecret -Dhomestead
mysql> SHOW TABLES;
+---------------------+
| Tables_in_homestead |
+---------------------+
| migrations |
| password_resets |
| tasks |
| users |
+---------------------+
4 rows in set (0.00 sec)
Web APIの作成
作成したモデルを読み書きするようなWeb APIを作成してみます。
コントローラの作成
まずは、HTTPリクエストを処理するコントローラを作成します。
$ php artisan make:controller TaskApiController
上記コマンドで、app/Http/Controllers/TaskApiController.php
が生成されるので、まずはTask
を作成、取得するようなAPIの実装を書いてみます。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class TaskApiController extends Controller
{
/**
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$task = new \App\Task;
$task->description = $request->input('description');
$task->save();
return response('OK', 200, [
'Location' => url('/api/tasks/' . $task->id)
]);
}
/**
* @param Request $request
* @param string $id
* @return Response
*/
public function show(Request $request, $id)
{
$task = \App\Task::findOrFail($id);
return response()->json([
'id' => $task->id,
'description' => $task->description,
]);
}
}
ルーティングの設定
次に、URLとコントローラ内のメソッドを関連づけるルートを作ります。
routes/api.php
に以下の行を追加します。
Route::post('/tasks', 'TaskApiController@store');
Route::get('/tasks/{id}', 'TaskApiController@show');
Route::post()
やRoute::get()
の第1パラメータがURL上のパスを表します。
なお、後述のweb.php
の場合と異なり、api.php
の場合、上で記述したパスの先頭に/api/
が付加されたものが実際のURL上のパスとなります。これが不要な場合は、\App\Providers\RouteServiceProvider
のmapApiRoutes()
でprefix('api')
の呼び出しを削除してください。
protected function mapApiRoutes()
{
Route::/* prefix('api')
-> */middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
実際にcurlコマンドを使ってPOSTやGETリクエストを送って、正常にレスポンスが返ってくることを確認します。
$ curl http://localhost/api/tasks --data 'description=My Task' -X POST -v
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying ::1...
* connect to ::1 port 80 failed: Connection refused
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 80 (#0)
> POST /api/tasks HTTP/1.1
> Host: localhost
> User-Agent: curl/7.47.0
> Accept: */*
> Content-Length: 19
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 19 out of 19 bytes
< HTTP/1.1 302 Moved Temporarily
< Server: nginx/1.13.6
< Content-Type: text/html; charset=UTF-8
< Transfer-Encoding: chunked
< Connection: keep-alive
< Location: http://localhost/api/tasks/1
< Cache-Control: no-cache, private
< Date: Thu, 05 Apr 2018 07:32:14 GMT
< X-RateLimit-Limit: 60
< X-RateLimit-Remaining: 59
<
* Connection #0 to host localhost left intact
$ curl http://localhost/api/tasks/1 -v
* Trying ::1...
* connect to ::1 port 80 failed: Connection refused
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET /api/tasks/1 HTTP/1.1
> Host: localhost
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.13.6
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: keep-alive
< Cache-Control: no-cache, private
< Date: Thu, 05 Apr 2018 07:33:56 GMT
< X-RateLimit-Limit: 60
< X-RateLimit-Remaining: 59
<
* Connection #0 to host localhost left intact
{"id":1,"description":"My Task"}
ファクトリとSeederの作成
テストのためのダミーデータを生成するファクトリを登録します。Faker
を使えば、いかにもらしいダミーデータを生成できます。config/app.php
に以下を追加して、Faker
が生成するダミーデータの言語を指定してください。
'faker_locale' => 'ja_JP',
以下を実行して、database/factories/TaskFactory.php
を作成してください。
$ php artisan make:factory TaskFactory --model=Task
以下のように、実装してください。
<?php
use Faker\Generator as Faker;
$factory->define(App\Task::class, function (Faker $faker) {
return [
'description' => $faker->streetAddress, // 追加
];
});
次に、ダミーデータをDB上に保存するためのSeederを作成します。
$ php artisan make:seeder TasksTableSeeder
TasksTableSeeder
クラスに、Task
モデルを生成・保存するコードを書きます。
<?php
use Illuminate\Database\Seeder;
class TasksTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// 個別にプロパティを指定することも出来ます。
factory(App\Task::class)->create([
'description' => 'TEST DESCRIPTION',
]);
// 複数個のインスタンスを生成することもできます。
factory(App\Task::class, 50)->create();
}
}
デフォルトのSeederであるDatabaseSeeder
から、作成したTasksTableSeeder
が呼び出されるようにします。
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$this->call([
TasksTableSeeder::class,
]);
}
}
以下を実行し、DB上にテーブルを再作成し、Seederにレコードを生成させます。
$ php artisan migrate:refresh --seed
もし、レコードが生成されないときは、新しく作成したSeederファイルが認識されていない可能性があるので、以下を実行します。
$ composer dump-autoload
DB上にダミーデータが生成されているのが確認できます。
$ mysql -uhomestead -psecret -Dhomestead
mysql> SELECT * FROM tasks;
+----+----------------------------+---------------------+---------------------+
| id | description | created_at | updated_at |
+----+----------------------------+---------------------+---------------------+
| 1 | TEST DESCRIPTION | 2018-04-05 07:46:00 | 2018-04-05 07:46:00 |
| 2 | 佐藤町三宅5-1-2 | 2018-04-05 07:46:00 | 2018-04-05 07:46:00 |
| 3 | 中島町中村10-5-9 | 2018-04-05 07:46:00 | 2018-04-05 07:46:00 |
| 4 | 鈴木町青田6-10-2 | 2018-04-05 07:46:00 | 2018-04-05 07:46:00 |
| 5 | 山田町加藤1-9-3 | 2018-04-05 07:46:00 | 2018-04-05 07:46:00 |
| 6 | 石田町浜田4-2-1 | 2018-04-05 07:46:00 | 2018-04-05 07:46:00 |
| 7 | 小泉町藤本4-7-7 | 2018-04-05 07:46:00 | 2018-04-05 07:46:00 |
| 8 | 江古田町宇野6-8-7 | 2018-04-05 07:46:00 | 2018-04-05 07:46:00 |
| 9 | 山本町田辺10-1-1 | 2018-04-05 07:46:00 | 2018-04-05 07:46:00 |
| 10 | 井高町山本7-5-4 | 2018-04-05 07:46:00 | 2018-04-05 07:46:00 |
なお、開発中にマイグレーションに失敗した場合などは、直接MySQLから全テーブルを削除してやり直すのが、手っ取り早いと思われます。
ユニットテスト
以下を実行し、tests/Feature
、もしくはtests/Unit
以下に、ユニットテストを作ります。
$ php artisan make:test TasksTest
$ #もしくは
$ php artisan make:test TasksTest --unit
GETリクエストをテストする例を以下に示します。HTTPレベルのテストの例は、公式ドキュメントもご参照ください。
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
class TasksTest extends TestCase
{
public function testGet()
{
$response = $this->get('/api/tasks/1');
$response->assertStatus(200);
$response->assertJsonStructure(
[
'id',
'description',
]
);
$response->assertJsonFragment([
'id' => 1,
'description' => 'TEST DESCRIPTION',
]);
}
}
作成したテストは、以下のように、phpunit
で実行できます。
$ phpunit
ページの作成
次に、ブラウザから操作できる、ページ遷移型のCRUD機能を作成します。
コントローラの作成
以下のように、CRUDを行うコントローラを、各ページのアクションのプレースホルダと共に作成します。(公式ドキュメント参照)
$ php artisan make:controller TaskController --resource --model=Task
以下のように、各アクションを実装します。
<?php
namespace App\Http\Controllers;
use App\Task;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
class TaskController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return view('tasks', [
'tasks' => \App\Task::orderBy('id')->get(),
]);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('tasks-create', [
]);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->validateRequest($request);
$task = new \App\Task;
DB::transaction(function() use ($task, $request) {
$task->description = $request->input('description');
$task->save();
});
return redirect(route('tasks.show', [$task]));
}
/**
* Display the specified resource.
*
* @param \App\Task $task
* @return \Illuminate\Http\Response
*/
public function show(Task $task)
{
return view('tasks-show', [
'task' => $task
]);
}
/**
* Show the form for editing the specified resource.
*
* @param \Illuminate\Http\Request $request
* @param \App\Task $task
* @return \Illuminate\Http\Response
*/
public function edit(Request $request, Task $task)
{
// 前回の入力が無いときは、モデルから読み込む
if (is_null($request->old('description'))) {
$request->merge([
'description' => $task->description,
]);
$request->flash();
}
return view('tasks-create', [
]);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Task $task
* @return \Illuminate\Http\Response
*/
public function update(Request $request, Task $task)
{
$this->validateRequest($request);
DB::transaction(function() use ($task, $request) {
$task->description = $request->input('description');
$task->save();
});
return redirect(route('tasks.show', [$task]));
}
/**
* Remove the specified resource from storage.
*
* @param \App\Task $task
* @return \Illuminate\Http\Response
*/
public function destroy(Request $request, Task $task)
{
$task->delete();
$request->session()->flash('status', '削除しました。');
return redirect(route('tasks.index'));
}
/**
* @param Request $request
*/
private function validateRequest(Request $request)
{
$request->validate([
'description' => 'required|max:20',
]);
}
}
ルーティングの設定
以下のようにルーティングを追加します。
Route::resource('tasks', 'TaskController');
これは、個別に以下の設定を行うのと同様の効果があります。
Route::get('/tasks', 'TaskController@index')->name('tasks.index');
Route::get('/tasks/create', 'TaskController@create')->name('tasks.create');
Route::post('/tasks', 'TaskController@store')->name('tasks.store');
Route::get('/tasks/{task}', 'TaskController@show')->name('tasks.show');
Route::get('/tasks/{task}/edit', 'TaskController@edit')->name('tasks.edit');
Route::put('/tasks/{task}', 'TaskController@update')->name('tasks.update');
Route::delete('/tasks/{task}', 'TaskController@destroy')->name('tasks.destroy');
Bootstrap 4のBootstrap 3への置き換え
Laravel 5.6ではBootstrap 4が標準CSSフレームワークとなっていますが、ここではレガシーブラウザへの対応のためBootstrap 3を利用します。
package.json
内の"bootstrap": "^4...
の行を"bootstrap-sass": "^3...
に変更します。
"bootstrap-sass": "^3.3.7",
以下を実行し、必要なnodeモジュールをインストールします。
$ yarn install --no-bin-links
$ yarn global add cross-env
bootstrap.js
をBootstrap 3用に書き換えます。
//require('bootstrap');
require('bootstrap-sass');
app.scss
をBootstrap 3用に書き換えます。
//@import '~bootstrap/scss/bootstrap';
@import "~bootstrap-sass/assets/stylesheets/bootstrap";
Bootstrap 3との単位の整合性のため、以下の行を変更します。
//$font-size-base: 0.9rem;
$font-size-base: 14px;
public/css/app.css
、public/js/app.js
を生成します。
$ yarn run dev
ビューの作成
ビューのフォームを作成する際に、生のHTML要素を用いても良いのですが、バリデーションが失敗したときに、再度入力フォームに以前の値を表示する処理などを書く必要があり、面倒です。このような処理を自動的に行ってくれるLaravel Collectiveを用いてビューを実装します。
以下で、Laravel Collectiveをインストールします。
$ composer require laravelcollective/html
ビューはBladeというテンプレートエンジンで表示させます。
まず、複数ページ間で共有するレイアウトを定義します。(もちろん、レイアウトを定義せずに、直接ビューのページを書くことも可能です。)
<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>チュートリアル</title>
<meta name="csrf-token" content="{{ csrf_token() }}">
<link href="{{ asset('css/app.css') }}?v=20180405" rel="stylesheet">
</head>
<body>
<div id="app">
<nav class="navbar navbar-default navbar-static-top">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="/tasks/">チュートリアル</a>
</div>
</div>
</nav>
@yield('content')
</div>
<script src="{{ asset('js/app.js') }}?v=20180405"></script>
@yield('script')
</body>
</html>
一覧のビューを作成します。@extends
で指定したレイアウトで表示されます。なお、ビューのファイル名の.blade.php
の前の部分が、コントローラ等から呼ばれるview()
関数の引数となっています。
@extends('layouts.app')
@section('content')
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
@if (session('status'))
<div class="alert alert-success">
{{ session('status') }}
</div>
@endif
<div class="panel panel-default">
<div class="panel-body">
<div class="pull-right">
<a href="{{ route('tasks.create') }}" class="btn btn-primary" role="button">新規作成</a>
</div>
</div>
</div>
<div class="clearfix"></div>
<table class="table table-bordered table-striped table-condensed table-hover">
<thead>
<tr>
<th>ID</th>
<th>description</th>
</tr>
</thead>
<tbody>
@foreach ($tasks as $task)
<tr class="task" id="{{ $task->id }}">
<td>{{ $task->id }}</td>
<td>{{ $task->description }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
@endsection
@section('script')
<script type="text/javascript">
$(function() {
$('.task').on('click', function(e) {
window.location = '/tasks/' + $(this).attr('id');
});
});
</script>
@endsection
表示のビューを作成します。
@extends('layouts.app')
@section('content')
<div class="container-fluid">
<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 class="form-horizontal">
<div class="form-group">
<label class="control-label col-sm-3">ID</label>
<div class="col-sm-9">
<div class="checkbox">{{ $task->id }}</div>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-3">description</label>
<div class="col-sm-9">
<div class="checkbox">{{ $task->description }}</div>
</div>
</div>
</div>
<div class="pull-right">
<a class="btn btn-primary" href="{{ route('tasks.edit', ['task' => Route::current()->parameter('task')]) }}" role="button">編集</a>
</div>
</div>
</div>
@endsection
作成、および編集のビューを作成します。
@extends('layouts.app')
@section('content')
<div class="container-fluid">
<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
{!! Form::open([
'url' => (Route::current()->getName() === 'tasks.create')
? route('tasks.store')
: route('tasks.update', ['tasks' => Route::current()->parameter('task')]),
'class' => 'form-horizontal'
]) !!}
@if(Route::current()->getName() === 'tasks.create')
{{ method_field('POST') }}
@else
{{ method_field('PUT') }}
@endif
<div class="pull-right">
{!! Form::button('登録', ['class' => 'btn btn-primary', 'type' => 'submit', 'name' => 'action', 'value' => 'submit']) !!}
</div>
<div class="clearfix"></div>
<div class="form-group">
{!! Form::label('name', 'description', ['class' => 'control-label col-sm-3']) !!}
<div class="col-sm-9">
{!! Form::text('description', null, ['class' => 'form-control', 'placeholder' => 'description']) !!}
<div class="text-danger">{{ $errors->first('description') }}</div>
</div>
</div>
<div class="pull-right">
{!! Form::button('登録', ['class' => 'btn btn-primary', 'type' => 'submit', 'name' => 'action', 'value' => 'submit']) !!}
</div>
{!! Form::close() !!}
@if (!is_null(Route::current()->parameter('task')))
{!! Form::open([
'url' => route('tasks.destroy', ['tasks' => Route::current()->parameter('task')]),
'class' => 'form-horizontal'
]) !!}
{{ method_field('DELETE') }}
<div class="pull-left">
{!! Form::button('削除', ['class' => 'btn btn-danger', 'type' => 'submit']) !!}
</div>
{!! Form::close() !!}
@endif
</div>
</div>
@endsection
これで、ブラウザからhttps://homestead.test/tasks/
にアクセスすれば、ページからCRUD操作ができるようになりました。
バリデーション文言の日本語化
config/app.php
を以下のように書き換えてください。
'locale' => 'ja',
そして、有志の方が作成されたコードなどを利用し、resources/lang/ja/
以下のファイルを作成してください。
Tips
Middlewareによるフィルタ処理
リクエスト処理の前後に何らかの処理を追加するための、他のフレームワークで言うところの「フィルタ」は、LaravelではMiddlewareと呼ばれています。Middlewareの作成と適用については公式ドキュメントをご参照ください。
複雑なバリデーション
バリデーションには様々な種類が用意されています。(公式ドキュメント)
独自のバリデーションを行いたいとき、公式で推奨されているバリデーションルールを追加する方法以外に、簡単に実装する方法としては以下のような方法があります。
use Illuminate\Support\Facades\Validator;
//...
/**
* @param Request $request
*/
private function validateRequest(Request $request)
{
// 既存のバリデーション
$validator = Validator::make($request->all(), [
'description' => 'required|max:20',
]);
// 独自のバリデーション
$validator->after(function($validator) use ($request) {
$description = $request->input('description');
if ($description === 'SECRET') {
$validator->errors()->add('description', 'SECRETは指定できません。');
}
});
$validator->validate();
}
ログのローテート
デフォルトでは、ログはstorage/logs/laravel.log
に追記され続けます。以下のように設定すると、日次で追記先ファイルを変更できます。
'stack' => [
'driver' => 'stack',
'channels' => ['daily'],
],
ソフトデリート(論理削除)
Laravelはソフトデリート(いわゆる論理削除)に対応しています。以下のようにマイグレーションとモデルのコードを書けば、deleted_at
カラムの値に従ってEloquentのクエリが行われるようになります。
<?php
// ...
class CreateTasksTable extends Migration
{
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
// ...
$table->softDeletes();
});
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Task extends Model
{
use SoftDeletes;
public $dates = ['deleted_at'];
}
MySQLのcollation
デフォルトでは、MySQLへの接続設定で、collationがutf8_unicode_ci
になっています。濁点の有り無しを同一視するなど、日本語環境には向いていない場合もありますので、必要に応じて変更してください。
'collation' => 'utf8_general_ci',
Gitリポジトリからのclone
通常Gitリポジトリには.env
は含めません。GitリポジトリからLaravelのプロジェクトをcloneしてきたときは、.env.example
を元に.env
を作り、以下のコマンドでAPP_KEY
を設定する必要があります。
$ php artisan key:generate
また、composer install
も行う必要があります。