Laravel+PostgreSQL+Vue.jsでSPA開発【チュートリアル】

はじめに

皆様こんにちは。OPTiM新卒1年目エンジニアの青木です。 前回は早押しボタンなんかを作っていました。

tech-blog.optim.co.jp

今回は、PHP フレームワークの Laravel を、PostgreSQL と Vue.js と組み合わせて作成する TODO アプリを通して紹介します。

このフレームワークらはこちらの記事でも密かに利用しています。

tech-blog.optim.co.jp

OPTiMではあまり利用されていませんが、一部のアプリケーションで実利用されている箇所もございます。

PHPは昔のイメージからかなり避けられていていますが...今のPHPとそのフレームワークはすごく発展していてとても使いやすいので是非使っていただきたい!という気持ちがあります。 ですが、現状はあまり利用していただけなくて個人的には悲しい気持ちでいっぱいです。

そんなPHPですが、フレームワーク(LaravelやFuelPHPなど)を利用することにより未利用時よりも高速かつ安全に開発することができます。 そんなフレームワークの紹介を「TODOリストアプリケーション」を作りながら見ていきましょう。

PHPフレームワーク「Laravel」

今回ご紹介するのはPHPフレームワークの「Laravel」です。

Laravel - The PHP Framework For Web Artisans

laravel.jp

フレームワークの仕様に乗っかって開発することによりかなり安全で可用性のあるアプリケーションの開発が容易になります。

個人的にLaravelのいいところはズバリ

爆速安全開発

です。

本当に手早くWebアプリケーションが作れます。 今まで数人で作業していたものをたった一人で1週間もあればかなり強い物が作れます!

2019年8月5日現在のLaravel最新バージョンは「5.8」で「5.7」のマイナーアップデートとなっています。 Laravelは奇数バージョンがメインとなっており、中でもLTSとなっているのは「5.1」「5.5」となっています。セキュリティフィックス期限はLTSで3年間、一般的なリリースについては1年間のサポートとメンテナンスがされています。

このように、言語仕様だけではカバーできないセキュリティの担保もフレームワークが担ってくれることにより、PHP5以前の時代よりも遥かに強くなっています。

ここで、Googleトレンドより注目度を比較してみましょう。

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20190809/20190809105020.png

実は代表的なWebフレームワークの「Ruby on Rails」や「Django」などよりもLaravelの注目度が上がってきています。

日本ではダントツで「Ruby on Rails」が多く見られます。 海外での「Laravel」の注目度が上がってきているようですね。

Laravelは機能がとても豊富です。ですからファイルサイズ的にも大きくなりますし、環境にも負担がかかってしまいます。 そんな時は「Lumen」を利用しましょう。ここではあまり解説しませんが、「Lumen」は「Laravel」の軽量版として開発されています。

とっても雑に説明するとNode.jsのExpressのような感じですね。

さて、前置きが長くなりましたが、本題に移りたいと思います。

今回の目標

PHPフレームワークLaravelで、DBを使用したSPA(Single Page Application)のTODOアプリを作ります。 またログイン認証によるユーザ管理も同時に行っていきます。

完成形

この記事の最後にはこのようなアプリケーションが出来上がります。

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20190813/20190813102855.gif

  • ユーザの新規登録・ログイン・ログアウト
  • ログインユーザごとのTODOの表示・追加・更新・削除
  • 最低限のView

使用する技術

  • PHP
  • Laravel
  • Vue.js
  • PostgreSQL
  • Docker

導入・環境構築

PHPの導入

LaravelでバージョンごとにPHPに対する要件が決められています。

  • 現時点でのPHPの最新バージョンは「7.3.8」
  • 現時点でのLaravelの最新バージョンは「5.8.17」

Laravel「5.8.*」で求められるPHPの要件は以下のとおりです。

  • PHP >= 7.1.3
  • BCMath PHP拡張
  • Ctype PHP拡張
  • JSON PHP拡張
  • Mbstring PHP拡張
  • OpenSSL PHP拡張
  • PDO PHP拡張
  • Tokenizer PHP拡張
  • XML PHP拡張

これに応じてPHPの導入を行いましょう。

ちなみにHomebrewでインストールした場合は「7.3.7」がインストールされました。こちらのバージョンでも要件を満たしておりますので大きな問題はありません。
※ただしセキュリティ等の関係上最新バージョンを使うに越したことはありません。下調べは慎重にかつ念入りに行いましょう。

Composerの導入

Laravelを構築するためのパッケージマネージャが必要となります。 PHPの代表的なパッケージマネージャである「Composer」を導入しましょう。

curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
composer config -g repositories.packagist composer https://packagist.jp

HelloWorld(プロジェクトの作成)

さて、いよいよプロジェクトを作成していきます。 「Laravel」では以下の1コマンドのみでプロジェクトの新規作成が行えます。

$ composer create-project laravel/laravel Techblog_sample

ビルトインサーバの起動

Laravelに標準搭載されているartisanコマンドでビルトインサーバを起動してみましょう。

$ cd Techblog_sample
$ php artisan serve
Laravel development server started: <http://127.0.0.1:8000>

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20190809/20190809105027.png

簡単ですね。環境構築後からはたった3コマンドしか叩いていませんが、HelloWorld同等の画面まで出せました。

データベースの導入とAuth実装

データベースの導入を行います。 今回はPostgreSQLを利用していますが、MySQLやSQLiteなども対応しています。

  • PostgreSQL導入 (Docker使用)
  • LaravelとDBの接続
  • Auth(認証)導入

PostgreSQL導入 (Docker使用)

PostgreSQLを構築します。

こちらは皆様のお好きな方法で構築していただいて構いません。 あくまで一例として記載いたします。

データベースを開発機にそのまま構築しても良いですが、比較的構築の簡単なdocker-composeを利用して構築をしたいと思います。

docker-compose.yml

version: '2'
services:
    db:
        image: postgres:11-alpine
        ports:
        - "15432:5432"
        environment:
            POSTGRES_USER: "postgres"
            POSTGRES_PASSWORD: "password"
            POSTGRES_DB: "techblog"
$ docker-compose up -d
Creating network "techblog_sample_default" with the default driver
Creating techblog_sample_db_1 ... done

$ docker-compose ps 
        Name                      Command              State            Ports
--------------------------------------------------------------------------------------
techblog_sample_db_1   docker-entrypoint.sh postgres   Up      0.0.0.0:15432->5432/tcp

データベースの構築が終わりました。

LaravelとDBの接続

LaravelとDBサーバを接続します。 こちらはデフォルトで環境変数から接続情報を取得するようになっており、環境変数を変更することにより接続情報を更新できます。

また、環境変数に存在しなかった場合はディレクトリに存在する.envファイルを参照するようになっています。

.envファイルを以下のように変更してください。各自で構築された場合は構築した際の情報に都度変更してください。

.env

...
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=15432
DB_DATABASE=techblog
DB_USERNAME=postgres
DB_PASSWORD=password
...

接続確認は次のAuth作成時に確認します。

Auth(認証)導入

LaravelにAuth(認証)を追加します。

至って簡単です。

$ php artisan make:auth
Authentication scaffolding generated successfully.

以上です。

試しにビルトインサーバを立ち上げ、確認をしてみましょう。

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20190809/20190809105024.png

お気づきの方が既にいらっしゃるかもしれませんが、右上にログインボタンなどが現れています。

試しにログインボタンを押してみましょう。

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20190809/20190809105029.png

ログイン画面が構築されています。 たった1コマンドで

  • ログイン画面
    • ログイン処理
  • 新規登録画面
    • 新規登録処置
  • パスワードリセット画面
    • パスワードリセット処理

が実装されます。 素晴らしいですね。

ただ、いまのままですとDBにアクセス出来ていません。 以下のコマンドを入力してマイグレーションを行いましょう。

$  php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table

これで新規登録などが出来るようになりました。 デフォルトではセッションでログイン情報を管理します。

コマンドライン(Tinker)でDBデータ確認

artisanコマンドによるデータ確認を行ってみたいと思います。 Laravelにはデフォルトでtinkerと呼ばれる対話型ツールが存在します。 こちらを利用することにより、ソースコードにする前に動作確認が出来ます。

また、Laravelに読み込まれるモジュール等が手動で読み込める状態になっているので、Laravelで開発する際はおすすめの動作確認方法です。

LaravelからDBにアクセスするために、クエリビルダを利用します。

作業をするそのまえに
データベースにデータを入れるため、一旦画面上から新規登録を行ってみましょう。

$ php artisan tinker
>>> DB::table('users')->get();
=> Illuminate\Support\Collection {#2977
     all: [
       {#2975
         +"id": 1,
         +"name": "TestUser",
         +"email": "test@test.test",
         +"email_verified_at": null,
         +"password": "$2y$10$b6zQjxNcM0U0ka5CPpIqPeobsO3Sxqa7icHD.uMBv5peUC35Bs972",
         +"remember_token": null,
         +"created_at": "2019-08-05 03:23:52",
         +"updated_at": "2019-08-05 03:23:52",
       },
     ],
   }
>>>

しっかりと新規登録されていますね。 また、こういった事もデバッグとして利用できます。

# ユーザID(1)のユーザの名前を表示
>>> DB::table('users')->find(1)->name;
=> "TestUser"

# 現在ログインしているかどうかのチェック
>>> Auth::check()
=> false

# ユーザID(1)のユーザでログイン処理
>>> Auth::loginUsingId(1)
=> App\User {#2991
     id: 1,
     name: "TestUser",
     email: "test@test.test",
     email_verified_at: null,
     created_at: "2019-08-05 03:23:52",
     updated_at: "2019-08-05 03:23:52",
   }

# 現在ログインしているかどうかのチェック
>>> Auth::check()
=> true

# 現在ログインしているユーザ情報を取得
>>> Auth::user()
=> App\User {#2991
     id: 1,
     name: "TestUser",
     email: "test@test.test",
     email_verified_at: null,
     created_at: "2019-08-05 03:23:52",
     updated_at: "2019-08-05 03:23:52",
   }

# ログアウト処理
>>> Auth::logout()
=> null

# 現在ログインしているかどうかのチェック
>>> Auth::check()
=> false

コマンドライン上でセッションも利用できるということですね。 独自で作成したサービスモジュールなんかも利用出来るので、是非デバッグなどにご利用ください。

テーブル設計

TODOアプリケーションを制作するに当たってテーブルを構築しなければいけません。 今回は1ユーザが複数のTODOを持てるように設計します。

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20190809/20190809105120.png

テーブル設計は前項でも利用した Migration を利用します。

$ php artisan make:migration create_todos_table
Created Migration: 2019_08_05_065824_create_todos_table

database/migrations/2019_08_05_065824_create_todos_table.php

<?php
    public function up()
    {
        Schema::create('todos', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('todo');     // この行を追加
            $table->integer('user_id');
            $table->timestamps();
        });
    }

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

作成できたら、マイグレーションを実行してデータベースに反映させましょう。

$ php artisan migrate
Migrating: 2019_08_05_065824_create_todos_table
Migrated:  2019_08_05_065824_create_todos_table

これで新たにテーブルが作成されました。

ここまで

ここまではLaravelの導入、環境構築、Authの導入を行いました。 PHPでのコーディングをほぼ無しとしてここまで作れました。

次は、実際にルーティングやMVCなど、Laravelのメイン機能についてご紹介していきます


LaravelのMVCとその他の機能

  • ルーティング
  • コントローラ(C)
  • ビュー(V)
  • モデル(M)
  • ミドルウェア
  • ログイン後のリダイレクト先

実装するページ

Authは自動で実装されるので割愛しています。 TODOアプリケーションはSPAで作成し、APIを叩いて操作を行います。

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20190809/20190809105059.png

ルーティング

いよいよ基盤が完成したので、ルーティングの説明に入ります。

Laravelのルーティングは routes ディレクトリ内に存在し、それぞれのファイルは用途によってプレフィックスが初期でつけられていたりします。

今回は web.php を編集していきましょう。

routes/web.php

<?php
Route::get('/todo', function () {
    return "このページはアプリケーションのページです";
});

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20190809/20190809105105.png

このルーティングを追加すると、画像のように表示されます。

今回はSPAなのであまり使いませんが、SPAではないアプリケーションを作る場合は基本的にここに記述していきます。

また、 api.php に作成することで自動的に/api/のプレフィックスが付きます。任意のプレフィックスに変更したい場合は app/Provider/RouteServiceProvider.php を変更しましょう。

さて、このままではクロージャがどんどん膨れ上がって行ってしまいますので、次の工程でしっかりと分業してあげます。

コントローラ

web.php がロジックでどんどん汚染されていくので、コントローラと呼ばれるもので分業していきましょう。 下記のコマンドでコントローラが自動生成されます。

$ php artisan make:controller TodoController
Controller created successfully.

TodoController.phpの中にメソッドを生やし、web.phpから読み込んでみましょう。

app/Http/Controllers/TodoController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class TodoController extends Controller
{
    public function index() {
        return "コントローラからこんにちは";
    }
}

routes/web.php

<?php
Route::get('/todo',TodoController::class . "@index");

実装が出来たら、php artisan serveで見てみましょう。

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20190809/20190809105102.png

いかがでしょうか。 コントローラに分割することによってルーティングによる画面や機能別にロジックを記述することが可能になりました。

ビュー

では、ビューを表示してみましょう。

Laravelのビューは強力なbladeテンプレートエンジンを含んでいます。 コントローラから変数を渡し、bladeテンプレートエンジンにより表示してみます。

$ touch resources/views/todo.blade.php

resources/views/todo.blade.php

<?php
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <h2>TODO アプリケーション</h2>
    </div>
</div>
@endsection

@から始まるものはbladeテンプレートエンジン特有のメソッドです。 今回は既に用意されているレイアウトデザインを利用します。

上記のファイル作成が終われば、TodoController.phpを変更していきます。

view()メソッドにbladeテンプレートのファイル名を記入すればviewが帰ります。

app/Http/Controllers/TodoController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class TodoController extends Controller
{
    public function index() {
        return view('todo');
    }
}

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20190813/20190813110726.png

モデル

Eloquent ORM と呼ばれる非常にシンプルかつ高機能なORMがLaravelでは提供されています。

ここでいうモデルとはEloquentモデルのことであり、Eloquent ORMの利点を最大限生かされたものです。

まずはじめに、モデルを作成しましょう。

$ php artisan make:model Todo

基本的にはこれで終わりです。

  • テーブル名が todos であれば、モデル名は Todo
  • モデル名が Sample であれば、テーブル名は samples

この方式で決定しています。 仮にテーブル名とモデル名を変更したい場合はモデルファイル(app/モデル名.php)のクラス変数に

<?php
protected $table = 'テーブル名';

と指定することが出来ます。

では、確認してみましょう。

$ php artisan tinker
Psy Shell v0.9.9 (PHP 7.3.7 — cli) by Justin Hileman
>>> use App\Todo;
>>> Todo::all();
=> Illuminate\Database\Eloquent\Collection {#2975
     all: [],
   }
>>> Todo::insert(["todo"=>"TestTest","user_id"=>1]);
=> true
>>> Todo::all();
=> Illuminate\Database\Eloquent\Collection {#2981
     all: [
       App\Todo {#2980
         id: 2,
         todo: "TestTest",
         user_id: 1,
         created_at: null,
         updated_at: null,
       },
     ],
   }

insert文で追加してみました。 しっかりと反映されていますね。

モデル(リレーションの定義)

モデルにリレーションを定義することが出来ます。 かなり便利なので是非使っていきたいところです。

今回のリレーション内容を再確認してみましょう。

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20190809/20190809105120.png

users.idtodos.user_id が紐付いていますね。

これをユーザ側のモデルからtodosを参照出来るようにモデルに定義していきます。

app/User.php

<?php
class User extends Authenticatable
{
    ... 省略
    public function todos(){
        return $this->hasMany('App\Todo');
    }
}

これで実装が終わりです。 Tinkerで確認してみましょう。

php artisan tinker
Psy Shell v0.9.9 (PHP 7.3.7 — cli) by Justin Hileman
>>> 
>>> App\User::find(1);
=> App\User {#2979
     id: 1,
     name: "TestUser",
     email: "test@test.test",
     email_verified_at: null,
     created_at: "2019-08-08 10:10:02",
     updated_at: "2019-08-08 10:10:02",
   }
>>> App\User::find(1)->with('todos')->get();
=> Illuminate\Database\Eloquent\Collection {#2974
     all: [
       App\User {#2968
         id: 1,
         name: "TestUser",
         email: "test@test.test",
         email_verified_at: null,
         created_at: "2019-08-08 10:10:02",
         updated_at: "2019-08-08 10:10:02",
         todos: Illuminate\Database\Eloquent\Collection {#2991
           all: [
             App\Todo {#2988
               id: 2,
               todo: "TestTest",
               user_id: 1,
               created_at: null,
               updated_at: null,
             },
           ],
         },
       },
     ],
   }
>>> 

->with('todos')->を利用することでユーザに紐付いたTodosテーブルのレコードを受け取ることが出来ます。

ミドルウェア

ルーティングする際に、特定のページらに一定の操作を与えたい。 などのニーズに応えるのがミドルウェアです。

現状問題となっているのは、ログインしていなくても/todoにアクセス出来てしまうということです。

ログインしていないのにTodo画面へ遷移されてはとても困ります。ユーザ毎に情報を表示するのにそのユーザが誰かわからないのでエラーが出てしまいます。

かといって、毎ページ同じ動作を記述するのはとてもスマートとは言えません。 そこで、ミドルウェアの登場です。

説明よりやってみたほうが早いので、コードを記述します。

routes/web.php

<?php
Route::group(["middleware"=>"auth"],function(){
    Route::get('/todo',TodoController::class . "@index");
});

Route::groupの第2引数のクロージャに先程の /todo を入れてみましょう。

すると、/todoにアクセスしようとするとログイン画面へリダイレクトします。これでログイン判定が出来るのでいちいちコントローラ内部にロジックを書くことはありません。

また、ミドルウェアは自作することが出来ます。後ほどAPI作成の際にひつようになりますのでそちらで詳しく解説いたします。

ログイン後のリダイレクト先

現状、新規登録・ログイン後に /home へリダイレクトしていると思います。

これはLoginController.php などが自動的にリダイレクト処理を行っているからです。

今回は/homeを利用しませんので、削除する方法を記載します。

app/Http/Controllers/Auth/LoginController.php
app/Http/Controllers/Auth/RegisterController.php
app/Http/Controllers/Auth/ResetPasswordController.php
app/Http/Controllers/Auth/VerificationController.php

- protected $redirectTo = '/home';
+ protected $redirectTo = '/todo';

resources/views/home.blade.php

削除

routes/web.php

- Route::get('/home', 'HomeController@index')->name('home');

これでログイン後は /todo へリダイレクトします。

TODOアプリケーションのメイン機能実装(サーバーサイド実装)

APIの実装

まずはじめにAPIの実装を行います。 今回のAPIでの認証は、セッションで行いたいと思います。

機能一覧

TODOアプリケーションを作成するに当たって、APIの機能を洗い出します。

  • 全件表示
  • 1件追加
  • 1件削除
  • 1件更新

こちらの機能を作成していこうと思います。

機能実装

まず、コントローラに定義します。

app/Http/Controllers/TodoController.php

<?php
use App\Todo;
use Illuminate\Support\Facades\Auth;

class TodoController extends Controller
{
    ... 省略
    public function get(){
        // 全件表示
    }

    public function post(){
        // 1件追加
    }

    public function delete(){
        // 1件削除
    }

    public function update(){
        // 1件更新
    }
}

次に、ルーターに追加していきます。api.phpになっているので注意してください。

route/api.php

<?php
Route::get('/todo','TodoController@get');
Route::post('/todo','TodoController@post');
Route::delete('/todo/{id}','TodoController@delete');
Route::put('/todo/{id}','TodoController@update');

次は確認をはさみつつ、それぞれの機能を実装していきます。

全件表示

<?php
public function get(){
    return response()->json(Auth::user()->todos()->get());
}

モデルの説明の時に行ったものと同じです。

1件追加

<?php
public function post(Request $request){
    $todo = new Todo();
    $todo->todo = $request->todo;
    $todo->user_id = Auth::id();
    $todo->save();
    return response("OK", 200);
}

モデルでインスタンスを生成し、それぞれに値を当てはめてから最後にセーブをすればSQLを発行して追加処理を行ってくれます。 Todo::insert()でも同じことが行なえます。

1件削除

<?php
public function delete($id){
    Todo::find($id)->delete();
    return response("OK", 200);;
}

Todoのインスタンスにdeleteメソッドがありますので、それを実行すれば削除できます。

1件更新

<?php
public function update(Request $request,$id){
    $todo = Todo::find($id);
    $todo->todo = $request->todo;
    $todo->save();
    return response("OK", 200);
}

追加の時とほぼ同じで、findでtodoのインスタンスを持ってくるだけで出来上がります。

APIのAuth実装

このままでは誰でもAPIにアクセスできてしまいます。

しかもコード内部で Auth::を利用しているので、ログインしていないユーザがアクセスするとエラーが発生します。

ここではそのAuth(認証)をAPIからでも行えるように変更、追記していきます。

  • セッションを有効化
  • ミドルウェアの作成
  • 実装

セッションを有効化

APIではデフォルトでAuthorizedTokenを利用するような設計になっています。 今回はセッションを利用するので以下のように変更します。

app/Http/Kernel.php

<?php
// 省略
 protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            // ここを追加
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            // ここまで追加
            'throttle:60,1',
            'bindings',
        ],
    ];
// 省略

これでセッションが有効になります。

ミドルウェアの作成

デフォルトで備わっているAuthのミドルウェアはビューに最適化されており、ログインせずにミドルウェアに到達すると /login にリダイレクトされてしまいます。

APIではリダイレクトではなく エラーメッセージ が帰ってくることを期待します。

ですのでミドルウェアを自作します。

$ php artisan make:middleware AuthApi

app/Http/Middleware/AuthApi.php

<?php
use Illuminate\Support\Facades\Auth;
// 省略
public function handle($request, Closure $next)
{
    if (!Auth::check()) return response("",401,["Content-type: application/json"]);
    return $next($request);
}

use ...Auth の行が必要ですので注意してください。

app/Http/Kernel.php

<?php
// 省略
protected $routeMiddleware = [
    // 省略
    'auth.api' => \App\Http\Middleware\AuthApi::class,
];

これでミドルウェアの実装完了です。

実装

先程作成したtodoのルーターに変更を加えていきます。

route/api.php

<?php
Route::group(["middleware" => "auth.api"],function(){
    Route::get('/todo','TodoController@get');
    Route::post('/todo','TodoController@post');
    Route::delete('/todo/{id}','TodoController@delete');
    Route::put('/todo/{id}','TodoController@update');
});

これでログインしていないと401エラーを返すことになります。

フロントの実装

いよいよフロントの実装に入ります。

ここではVue.jsを利用したSPAで制作していきたいと思います。

VueJSの構築

LaravelはデフォルトでVueJSが導入されていて、 既にExampleファイルも構築されています。 Webpackも何も書かなくても初期導入されています。

したがって、npmインストールを行い、Viewを少し変更するだけでVueJSを導入できます。

$ npm i
$ npm run dev

$ npm run watch # ファイルの変更時、自動的にコンパイルが走る

resources/views/todo.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <h2>TODO アプリケーション</h2>
        <example-component></example-component>
    </div>
</div>
@endsection

<example-component>を追加することでVue.jsの導入は完了です。

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20190813/20190813111728.png

APIを叩くリクエストモジュールを作成

APIを叩く際、数が多くなってくるとごちゃごちゃになってくるので、APIを叩く機能は別に作成します。

resources/js/api.js

"use strict"

const send = (method,uri,data={}) => {
    const url = 'http://127.0.0.1:8000' + uri
    return new Promise((resolve)=>{
        var xhr = new XMLHttpRequest();
        xhr.open(method, url);
        xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
        xhr.onload = () => {
            try{
                const res_json = JSON.parse(xhr.responseText)
                resolve(res_json)
            }catch (e) {
                resolve(xhr.responseText)
            }
        }
        xhr.onerror = () => {
            console.log(xhr.status);
            console.log("error!");
        };
        xhr.send(data);
    })
}

const api = {
    getTodoList(){
        return send("GET","/api/todo");
    },
    postTodo(todo){
        return send("POST","/api/todo",todo);
    },
    updateTodo(id,todo){
        return send("PUT","/api/todo/" + id,todo);
    },
    deleteTodo(id,data){
        return send("DELETE","/api/todo/" + id,data);
    }
}

export default api

APIを叩くためのメソッド send を用意して、下段にAPIをリストのように記述することで新たにAPIが増えても追加しやすいようにします。

コンポーネントの実装

では、駆け足でコンポーネントを作成していきましょう。

今回はLaravelの紹介なのでフロント実装の部分は大幅にカットします。

以下のソースコードをまるごとコピーすれば動作すると思います。

resources/js/components/ExampleComponent.vue

<template>
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="form-group">
                        <input type="text" class="form-control" id="inputtodo" v-model="todo_form">
                        <button type="button" class="btn btn-primary" @click="addTodo">Add</button>
                    </div>
                </div>
                <div class="card" v-for="todo in todos">
                    <div class="card-header">
                        <button type="button" class="btn btn-danger" @click="deleteTodo(todo.id)">Delete</button>
                        <button type="button" class="btn btn-info" @click="updateTodo(todo.id)">Update</button>
                        <input type="text" class="form-control" id="todo" v-model="todo.todo">
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    import api from "../api.js"
    export default {
        data(){
            return {
                active_todo: null,
                todo_form:"",
                todos:[]
            }
        },
        methods:{
            addTodo(){
                let data = {todo:this.todo_form}
                data._token = document.getElementsByName('csrf-token')[0].content;
                api.postTodo(JSON.stringify(data)).then(()=>{
                    this.getTodoList()
                })
            },
            deleteTodo(id){
                let data = {}
                data._token = document.getElementsByName('csrf-token')[0].content;
                api.deleteTodo(id,JSON.stringify(data)).then(()=>{
                    this.getTodoList()
                })
            },
            updateTodo(id){
                let data = {todo:this.todos.filter((v)=>{return v.id === id})[0].todo}
                data._token = document.getElementsByName('csrf-token')[0].content;
                api.updateTodo(id,JSON.stringify(data)).then(()=>{
                    this.getTodoList()
                })
            },
            getTodoList(){
                api.getTodoList().then((result)=>{
                    this.todos = result
                })
            }
        },
        mounted() {
            this.getTodoList()
            console.log('Component mounted.')
        }
    }
</script>

それぞれの機能ごとに method を用意して、datav-modelなどを駆使してAPIを叩きます。 APIを叩いた後はすかさずthis.getTodoListを行っていますが、これは変更を適応するためです。

完成

TODOリストが完成しました。

https://cdn-ak.f.st-hatena.com/images/fotolife/o/optim-tech/20190813/20190813102855.gif

最後に

いかがでしたでしょうか。

記事が長くなってしまいましたが、実際に記述したソースコードはほんの一部分だけです。 こちらにアプリケーションのソースコードを載せております。

GitHub - optim-corp/techblog-laravel-todo-sample

Auth(認証)やAPIの作成、SPAの実装までこの量で出来てしまいます。

この記事の通りに実装を行えば上記のTodoアプリケーションが出来上がりますが、バリデーションチェック等の エラーハンドリングを全く行っておりません(特にAPI部分) ので、ご注意ください。

最後になりましたが、「Laravelを使って爆速Webアプリケーション開発を行っていこう!」という言葉を残して締めくくりたいと思います。

ありがとうございました。 


OPTiMでは様々な分野で活躍できるエンジニアを募集しております。 今回のようなWebアプリケーションも然り、機械学習やクラウド系インフラ周りまであらゆる分野で活躍出来る場があります!

興味がある方は是非弊社採用ページにてご覧ください!

www.optim.co.jp

また、夏季インターンシップも募集しておりますので、合わせてご覧ください。

www.optim.co.jp