LoginSignup
3
2

More than 5 years have passed since last update.

LaravelによるWebアプリケーション開発入門 第三回 Controller

Last updated at Posted at 2018-07-15

はじめに

第二回はこちら
第三回はLaravelのControllerについて解説します。
今回のソースはこちらで確認できます。GitHub

Route

前回に引き続きルーティングファイルを見ていきます。

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('/', 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

resources/views/index.blade.php
  /**
   * welcome.blade.phpからリネーム
   */

  @section('title', 'Laravel')

  @section('content')
-   <div class="v-welcome">
+   <div class="v-index">
      <div class="title m-b-md">
        Laravel
      </div>
resources/assets/sass/views/index.scss
  /**
   * welcome.scssからリネーム
   */

- .v-welcome{
+ .v-index {
    .title {
      font-size: 84px;
    }
    .m-b-md {
      margin-bottom: 30px;
    }
  }
resources/assets/sass/app.scss
  // views
  @import "views/layouts/frame";
- @import "views/welcome";
+ @import "views/index";
routes/web.php
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 が生成されているので、ここにアクションを追加して行きます。

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);
    }
}
routes/web.php
- 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')と書くことが出来ます。
  • 後ろについている->name('index');は、そのルーティングの名前を指定するものです。
    • こうすることで、PHP上でroute('index')http://localhost:8000 と出力できるようになります
    • <a href="{{ route('index') }}>トップページに戻る</a>といった感じで良く使います

修正が完了したら、下記のコマンドで確認しましょう。

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リクエストに対してレスポンスを返す処理のことです。
また、アクションの処理本体の関数の事をアクションメソッドと呼びます。

app/Http/Controllers/IndexController.php
-   public function index()
+   public function __invoke()
    {
        $now = Carbon::now();

        return view('index')
            ->with('now', $now);
    }
routes/web.php
- Route::get('/', 'IndexController@index')->name('index')
+ Route::get('/', IndexController::class)->name('index');

シングルアクションコントローラーにするには、アクションメソッド名を__invokeにして、ルーティングでの指定をクラス指定にするだけです。

余談ですが、普段書くときクラス名はシングルアクションコントローラーだと単数形そうでない場合は複数形にするようにしています。

お問い合わせフォームを作る

特殊な例のみ紹介してもわかりづらいので、複数のアクションメソッドを持つコントローラーの例としてお問い合わせフォームを作ってみましょう。

前準備

Formを実装するにあたって、Formファザードが便利なので、追加します。

PowerShell
PS > composer require laravelcollective/html5.5.*

config/app.phpaliasesに以下を追加します

config/app.php
    '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
resources/views/contact/index.blade.php
@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::textForm::emailなどで、input要素を作成できます
    • 第一引数にname、第二引数にvalueを指定
  • Form::submitで送信ボタンを作成できます

Formファザードの詳しい使い方は以下を参照してください。
Laravel Collective

resources/views/contact/complate.blade.php
@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
app/Http/Controllers/ContactsController.php
<?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
routes/web.php
  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になります
    • asはルートの名前を指定します
      • nameに前方結合され、contact.indexcontact.sendというルート名になります
  • URIの先頭の/は付けなくても良いです(個人的に気に入って付けているだけなので)

つまり、以下のように書いても同様の結果が得られます。

routes/web.php
- 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に入力した情報が出力されていれば完了です。

storage/logs/laravel.log
[2018-07-14 02:03:53] local.NOTICE: array (
  '名前' => 'とろゝ',
  'メールアドレス' => 'toro_ponz@hoge.jp',
  '問い合わせ内容' => 'お問い合わせ内容です。お問い合わせ内容です。お問い合わせ内容です。お問い合わせ内容です。',
)

実際にはここでメールを送信する処理を入れたり、Slackに通知したり、DBに保存したりしますが、それはまた別の機会で。

次回

次回はModelについて解説します。

参考サイト

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2