RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

Laravel の session(セッション) についてまとめてみた。

f:id:tech-rakus:20210407152443p:plain

はじめに

こんにちは、開発エンジニアのシカタです。
今回は、開発を担当するプロダクトでアーキテクチャを刷新することになり、Laravelの検討したときのお話です。
アーキテクチャの刷新だけにたくさんの課題がありましたが、その中でも今回はセッションのことについて書きます。
Laravelをやってみようとか、既存プロダクトにLaravelを載せてみたいなーと思ってる人の心に刺されば幸いです。

背景

既存の運用中プロダクトの一部サブシステムを、新アーキテクチャで構築し直すプロジェクトがスタートしました。 一気にリリースすると影響が大きすぎるので、一部の機能から徐々にリリースしていく方針です。
現在ノンフレームワークのため、せっかく新しくするならフレームワークを採用したい…
ということで、Laravelを検討してみることになりました。
既存プロダクトのノンフレームワークな世界と、Laravelで新しく作る世界を共存させることを検討する必要があり、フレームワークの機能を活用することを目指していますが、たくさんの課題が出てきました。

  • アーキテクチャどうしよう…
  • 既存プロダクトとLaravelでどうやってセッション共有しよう…
  • 認証周りとかどうするのが良いんだろう…etc

今回は課題の中からセッションについて取り上げます。

Laravelのセッション管理について

まず、Laravelの基本的なセッション管理についてです。

セッションドライバについて

Laravelでは、file、cookie, DBなどでセッション情報を管理することができます。
config/session.phpにセッションに関する設定は記載されています。

config/session.php
return [

    /*
    |--------------------------------------------------------------------------
    | Default Session Driver
    |--------------------------------------------------------------------------
    |
    | This option controls the default session "driver" that will be used on
    | requests. By default, we will use the lightweight native driver but
    | you may specify any of the other wonderful drivers provided here.
    |
    | Supported: "file", "cookie", "database", "apc",
    |            "memcached", "redis", "dynamodb", "array"
    |
    */

    'driver' => env('SESSION_DRIVER', 'file'),
    
    :
    :
    
]

セッションの操作

Laravelでセッションの操作を行うには、RequestインスタンスsessionヘルパSessionファサードを使う方法があります。
基本操作に大きな違いはありません。

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Session;

class HogeFunction extends Controller
{
    /**
     * @param  Request  $request
     * @return Response
     */
    public function hogee(Request $request)
    {
        /* セッションから1つデータを取得する */
        // Requestインスタンス
        $value = $request->session()->get('key');
        // Sessionヘルパ
        $value = session('key');
        // Sessionファサード
        $value = Session::get('key'); 
        
        /* セッションの値をすべて取得する */
        // Requestインスタンス
        $values = $request->session()->all();
        // Sessionヘルパ
        $values = session()->all();
        // Sessionファサード
        $values = Session::all(); 
        
        /* セッションに値を保存する */
        // Requestインスタンス
        $request->session()->put('key', 'value');
        // Sessionヘルパ
        session(['key' => value]);
        // Sessionファサード
        Session::put('key', 'value');
        
        /* セッションに値を削除する */
        // Requestインスタンス
        $request->session()->forget('key');
        // Sessionヘルパ
        session()->forget('key');
        // Sessionファサード
        Session::forget('key');
        
        /* セッション中に値が存在することの確認(existsはnullでもTrueを返す) */
        // Requestインスタンス
        $request->session()->has('key');
        $request->session()->exists('key');
        // Sessionヘルパ
        session()->has('key');
        session()->exists('key');
        // Sessionファサード
        Session::has('key');
        Session::exists('key');
    }
}

セッションの共有を検討してみた

既存プロダクトとLaravelでセッションを共有させる方法を検討してみました。
既存プロダクトがDBでセッション管理されているため、今回はLaravelでもDBを用いたセッション管理を前提に考えています。
また、既存プロダクトではセッション周りは純粋なPHPで実装していますが、Laravelのセッション周りは独自実装になっています。
どこかでsession_start();しておけばいい感じになるだろう、というわけにはいきません。
この違いも少し厄介なポイントです。

LaravelでDBを使ってセッション管理する

LaravelでDBを使いセッション管理する場合は、以下のようなテーブルを作成する必要があります。

Schema::create('sessions', function ($table) {
    $table->string('id')->unique();
    $table->foreignId('user_id')->nullable();
    $table->string('ip_address', 45)->nullable();
    $table->text('user_agent')->nullable();
    $table->text('payload');
    $table->integer('last_activity');
});

以下のように設定しておくと、セッション管理に使用するテーブルと認識されます。

config/session.php

return [

    /*
    |--------------------------------------------------------------------------
    | Default Session Driver
    |--------------------------------------------------------------------------
    |
    | This option controls the default session "driver" that will be used on
    | requests. By default, we will use the lightweight native driver but
    | you may specify any of the other wonderful drivers provided here.
    |
    | Supported: "file", "cookie", "database", "apc",
    |            "memcached", "redis", "dynamodb", "array"
    |
    */

    'driver' => env('SESSION_DRIVER', 'database'),
    
    :
    :
    

    /*
    |--------------------------------------------------------------------------
    | Session Database Table
    |--------------------------------------------------------------------------
    |
    | When using the "database" session driver, you may specify the table we
    | should use to manage the sessions. Of course, a sensible default is
    | provided for you; however, you are free to change this as needed.
    |
    */

    'table' => 'sessions',
    
        
    :
    :
]

セッション共有の検討

既存プロダクトで使用しているテーブルを使用するか、Laravelに合わせたテーブルを使用するのか…他にもいくつか選択肢がありました。
少なくとも、Laravelのセッション機能を活用しようと思うと、 LaravelでDBを使ってセッション管理する際にテーブル定義の通り作成が必要となるようです。
Laravelのセッションが確立するまでの独自実装を追ってみると、以下にたどり付きました。
Illuminate/Session/DatabaseSessionHandler.php
ここを見る限りは指定されているテーブル定義前提で動くような実装になっています。
セッション管理のテーブルやセッション周りの実装が似ていれば移行前提で検討できるかもしれませんが、そうでなければ難しいです…。

よって、LaravelデフォルトのDBのセッションドライバのままでは、既存プロダクトとのセッション共存は困難であることがわかりました。 しかし、Laravelにはカスタムセッションドライバという仕組みがあり独自のセッションドライバを実装できます。

Laravelのカスタムセッションドライバ

以下のように、SessionHandlerInterfaceを実装することで独自のセッションドライバの作成が可能です。

<?php

namespace App\Extensions;

class CustomSessionHandler implements \SessionHandlerInterface
{

    // open: ファイルベースでなければ空でOK
    public function open($savePath, $sessionName) {}
    // openと同様
    public function close() {}
    // セッションの読み込み処理($sessionIdに紐づくセッションデータの取得)
    public function read($sessionId) {}
    // セッションの書き込み処理($sessionIdに紐づく$dataの書き込み)
    public function write($sessionId, $data) {}
    // セッションの読み込み処理($sessionIdに紐づくセッションデータの削除)
    public function destroy($sessionId) {}
    // セッションの読み込み処理($lifetimeを超えたセッションデータの削除)
    public function gc($lifetime) {}
}

次に、作成したカスタムセッションドライバの登録方法についてです。
Sessionファサードextendメソッドで登録することができます。
ServiveProviderbootメソッドで上記メソッドを呼ぶことでカスタムセッションドライバの登録ができます。
既存のAppServiceProviderまたはセッション用に作成したServiceProviderから呼び出します。
下記はServiceProviderから呼び出す例です。

<?php

namespace App\Providers;

use App\Extensions\CustomSessionHandler;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;

class SessionServiceProvider extends ServiceProvider
{
    /**
     * 全アプリケーションサービスの登録
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * 全アプリケーションサービスの初期起動
     *
     * @return CustomSessionHandler
     */
    public function boot()
    {
        Session::extend('custom', function ($app) {
            // Return implementation of SessionHandlerInterface...
            return new CustomSessionHandler;
        });
    }
}

最後に、作成したカスタムセッションドライバをconfig/session.phpで設定すると、使用できるようになります。

config/session.php

return [

    /*
    |--------------------------------------------------------------------------
    | Default Session Driver
    |--------------------------------------------------------------------------
    |
    | This option controls the default session "driver" that will be used on
    | requests. By default, we will use the lightweight native driver but
    | you may specify any of the other wonderful drivers provided here.
    |
    | Supported: "file", "cookie", "database", "apc",
    |            "memcached", "redis", "dynamodb", "array"
    |
    */

    'driver' => env('SESSION_DRIVER', 'custom'),
    
    :
    

まとめ

Laravelのセッションについてまとめてみました。
既存のプロダクトとLaravelでセッションをさせる方法は現在検討中なので、
また次の機会に実際に実装したカスタムセッションドライバなどは紹介できればと思います。

Copyright © RAKUS Co., Ltd. All rights reserved.