Laravel で手っ取り早くモジュラモノリスやるときは laravel-modules を良く使います。
Modules 配下に各モジュールを配置しているためファイルがモジュール毎にまとまっており、内部設計的にはシンプルでわかりやすいと思います。ただし、モジュール間でファイルを相互に参照出来るので、依存関係の検証は別の仕組みでなんとかする必要があります。
今回は、laravel-modules でモジュール内に作成した middleware を登録する方法をまとめてみます。
準備
Laravel と laravel-modules をインストールしておきます。今回は両方とも11です。
$ composer create-project laravel/laravel .
$ composer require nwidart/laravel-modules
忘れがちな設定ですが、extra セクションに merge-plugin を追加します。
"extra": {
"laravel": {
"dont-discover": []
},
"merge-plugin": {
"include": [
"Modules/*/composer.json"
]
}
},
適当な名前のモジュールと middleware を作っておきます。middleware は artisan コマンドを使うと簡単に生成出来ます。
$ php artisan module:make Posts
$ php artisan module:make-middleware PostsMiddleware Posts
作成した middleware に適当な処理を追加しておきます。
<?php
namespace Modules\Posts\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class PostsMiddleware
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next)
{
echo 'Hello, world!! ';
return $next($request);
}
}
bootstrap/app.php で登録する
middleware は bootstrap/app.php の withMiddleware セクション内で登録するのが正攻法です。
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
// グローバルミドルウェアで登録
$middleware->append(\Modules\Posts\Http\Middleware\PostsMiddleware::class);
// ミドルウェアグループに登録
$middleware->appendToGroup('group', [\Modules\Posts\Http\Middleware\PostsMiddleware::class]);
// エイリアスとして登録
$middleware->alias(['post' => \Modules\Posts\Http\Middleware\PostsMiddleware::class ]);
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();
以下のようなルートを用意して GET リクエストを投げてみます。
<?php
use Illuminate\Support\Facades\Route;
Route::get('/posts/global', function() {
return '(global)';
});
Route::get( '/posts/alias', function() {
return '(alias)';
})->middleware('post');
Route::middleware(['group'])->group(function() {
Route::get( '/posts/group', function() {
return '(group)';
});
});
$ curl http://localhost:8000/posts/global
Hello, world!! (global)%
$ curl http://localhost:8000/posts/alias
Hello, world!! Hello, world!! (alias)%
$ curl http://localhost:8000/posts/group
Hello, world!! Hello, world!! (group)%
alias と group はグローバルミドルウェアと重複して登録しているので2回出力がありますが、ちゃんと動いていることが確認出来ます。
ただし、グローバルミドルウェアとして登録されているので、モジュール配下のルート以外で定義されたルートでも当該 middleware は適用されてしまいます。この点だけは注意が必要です。
<?php
use Illuminate\Support\Facades\Route;
Route::get('/global', function() {
return '(global common)';
});
$ curl http://localhost:8000/global
Hello, world!! (global common)%
RouteServiceProvider で登録する
別の方法として、RouteServiceProvider に記述することも出来ます。
この RouteServiceProvider は Illuminate\Foundation\Support\Providers\RouteServiceProvider
クラスを継承しており、register 内で map メソッドを呼び出します。
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->booted(function () {
$this->setRootControllerNamespace();
if ($this->routesAreCached()) {
$this->loadCachedRoutes();
} else {
$this->loadRoutes();
$this->app->booted(function () {
$this->app['router']->getRoutes()->refreshNameLookups();
$this->app['router']->getRoutes()->refreshActionLookups();
});
}
});
}
/**
* Load the application routes.
*
* @return void
*/
protected function loadRoutes()
{
if (! is_null(self::$alwaysLoadRoutesUsing)) {
$this->app->call(self::$alwaysLoadRoutesUsing);
}
if (! is_null($this->loadRoutesUsing)) {
$this->app->call($this->loadRoutesUsing);
} elseif (method_exists($this, 'map')) {
$this->app->call([$this, 'map']);
}
}
RouteServiceProvider の map メソッド内では、web 及び api ルートに関する定義がされているので、ここで middleware の登録をすると良さそうです。今回はエイリアスを登録して web ルートに適用してみます。
<?php
namespace Modules\Posts\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Modules\Posts\Http\Middleware\PostsMiddleware;
class RouteServiceProvider extends ServiceProvider
{
/**
* Called before routes are registered.
*
* Register any model bindings or pattern based filters.
*/
public function boot(): void
{
parent::boot();
}
/**
* Define the routes for the application.
*/
public function map(): void
{
Route::aliasMiddleware('post', PostsMiddleware::class);
$this->mapApiRoutes();
$this->mapWebRoutes();
}
/**
* Define the "web" routes for the application.
*
* These routes all receive session state, CSRF protection, etc.
*/
protected function mapWebRoutes(): void
{
Route::middleware(['web', 'post'])->group(module_path('Posts', '/routes/web.php'));
}
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*/
protected function mapApiRoutes(): void
{
Route::middleware('api')->prefix('api')->name('api.')->group(module_path('Posts', '/routes/api.php'));
}
}
GET リクエストを投げてみます。
$ curl http://localhost:8000/posts/global
Hello, world!! (global)%
middleware が適用されていました。モジュール内で作成した middleware をモジュール内で閉じたい場合はこの方法が良さそうな気がします。
ただし、モジュール配下の RouteServiceProvider 内で登録した middleware のエイリアスはモジュール外でも使えるということと、モジュール外で使った場合、当該モジュールが disabled だとエイリアスが見つからず500エラーを返してしまう点は注意が必要です。
<?php
use Illuminate\Support\Facades\Route;
Route::get('/global', function() {
return '(global common)';
})->middleware('post');
{
"Posts": false
}
$ curl -I http://localhost:8000/global
HTTP/1.1 500 Internal Server Error