はじめに
第二回はこちら
第三回はLaravelのControllerについて解説します。
今回のソースはこちらで確認できます。GitHub
Route
前回に引き続きルーティングファイルを見ていきます。
<?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('/', function () {
$now = \Carbon\Carbon::now();
return view('welcome')
->with('now', $now);
});
前回はViewに変数の定義などを書くべきでないためルーティングファイルに記載しましたが、ここに書くのも好ましくありません。あくまでルーティング関連の記述のみにとどめるべきです。
ではどこに書くか、それこそが今回のメインであるControllerです。
Controller
正直自分自身Controllerがなんなのかうまく言語化できていないんですが、ふわっと受付の人みたいなイメージを持っています。今日の日付などは把握していますが、ある部署のXXさんが今どこで何をしているかなどは、その部署に電話をしないと知りえない、といったように、簡単なロジックを自分で、もしくは難しいロジックを問い合わせによって取得し、訪問者に結果を返す(Viewに変数を渡してhtmlとして返す)もの、というように認識しています。ここで言う難しいロジックはModelが担うべきものですが、それについては第四回でやる予定なので、Modelを使わない簡単な実装について解説していきます。
Controllerはapp/Http/Controllers以下に配置します。例のごとくViewに合わせて、WelcomeControllerを作成……と言いたい所ですが、トップページがwelcome
っていうのがあまりしっくりこないので、この機会にindex
に直しましょう。
welcome => index
/**
* welcome.blade.phpからリネーム
*/
@section('title', 'Laravel')
@section('content')
- <div class="v-welcome">
+ <div class="v-index">
<div class="title m-b-md">
Laravel
</div>
/**
* welcome.scssからリネーム
*/
- .v-welcome{
+ .v-index {
.title {
font-size: 84px;
}
.m-b-md {
margin-bottom: 30px;
}
}
// views
@import "views/layouts/frame";
- @import "views/welcome";
+ @import "views/index";
Route::get('/', function () {
$now = \Carbon\Carbon::now();
- return view('welcome')
+ return view('index')
->with('now', $now);
});
PS > npm run production
Controllerを作成
Controllerは、App/Http/Controllerクラスを継承して実装します。
手で書いても良いですが、artisan
コマンドを使って空のコントローラーを生成できます。
PS > php artisan make:controller IndexController
app/Http/Controllers/IndexController.php
が生成されているので、ここにアクションを追加して行きます。
<?php
namespace App\Http\Controllers;
use Carbon\Carbon;
class IndexController extends Controller
{
public function index()
{
$now = Carbon::now();
return view('index')
->with('now', $now);
}
}
- Route::get('/', function () {
- $now = \Carbon\Carbon::now();
-
- return view('welcome')
- ->with('now', $now);
- });
+ Route::get('/', 'IndexController@index')->name('index');
-
Route::get()
の第二引数をコントローラークラス名@メソッド名と指定することで指定URIにアクセス時にそのメソッドへ処理が回ってくるようになります -
Route::get()
の第二引数は、App\Http\Controllersから探されるため、完全な名前空間を指定する必要はありません。-
App\Http\Controllers\Hoge\FugaControllerを指定する場合は、
Route::get('/', 'Hoge\FugaController@index')
と書くことが出来ます。
-
App\Http\Controllers\Hoge\FugaControllerを指定する場合は、
- 後ろについている
->name('index');
は、そのルーティングの名前を指定するものです。- こうすることで、PHP上で
route('index')
が http://localhost:8000 と出力できるようになります -
<a href="{{ route('index') }}>トップページに戻る</a>
といった感じで良く使います
- こうすることで、PHP上で
修正が完了したら、下記のコマンドで確認しましょう。
PS > php artisan route:list
+--------+----------+----------+-------+--------------------------------------------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+----------+-------+--------------------------------------------+--------------+
| | GET|HEAD | / | index | App\Http\Controllers\IndexController@index | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
+--------+----------+----------+-------+--------------------------------------------+--------------+
GET/
にアクセスした際に、App\Http\Controllers\IndexController@indexが呼びだされていることが分かりますね。
このままでも良いですが、TOPのコントローラーはGET/
のアクションしか持たず、ほかのアクションが追加されることも考えづらいのでシングルアクションコントローラーとして実装してみましょう。
シングルアクションコントローラー
わざわざこのような名前がついているということは、通常はコントローラーは複数のアクションを持っているということになります。
アクションとは、HTTPリクエストに対してレスポンスを返す処理のことです。
また、アクションの処理本体の関数の事をアクションメソッドと呼びます。
- public function index()
+ public function __invoke()
{
$now = Carbon::now();
return view('index')
->with('now', $now);
}
- Route::get('/', 'IndexController@index')->name('index')
+ Route::get('/', IndexController::class)->name('index');
シングルアクションコントローラーにするには、アクションメソッド名を__invokeにして、ルーティングでの指定をクラス指定にするだけです。
余談ですが、普段書くときクラス名はシングルアクションコントローラーだと単数形、そうでない場合は複数形にするようにしています。
お問い合わせフォームを作る
特殊な例のみ紹介してもわかりづらいので、複数のアクションメソッドを持つコントローラーの例としてお問い合わせフォームを作ってみましょう。
前準備
Formを実装するにあたって、Formファザードが便利なので、追加します。
PS > composer require laravelcollective/html5.5.*
config/app.php
のaliases
に以下を追加します
'aliases' => [
'App' => Illuminate\Support\Facades\App::class,
'Artisan' => Illuminate\Support\Facades\Artisan::class,
'Auth' => Illuminate\Support\Facades\Auth::class,
'Blade' => Illuminate\Support\Facades\Blade::class,
'Broadcast' => Illuminate\Support\Facades\Broadcast::class,
'Bus' => Illuminate\Support\Facades\Bus::class,
'Cache' => Illuminate\Support\Facades\Cache::class,
'Config' => Illuminate\Support\Facades\Config::class,
'Cookie' => Illuminate\Support\Facades\Cookie::class,
'Crypt' => Illuminate\Support\Facades\Crypt::class,
'DB' => Illuminate\Support\Facades\DB::class,
'Eloquent' => Illuminate\Database\Eloquent\Model::class,
'Event' => Illuminate\Support\Facades\Event::class,
'File' => Illuminate\Support\Facades\File::class,
+ 'Form' => Collective\Html\FormFacade::class,
'Gate' => Illuminate\Support\Facades\Gate::class,
'Hash' => Illuminate\Support\Facades\Hash::class,
'Lang' => Illuminate\Support\Facades\Lang::class,
'Log' => Illuminate\Support\Facades\Log::class,
'Mail' => Illuminate\Support\Facades\Mail::class,
ここに記載することで、プロジェクト上で名前空間を明示的に指定しなくて済むようになります。
概要
- GET
contact
でお問い合わせフォームを表示- 名前とメールアドレス、問い合わせ内容を入力させる
- POST
contact
でお問い合わせフォーム送信- 入力されたデータをログに出力する
- その後完了ページにリダイレクトし、お礼のメッセージを出す
- GET
contact/complate
で完了ページを表示
実装
概要から、必要なビュー数と、コントローラーのアクション数はいくつでしょうか?
HTTP | URI | ビュー | アクション | 処理内容 |
---|---|---|---|---|
GET | contact | 要 | 要 | フォームを表示 |
POST | contact | 不要 | 要 | 送信されたフォーム内容を受け取りログに出力 |
GET | contact/complate | 要 | 要 | フォーム送信後に表示 |
表のようにそれぞれ、2つ,3つとなります。
これを元に、実装していきます。
View
@extends('layouts.frame')
@section('title', 'お問い合わせ')
@section('content')
<div class="v-contact-index">
<h1>お問い合わせフォーム</h1>
<div class="form-box">
{!! Form::open(['url' => route('contact.send'), 'method' => 'post']) !!}
<h2>名前</h2>
{!! Form::text('name', null, ['required' => 'required', 'placeholder' => '山田 太郎']) !!}
<h2>メールアドレス</h2>
{!! Form::email('email', null, ['required' => 'required', 'placeholder' => 'example@hoge.jp']) !!}
<h2>お問い合わせ内容</h2>
{!! Form::textarea('content', null, ['required' => 'required', 'placeholder' => 'お問い合わせ内容']) !!}
<div class="submit-button-box">
{!! Form::submit('送信する') !!}
</div>
{!! Form::close() !!}
</div>
</div>
@endsection
-
Form::open()
~Form::close()
でフォームを作成できます- url,method等指定できます
- CSRF Tokenも同時に生成してくれます
-
Form::text
やForm::email
などで、input
要素を作成できます- 第一引数に
name
、第二引数にvalue
を指定
- 第一引数に
-
Form::submit
で送信ボタンを作成できます
Formファザードの詳しい使い方は以下を参照してください。
Laravel Collective
@extends('layouts.frame')
@section('title', 'お問い合わせ完了')
@section('content')
<div class="v-contact-complate">
<h1>お問い合わせありがとうございます。</h1>
<p>
<a href="{{ route('contact.index') }}">お問い合わせフォームに戻る</a>
</p>
<p>
<a href="{{ route('index') }}">トップページに戻る</a>
</p>
</div>
@endsection
Controller
<?php
namespace App\Http\Controllers;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Log;
class ContactsController extends Controller
{
public function index()
{
return view('contact.index');
}
public function send(Request $request)
{
// 送信された内容を取得
$contents = [
'名前' => $request->input('name'),
'メールアドレス' => $request->input('email'),
'問い合わせ内容' => $request->input('content'),
];
// ログに出力
Log::notice($contents);
// 完了ページにリダイレクト
return redirect()->route('contact.complate');
}
public function complate()
{
return view('contact.complate');
}
}
- リクエストデータは、アクションメソッドの引数として受け取ることが出来ます
- POSTなどのアクションメソッドでは、F5で再送信されるのを防ぐために、
return view()
とせずに、リダイレクトしましょう
Route
Route::get('/', IndexController::class)->name('index');
+
+ // お問い合わせ
+ Route::group(['prefix' => 'contact', 'as' => 'contact.'], function () {
+ Route::get('/', 'ContactsController@index')->name('index');
+ Route::post('/', 'ContactsController@send')->name('send');
+ Route::get('/complate', 'ContactsController@complate')->name('complate');
+ });
- Route::groupはその名の通りルーティングのグループ化をします
-
prefix
は共通のURIを指定します- URIに前方結合され、
contact/
やcontact/complate
というURIになります
- URIに前方結合され、
-
as
はルートの名前を指定します- nameに前方結合され、
contact.index
やcontact.send
というルート名になります
- nameに前方結合され、
-
- URIの先頭の
/
は付けなくても良いです(個人的に気に入って付けているだけなので)
つまり、以下のように書いても同様の結果が得られます。
- Route::get('/', IndexController::class)->name('index');
+ Route::get('', IndexController::class)->name('index');
+
+ // お問い合わせ
+ Route::get('contact', 'ContactsController@index')->name('contact.index');
+ Route::post('contact', 'ContactsController@send')->name('contact.send');
+ Route::get('contact/complate', 'ContactsController@complate')->name('contact.complate');
PS > php artisan route:list
+--------+----------+------------------+------------------+--------------------------------------------------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+------------------+------------------+--------------------------------------------------+--------------+
| | GET|HEAD | / | index | App\Http\Controllers\IndexController | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
| | GET|HEAD | contact | contact.index | App\Http\Controllers\ContactsController@index | web |
| | POST | contact | contact.send | App\Http\Controllers\ContactsController@send | web |
| | GET|HEAD | contact/complate | contact.complate | App\Http\Controllers\ContactsController@complate | web |
+--------+----------+------------------+------------------+--------------------------------------------------+--------------+
実行結果
contact | contact/complate |
---|---|
http://localhost:8000/contact にアクセスし、フォームを送信し完了ページが出た後に、storage/logs/laravel.log
に入力した情報が出力されていれば完了です。
[2018-07-14 02:03:53] local.NOTICE: array (
'名前' => 'とろゝ',
'メールアドレス' => 'toro_ponz@hoge.jp',
'問い合わせ内容' => 'お問い合わせ内容です。お問い合わせ内容です。お問い合わせ内容です。お問い合わせ内容です。',
)
実際にはここでメールを送信する処理を入れたり、Slackに通知したり、DBに保存したりしますが、それはまた別の機会で。
次回
次回はModelについて解説します。