サブドメインを使って複数のルーティングを分ける【Laravel】

sub-domain-routingLaravel

今回はLaravelでサブドメインごとにルーティングを分ける方法を解説します。

Laravelではデフォルトでwebとapiの2つのルーティングが設定されています。

それとは別にサブドメインを使って新しいルーティングを作成したいときは簡単にルーティングを追加することが可能です。

認証もルーティングごとに分けることでマルチログインも対応できます。

管理画面用のルーティング追加を例に進めます。

ルーティングファイルの追加

まず新たに作成したいルーティングファイルを作成します。

routesディレクトリ配下にadmin.phpを作成します。

laravel-app
  |-routes
    |-admin.php ←追加
    |-api.php
    |-channels.php
    |-console.php
    |-web.php

ルーティングファイルを作成してルートを記述しても今のままでは404エラーになってしまいます。

<?php

// 存在しないルートのため404エラーになる
Route::get('/hello', function () {
    return 'Hello Admin World!';
});

余談ですが、laravel-ide-helperを使えばファサードの保管なども効いてくれて実装が楽になります。

laravel-ide-helperについてはこちらを参考ください。

RouteServiceProviderの設定

ルーティングファイルを作成しただけではそのルートに対してアクセスできないので、RouteServiceProviderに設定を追加してあげることでadmin.phpに記述されているルートを読み込むようにします。

<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
    /**
     * This namespace is applied to your controller routes.
     *
     * In addition, it is set as the URL generator's root namespace.
     *
     * @var string
     */
    protected $namespace = 'App\Http\Controllers';

    /**
     * The path to the "home" route for your application.
     *
     * @var string
     */
    public const HOME = '/home';

    /**
     * Define your route model bindings, pattern filters, etc.
     *
     * @return void
     */
    public function boot()
    {
        parent::boot();
    }

    /**
     * Define the routes for the application.
     *
     * @return void
     */
    public function map()
    {
        $this->mapAdminWebRoute(); // 追加
        $this->mapApiRoutes();
        $this->mapWebRoutes();
    }

    // ==========ここから==========
    protected function mapAdminWebRoute()
    {
        Route::middleware('web')
            ->namespace($this->namespace)
            ->group(base_path('routes/admin.php'));
    }
    // ==========ここまで追加==========

    /**
     * Define the "web" routes for the application.
     *
     * These routes all receive session state, CSRF protection, etc.
     *
     * @return void
     */
    protected function mapWebRoutes()
    {
        Route::middleware('web')
            ->namespace($this->namespace)
            ->group(base_path('routes/web.php'));
    }

    /**
     * Define the "api" routes for the application.
     *
     * These routes are typically stateless.
     *
     * @return void
     */
    protected function mapApiRoutes()
    {
        Route::prefix('api')
            ->middleware('api')
            ->namespace($this->namespace)
            ->group(base_path('routes/api.php'));
    }
}

先程作成したadmin.phpに対してwebというミドルウェアを通って通信するということを設定してあげます。

これで先程admin.phpに定義した/helloというルートに対してアクセスできるようになりました。

サブドメイン設定

定義したルートにアクセスできるようになりましたが、今のままだとどちらのファイルもエンドポイントのドメインが同じになってしまいます。

同じエンドポイントだった場合どちらのファイルにアクセスしているのか気になったため試してみました。

それぞれのファイルで同じエンドポイントを定義しそのエンドポイントに対してリクエストをしてみると、RouteServiceProviderのmapメソッドで後に定義したルーティングにアクセスしていることがわかりました。

今回の場合だとweb.phpの方にアクセスします。

仮にweb.phpにもadmin.phpで定義したものと全く同じルートを定義するとエラーにはなりませんがバグに繋がりそうです。

    public function map()
    {
        $this->mapAdminWebRoute();
        $this->mapApiRoutes();
        $this->mapWebRoutes(); // 同じエンドポイントだと後に定義しているこっちにアクセスする
    }

別ルートを作成するからにはエンドポイントのドメインは分けるのが普通です。

ここでadmin.phpのルーティングにはサブドメインを設定します。

こうすることでhttp://admin.localhost/helloにアクセスが可能になります。

仮にweb.phpに同じURIのルートが存在してもドメイン部分が異なるため別エンドポイントとなります。

<?php

Route::domain('admin.' . config('app.domain'))->group(function () {
    Route::get('/hello', function () {
        return 'Hello Admin World!';
    });
});

Routeファサードのdomainメソッドを使うことでそのルーティングのドメインを指定できます。

今回の場合だとadmin.に対してconfigに定義しているアプリケーションのドメインを文字列結合しています。

config/app.phpに下記のように設定してアプリケーションのドメインをconfig値として持っておきます。

envメソッドの第2引数はデフォルト値です。

'domain' => env('APP_DOMAIN', 'localhost');

.envファイルにAPP_DOMAINという変数名にアプリケーションのドメインを設定します。

【おまけ】マルチログイン

細かくは解説しませんが、今までのルーティングのを使ってマルチログインも実装できます。

詳しくはこちらが参考になるかと思います。

まず認証の設定を変更します。

auth.phpに対してAdmin用のガードやプロバイダーを追加してあげます。

※Adminモデルが存在する前提で進めています。

<?php

use App\Models\Admin;
use App\Models\User;

return [
    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'admin' => [ // ------------------ 追加
            'driver' => 'session',
            'provider' => 'admins'
        ],
    ],

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model'  => User::class,
        ],

        'admins' => [ // ------------------ 追加
            'driver' => 'eloquent',
            'model'  => Admin::class,
        ],
    ],

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table'    => 'user_password_resets',
            'expire'   => 60,
            'throttle' => 60,
        ],

        'admins' => [ // ------------------ 追加
            'provider' => 'admins',
            'table' => 'admin_password_resets',
            'expire' => 60,
            'throttle' => 60,
        ]
    ],
];

Authenticateというミドルウェアがあるのでそれを使って指定したガードに対して認証しているかを確認し、認証していない状態であればアクセスできなくなります。

このように管理画面用のルーティングとユーザー用のルーティングを分けることが実現できます。

<?php

Route::domain('admin.' . config('app.domain'))->group(function () {
    // adminというガードに認証しているか
    Route::middleware('auth:admin')->group(function () {
        Route::get('/hello', function () {
            return 'Hello Admin World!';
        });
    });
});
<?php

Route::domain(config('app.domain'))->group(function () {
    // webというガードに認証しているか
    Route::middleware('auth:web')->group(function () {
        Route::get('/hello', function () {
            return 'Hello World!';
        });
    });
});

単にサブドメインを切ってルーティングを分けるだけであればそんなに難しいことではなかったですね。

コメント

タイトルとURLをコピーしました