2015.06.04

Laravel 5.0を使ってみた


はじめに

こんにちは。次世代システム研究室のT.Tです。
PHPフレームワークのLaravel 5.0がリリースされてからしばらく経ちますが、Laravel 5.0を使って
FuelPHPのコントローラとモデルの拡張で導入したPHP標準フレームワークのコントローラの一部の機能を実現してみました。
その環境の構築からコントローラが動作するところまでをご紹介したいと思います。

環境構築

Laravelの環境としてPHP 5.4以上といくつかの拡張モジュールや、その他Webサーバー等も必要になりますが、既にFuelPHP用に構築してあるVagrantの実行環境があるため、そのnginxに仮想ホストを追加する形で環境を構築しました。
追加で必要となるパッケージはComposerとLaravel 5.0で、nginxの仮想ホストの設定を追加する必要があります。
(環境が何もない場合はLaravel Homesteadを使うのが良さそうです。手動で一通り環境を用意する場合はCentOS 6.5 + nginx 1.6.1 + Laravel 4.2のインストールが参考になります。)

Composerのインストール

LaravelをComposerでインストールするため、Composerをインストールして直接実行できるようにします。
$ curl -sS https://getcomposer.org/installer | php
$ sudo mv composer.phar /usr/local/bin/composer

Laravelのインストール

ComposerでLaravelの実行環境をインストールします。
$ composer global require "laravel/installer=~1.1"

Laravelのプロジェクトの生成

LaravelのプロジェクトをCGIを実行可能なディレクトリに生成します。
ここではsampleという名前のプロジェクトを生成しています。
$ cd /var/www
$ ~/.composer/vendor/bin/laravel new sample

パーミッションの設定

nginxの実行ユーザーに書き込み権限を付与します。
$ cd sample
$ chown -R nginx sample/storage
$ chown -R nginx vendor
$ chown -R nginx database

nginxの仮想ホストの設定

以下のディレクティブを追加した設定ファイルをnginxのconf.d下に配置します。
location / {
    try_files $uri $uri/ /index.php?$query_string;
}

/etc/nginx/conf.d/sample.conf
server {
    listen 81;
    server_name  localhost;

    # ドキュメントルートの設定
    root /var/www/sample/public;
    index index.html index.htm index.php;

    # アクセスログの設定
    access_log /var/log/nginx/sample_access.log;
    # エラーログの設定
    error_log /var/log/nginx/sample_error.log;

    location / {
        try_files $uri $uri/ /index.php?q=$uri&$args;
    }

    location ~ \.php$ {
        try_files                $uri = 404;
        include                  /etc/nginx/fastcgi_params;
        fastcgi_pass             127.0.0.1:9000;

        fastcgi_index            index.php;
        fastcgi_split_path_info  ^(.+\.php)(/.+)$;

        fastcgi_param            SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param            PATHINFO        $fastcgi_path_info;
        fastcgi_param            PATH_TRANSLATED $document_root$fastcgi_path_info;
    }

    error_page 404 /index.php;

    # その他の設定
    # ...
}

動作確認

nginxの81番ポート(http://192.168.33.10:81)にアクセスするとLaravelのタイトル画面が表示されます。

アプリケーション設定

DB設定とユーザーテーブル、セッションテーブルの作成、アプリケーション設定、セッション設定を追加します。

DB設定

config/database.phpにDBのホスト名とデータベース、ユーザー、パスワードを設定します(ここではDBにMySQLを利用しています)。
ローカルやステージング、プロダクション等の環境ごとに設定を変える場合は環境ごとに定義しますが、ここではデフォルト値で設定します。
読み込み用と書き込み用に設定を分けていますが、同じデータベースを使うように設定しています。
 'mysql' => [
    'driver'    => 'mysql',
    'read'      => [
        'host'      => env('DB_HOST', 'localhost'),
    ],
    'write'     => [
        'host'      => env('DB_HOST', 'localhost'),
    ],
    'database'  => env('DB_DATABASE', 'sample'),
    'username'  => env('DB_USERNAME', 'user'),
    'password'  => env('DB_PASSWORD', 'password'),
    'charset'   => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix'    => '',
    'strict'    => false,
],

ユーザーテーブル

デフォルトでmigrationファイルが存在しているので、それをそのまま利用してマイグレーションを実行します。
$ php artisan migrate

セッションテーブル

セッションをDBで管理するため、セッションテーブルを作成するmigrationファイルを生成して、マイグレーションを実行します。
$ php artisan session:table
$ php artisan migrate

アプリケーション設定

config/app.phpを編集します。
ここでは最低限必要だと思われる設定のみ変更しています。
'debug' => env('APP_DEBUG', true), // スタックトレースをアプリケーションに表示する
'timezone' => 'Asia/Tokyo', // タイムゾーンを東京に変更する
'locale' => 'ja', // ロケールを日本語に変更する

セッション設定

config/session.phpを編集します。
'driver' => env('SESSION_DRIVER', 'database'), // セッションをDBで管理する
'connection' => 'mysql', // config/database.phpのmysqlコネクションを使う

動作確認

ここまで設定すると、標準のログイン画面(http://192.168.33.10:81/home)からログインすることができます。
home
login

コントローラ

コントローラはApp\Http\Controllers\Controllerを継承した上で、以下の機能を実装してみました。
  • セッションチェック
  • DBのトランザクション管理
  • JSONをPOSTメソッドで受け取り、JSONをレスポンスする処理

セッションチェック

認証成功時にセッションにユーザーIDを書き込み、リクエスト時にユーザーIDが存在しているかどうかで内容の正当性を確認して、不正な場合はリクエストを受け付けないようにします。
ログイン時にセッションにユーザーIDをセットし、ログアウト時にセッション情報をフラッシュするためにイベントハンドラを登録します。
$ sudo php artisan handler:event AuthLoginEventHandler
$ sudo php artisan handler:event AuthLogoutEventHandler

app/Handlers/Events/AuthLoginEventHandler.php 
<?php namespace App\Handlers\Events;

use App\Events;

use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldBeQueued;

class AuthLoginEventHandler {

    public function __construct()
    {
    }

    public function handle($user)
    {
        // セッションにIDを追加
        \Session::put('user_id', $user->id);
    }
}

app/Handlers/Events/AuthLogoutEventHandler.php 
<?php namespace App\Handlers\Events;

use App\Events;

use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldBeQueued;

class AuthLogoutEventHandler {

    public function __construct()
    {
    }

    public function handle($user)
    {
        // セッション情報を削除
        \Session::flush();
    }
}

app/Providers/EventServiceProvider.php 
<?php namespace App\Providers;

use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider {

    protected $listen = [
        'auth.login' => [
            'App\Handlers\Events\AuthLoginEventHandler',
        ],
        'auth.logout' => [
            'App\Handlers\Events\AuthLogoutEventHandler',
        ],
    ];

    public function boot(DispatcherContract $events)
    {
        parent::boot($events);
    }
}

コントローラ側でセッションにユーザーID情報がなければリクエストを受け付けないようにします。
<?php namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;

class ApiController extends Controller {
    public function __construct()
    {
        $this->beforeFilter(function()
        {
            if (\Session::get('user_id') === null) {
                return \Response::json(array(), 403);
            }
        });
    }
}

app/Http/routes.php
<?php

Route::get('/', 'WelcomeController@index');

Route::get('home', 'HomeController@index');

Route::controllers([
    'auth' => 'Auth\AuthController',
    'password' => 'Auth\PasswordController',
    'api' => 'Api\ApiController',
]);

DBのトランザクション管理

APIのベースとなるコントローラでトランザクションを一元的に管理する仕組みを提供して、各コントローラではトランザクションを意識しないようにしています。
<?php namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;

class ApiController extends Controller {
    public function postApi()
    {
        try {
            \DB::beginTransaction();

            // 処理実行

            \DB::commit();
        } catch (\Exception $e) {
            \DB::rollback();
        }
    }
}
JSONをPOSTメソッドで受け取り、JSONをレスポンスする処理

postプリフィックスを付けたメソッドを定義すればPOSTメソッドのリクエストだけを受けることができるので、コントローラではそのようにメソッド名を付けています。

さらにJSONをパースしてJSONを返す処理を以下のように付け加えています。
<?php namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;

class ApiController extends Controller {
    public function postApi()
    {
        $json = \Input::all();
        $id = $json['id'];

        // 処理実行
       
        $status = 200;
        $data = array('user_id' => 1);

        return \Response::json($data, $status);
    }
}

最後に

Laravelは今回初めて動かしてみましたが、ざっと見て使った感じでもAOPをサポートしていたり、5.0から導入されているLaravel SocialiteやFlysystem Integration等のソーシャル連携やクラウドサービス連携の機能が充実してきているあたりが今後も期待が持てるところです。
FuelPHPとの比較で言うとLaravelもFuelPHPと同じくらい簡単に記述ができ、機能やドキュメントはより充実していて良い感じです。

いくつか良いと思った点を挙げると、
  • グローバルなイベントリスナーが使える
  • 標準のルーティングやコントローラの前後処理が豊富でビジネスロジックを分離しやすい
  • Homestead等の環境サポートがある
気になった点としては、リリースサイクルが割と早くバージョン間の互換性を損なうレベルの変更とかも入っているように見えたので、導入後のバージョンアップや旧バージョンの手法に陥らないように気を付ける必要がありそうなところです。

あまりLaravelは扱えてはいないですが、新しくPHPフレームワークを導入する場合はLaravelも有力な候補になりそうです。

次世代システム研究室では、アプリケーション開発や設計を行うアーキテクトを募集しています。アプリケーション開発者の方、次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ 募集職種一覧 からご応募をお願いします。

皆さんのご応募をお待ちしています。