まえがき
マルチログインで作っている際、コントローラを別々に作っていると、
合わせてビューも別ディレクトリに分けて作りたいな、と思った次第。
とりあえず出来たので残しておきます。
前提
ツールのバージョン
- XAMPP v3.2.4
- PHP 7.3.8
- Laravel 5.5.45
- PHP 7.3.8
すでにやってるルーティング
<?php
// user
Route::prefix('user')->name('user.')->group(function () {
// ユーザー画面
Route::get('/', 'User\TopController@index')->name('top');
});
// admin
Route::prefix('admin')->name('admin.')->group(function () {
// 管理者画面
Route::get('/', 'Admin\TopController@index')->name('top');
});
URLとルーティング名にプレフィックスを使ってます。
なので、ユーザー側のトップのURLは http://localhost/user/、
管理側のトップURLは http://localhost/admin/になります。
同様に、ユーザー側トップのルーティング名は user.top、管理側は admin.topとなります。
コントローラはマルチログインごとにディレクトリをきっているので、
コントローラアクションの呼び出しにも含めています。
ただ、このまま素直に作ってしまうと、ビューが同じテンプレート名になってしまうので、
それが嫌だった、というわけです。
まぁ、トップくらいなら top/user.blade.php とかで分ければいいだけだけど。。
設定
呼び出しやすいように設定ファイルにパスを追加しておきます。
<?php
return [
/**
* 各権限ごとのビューテンプレートパス
*/
'user_path' => resource_path('views/user'),
'admin_path' => resource_path('views/admin'),
];
で、以下のミドルウェアを用意。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\View\Factory as ViewFactory;
use Illuminate\View\FileViewFinder;
/**
* 権限により参照するビューディレクトリを変更する
*/
class ViewSwitchMiddleware
{
/**
* ビュー
* @var Illuminate\Contracts\View\Factory
*/
protected $view;
public function __construct(ViewFactory $view)
{
$this->view = $view;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
* @see https://qiita.com/sogawa@github/items/644710df8d58d54f2665
*/
public function handle($request, Closure $next)
{
$add_path = $this->getViewPath($request);
if (!empty($add_path)) {
$this->resettingView($add_path);
}
return $next($request);
}
/**
* ビューディレクトリパスを取得
* ルーティングのプレフィックスにより見分ける
*
* @access private
* @param \Illuminate\Http\Request $request
* @return string|null
*/
private function getViewPath($request)
{
// プレフィックスで分岐
$uri_prefix = $request->route()->getPrefix();
if ($uri_prefix == '/admin') {
// 管理
return \Config::get('view.admin_path');
} else {
// ユーザー
return \Config::get('view.user_path');
}
// // 権限で分岐
// $middlewares = $request->route()->middleware();
// // ルーティング名で分岐
// $route_name = $request->route()->getName();
}
/**
* ビューの再設定
*
* @access private
* @param string $add_path
* @return void
*/
private function resettingView($add_path)
{
// 権限ごとのディレクトリを先に検索させるため、パス情報の先頭へ
$paths = \Config::get('view.paths');
array_unshift($paths, $add_path);
// パスを変更するためFileViewFinderを生成
// ただし、そのまま新しいFileViewFinderをセットするとすでにセットされたhint情報が消えてしまうので
// すでに設定済みのFileViewFinderのhintをコピーしてからセットする
// (ページャーや通知でhint情報をいれている)
$finder = new FileViewFinder(app()['files'], $paths);
$old_finder = $this->view->getFinder();
foreach ($old_finder->getHints() as $namespace => $hint) {
$finder->addNamespace($namespace, $hint);
}
$this->view->setFinder($finder);
}
}
ちょうどよくルーティングでプレフィックスを使っているので、
それでもってテンプレートのパス分岐も判定します。
パスが取得できたら、テンプレートの設置先パスの先頭に追加。
viewのFinderを差し替えるわけだけど、
この際にそのまま新しいFinderを入れてしまうと、
元のFinderに入っていたhintがなくなりそもそも表示でコケることになるので、
ここだけ新しいFinderに入れ直してから再セットします。
その後、このMiddlewareを読み込ませて設定終わり。
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
//
// 省略
//
/**
* The application's route middleware groups.
*
* @var array
*/
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,
\App\Http\Middleware\ViewChangeMiddleware::class,
],
'api' => [
'throttle:60,1',
'bindings',
],
];
//
// 省略
//
}
これにより、ビューはそれぞれで分割できるようになります。
+---resources
\---views
+---admin
| \---top
\---user
\---top
こんな感じ。
あとがき
やってる途中で、たんにディレクトリ分けて、ビューを呼び出すパス変えてやればいいだけだな、とは思ってた(´・ω・`)
まぁ、もう作ってたところ書き換えて回るのもメンドいし、いいかな、と。