Laravel Jetstreamの概要
Laravel JetstreamはLaravel 8から標準になった認証機能のパッケージで、7までのlaravel/uiや、5.8までの組み込みのartisan make:auth
の代わりとなるパッケージ。
Laravel本体とは独立して存在しているが、Laravelのインストール時にいっしょに設定することも可能。
: composerとartisanで
composer require laravel/jetstream
php artisan jetstream:install livewire
: あるいは、Laravelのインストール時に
laravel new <DIR> --jet
Jetstreamは単体のパッケージではなく、いくつかのパッケージを寄せ集めて、その上で統合している。各パッケージが担当する機能については以下のようになっている。
- Laravel Fortifyがセッションベースの認証を。
- Laravel SanctumがAPI認証を。
- LivewireかInertia.jsがユーザプロフィール・チーム管理周りのUIのビューを。
- Tailwind CSSがUIのデザインを。
さらにJetstream自体がルートやビュー、コントローラのスカフォールド等を担当する。
この記事ではビューにはLivewireを使う前提とし、Inertia.jsは取り扱わない。またTailwind CSSも取り扱わない。
その前提で、それぞれの機能をどう変更するべきかの簡易なリファレンスを提供する。
参考:
Jetstreamのインストール/インストール 1.0 Laravel Jetstream
ビューを変更するには
機能自体は提供されたものをそのまま使うとしても、デザインをそのまま使うことは少ない。また機能を変更する場合にはデザインも同時に変更することがほとんどだろう。たとえばログインにメールアドレスとは別にログインIDを用意して使うようにするだけでも、ログインフォームの項目名の修正が必要だ。
ということでまずビューの変更方法から始める。
Laravel Jetstreamで提供されるビューは大まかに以下の3つに分けられる。
-
auth
以下にある、Fortifyによる基本的な認証機能のためのもの。 -
vendor/laravel/jetstream/resources/views/components
にある一般的なUIのためのBladeコンポーネント。 -
api
,profile
,teams
以下にあるLivewireコンポーネントのビュー。
認証機能のためのauth
以下のビュー
resources/views/auth
以下に存在しているビューは、単純に直接編集すればよい。ログインフォームであれば、resources/views/auth/login.blade.php
だ。
中身を見てみると、見慣れないものがある。<x-guest-layout>
のような、x-
接頭辞のあるタグだ。
これはLaravel 7(8ではなく)から導入されたコンポーネントの新しい書き方で、laravel/uiでは使われていなかった。
<x-guest-layout>
はapp/View/Components/GuestLayout.php
コンポーネント経由でresources/views/layouts/guest.blade.php
を読み込んでいるので、このファイルを変更すればよい。
これらのビューはFortifyのためのものではあるが、Jetstreamによって提供されている。
参考:
コンポーネントの表示/Bladeテンプレート 8.x Laravel
一般的なUIのためのx-jet-
接頭辞のBladeコンポーネント
auth
以下のビューを見てみると、接頭辞がx-jet-
となるコンポーネントが見つかる。これらはJetstreamが登録するBladeコンポーネントで、実体はvendor/laravel/jetstream/resources/views/components/
以下にある。
ボタンやモーダルなど、ビューの再利用性を上げるためによく使うコンポーネントが登録されているが、これらも変更なしで使うことはほとんどないだろう。
変更する場合はartisan vendor:publish --tag=jetstream-views
でresources/views
以下に出力した上で編集する。
なおこれらのコンポーネントはLaravel\Jetstream\JetstreamServiceProvider
( vendor/laravel/jetstream/src/JetstreamServiceProvider.php
, App\Providers\JetstreamServiceProvider
とは別物。Jetstreamのほとんどの機能はこのサービスプロバイダによってLaravelアプリケーション内で使用可能になっている。)で一つずつエイリアスを登録されている。そのため同じディレクトリにビューファイルを保存しても、x-jet-
接頭辞で読み込むことはできない。
コンポーネントのエイリアスは、Blade::getClassComponentAliases()
で一覧できる。エイリアスが登録されていればそれが最優先で、次にクラスベースコンポーネント、最後にresources/views/components
以下のコンポーネント(匿名コンポーネント)という優先順で読み込まれる。
Livewireコンポーネント
auth
以下のビューでは使われていないが、ログイン後のレイアウトであるlayouts.app
やユーザプロフィールページであるprofile.show
などでは、@livewire
というBladeディレクティブが使われている。
@livewire
は@include
のような挙動をする。ディレクティブのある位置にほかのビューを読み込むような挙動だ。ただしBladeのビューではなく、Livewireコンポーネントを読み込む。
Jetstreamが提供するビューを変更する中で、このディレクティブに対する理解も必要になってくる。とはいえLivewire自体を詳細に解説するのがこの記事の目的ではないため、説明は最小限に留める。ほか同様、どこを変更すればいいのかのみ説明する。
artisan jetstream:install livewire
した時点で存在するビュー内で、@livewire()
に渡されている文字列には、'api'
, 'profile'
, 'teams'
が接頭辞に来るものと、'navigation-dropdown'
がある。
これらの名前と対応するビューはresources/views
以下に、Bladeビューと同じ対応方法でビューが存在する。'api.api-token-manager'
であれば、resources/views/api/api-token-manager.blade.php
にある。
これだけ見るとLivewireコンポーネントがBladeビューのような名前・ファイルの対応になっているように見えるが、じつは違う。
むしろBladeコンポーネントに近く、Livewireコンポーネントの名前は、ビューではなくクラスに対応している。Bladeコンポーネントはクラスがない場合はビューにフォールバックしているが、Livewireコンポーネントの場合はそれもない。
foo.bar
というコンポーネントは、App\Livewire\Foo\Bar
と関連付いている。このコンポーネントが最終的にresources/views/foo/bar.blade.php
というビューを読み込んでいたとしても、それはBarクラス内でそのビューを読み込んでいるからという以上のことはない。誤解しやすいところなので注意しよう。
artisan livewire:make Foo\\Bar
でLivewireコンポーネントのスカフォールドを作成してみるとわかりやすい。
resources/views/livewire/foo/bar.blade.php
というビューが作られるが、これはBar
クラスのrender()
メソッドで明示的に読み込むようにしているから使われるだけで、これを変えてしまえば、このビューは無視される。
Jetstreamが提供するLivewireコンポーネントはLaravel\Jetstream\JetstreamServiceProvider
でエイリアスが登録されている。
Bladeコンポーネントと違い、エイリアスを一覧するメソッドは用意されていないが、artisan tinker
からdump(app('livewire'))
などしてみると、$componentAliases
というプロパティの中で定義が確認できる。
エイリアスが登録されていない場合は、名前からクラス名に変更され、読み込まれる。上記のfoo.bar
であれば、App\Livewire\Foo\Bar
となる。
参考:
最近Livewireの公式ドキュメントが日本語訳された。Laravel公式ドキュメントの日本語訳者によるものだ。
2.x Livewire
使用するビュー自体を変更する
ここまでは主にビューの内容を変更する方法について書いた。だが使用するビュー自体を変更したい場合もあるだろう。そのようなカスタマイズも用意されている。簡単に紹介する。
Fortifyのビュー
Fortifyのビューは、Laravel\Jetstream\JetstreamServiceProvider
内で、Fortify::viewPrefix('auth.')
というメソッドで、auth
以下から読み込むように指示されている。
変更する場合はApp\Providers\JetstreamServiceProvider::boot()
内などで、viewPrefix()
を実行すればよい。
<?php
namespace App\Providers;
use Laravel\Fortify\Fortify;
class JetstreamServiceProvider extends ServiceProvider
{
public function boot()
{
Fortify::viewPrefix('myauth.');
}
}
Laravel\Fortify\Fortify::viewPrefix()
の実装を見てみるとわかるが、使用する個々のビューを変更することも可能だ。同クラス内にビューごとに専用のメソッドが用意されている。
<?php
namespace App\Providers;
use Laravel\Fortify\Fortify;
class JetstreamServiceProvider extends ServiceProvider
{
public function boot()
{
Fortify::loginView('login');
}
}
とすれば、resources/views/login
が使用される。
参考:
認証 1.0 Laravel Jetstream の"ビューレンダリングのカスタマイズ"以下。
Bladeコンポーネント
Laravel\Jetstream\JetstreamServiceProvider
で、Blade::component()
によってエイリアスが登録されている。エイリアスは上書きできるので、これもApp\Providers\JetstreamServiceProvider::boot()
内などで登録すればよい。
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
class JetstreamServiceProvider extends ServiceProvider
{
public function boot()
{
Blade::component('my-section-border', 'jet-section-border');
}
}
ビューは開発環境でもキャッシュされ、ビュー自体の変更がなければ再コンパイルされないため、Bladeコンポーネントを変更した際はビューのキャッシュをクリアしないと有効にならない場合がある。そのような場合はartisan view:clear
する。
Livewireコンポーネント
Bladeコンポーネント同様だが、エイリアスを登録するメソッドはLivewire::component()
だ。
<?php
namespace App\Providers;
use App\Http\Livewire\MyNavigationDropdown;
use Livewire\Livewire;
class JetstreamServiceProvider extends ServiceProvider
{
public function boot()
{
Livewire::component('navigation-dropdown', MyNavigationDropdown::class);
}
}
ルートを変更するには
Jetstreamによって定義されるルートには2系統ある。
- Jetstreamによる、ユーザプロフィールやチーム関連、またLivewireやInertia.js用のAPI関連のルート。
- Fortifyによるログインページなどの認証周りのルート。
artisan route:list
でどのようなルートが登録されているか確認できる。login
, logout
, register
などは2のFortifyによるもので、profile.show
や、名前のないapi/user
パスのルートは、1のJetstreamによるものだ。
それぞれ変更方法は微妙に違う。
Jetstreamによるルート
JetstreamによるルートはいつものJetstreamパッケージ内のJetstreamServiceProviderから定義されている。実体はLivewireを使用している場合はvendor/laravel/jetstream/routes/livewire.php
となる。
ここでは機能の有効無効によるオン・オフ以上のカスタマイズはできない。たとえばチーム機能の有効・無効によってチーム機能関連のルートは有効・無効になるが、それだけだ。
<?php
# vendor/laravel/jetstream/routes/livewire.php
#
# Copyright (c) Taylor Otwell
# https://github.com/laravel/jetstream/blob/2.x/LICENSE.md
use Illuminate\Support\Facades\Route;
use Laravel\Jetstream\Http\Controllers\CurrentTeamController;
use Laravel\Jetstream\Http\Controllers\Livewire\TeamController;
use Laravel\Jetstream\Jetstream;
Route::group(['middleware' => config('jetstream.middleware', ['web'])], function () {
Route::group(['middleware' => ['auth', 'verified']], function () {
// Teams...
# チーム機能が有効な場合のみ、以下のルートが有効になっている
if (Jetstream::hasTeamFeatures()) {
Route::get('/teams/create', [TeamController::class, 'create'])->name('teams.create');
Route::get('/teams/{team}', [TeamController::class, 'show'])->name('teams.show');
Route::put('/current-team', [CurrentTeamController::class, 'update'])->name('current-team.update');
}
});
});
なのでたとえばプロフィール編集ページのパスを変更したいと思った場合は、手動でルートを設定しなければならない。
一から設定してもいいが、Jetstreamのルートファイルをpublishして、カスタマイズすることもできる。
php artisan vendor:publish --tag=jetstream-routes
でroutes/jetstream.php
が配置されるので、それを自由にカスタマイズし、追加で、
- RouteServiceProvider等で上記ファイルを読み込む。
- 元のルートをJetstreamServiceProvider::register()あたりで
Jetstream::ignoreRoutes()
とすることによって無効化する。このJetstreamServiceProviderはApp\Providers
の方なので注意。
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
public function boot()
{
$this->routes(function () {
Route::namespace($this->namespace)
->group(base_path('routes/jetstream.php'));
});
}
}
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Laravel\Jetstream\Jetstream;
class JetstreamServiceProvider extends ServiceProvider
{
public function register()
{
Jetstream::ignoreRoutes();
}
}
Fortifyによるルート
こちらはもう少しカスタマイズが考えられていて、たとえば接頭辞をつけるだけであればconfig/fortify.php
に設定を追加するだけで可能だ。path
というキーにたとえば'bar'
という値を設定すれば、ログインページのパスは'/bar/login'となる。
<?php
# config/fortify.php
return [
'path' => 'bar',
];
だがそれ以上のカスタマイズ方法は用意されていない。vendor:publish
で出力することもできず、完全に手動でルートを設定するしかない。
元のルートを無効にする場合はJetstreamと同じように、Fortify::ignoreRoutes()
を使う。
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Laravel\Fortify\Fortify;
class JetstreamServiceProvider extends ServiceProvider
{
public function register()
{
Fortify::ignoreRoutes();
}
}
その上で、vendor/laravel/fortify/routes/routes.php
を参考に必要なルートを設定すればよい。
機能を変更するには
Jetstreamが提供する機能も、Fortifyが提供する機能も、コントローラアクション単位での変更はできない。ユーザ登録にLaravel\Fortify\Http\Controllers\RegisteredUserController::store
を使わず自前のメソッドを使うような変更をしたいならば、ルートの変更の項で見たようにルート自体を差し替えるしかない。
とはいえコントローラアクションは抽象的に実装されており、変更しないでもかなりの部分までカスタマイズができるようになっている。
上記の例でいえば、じっさいのユーザ作成の実装も、作成後のレスポンスも簡単にカスタマイズ可能だ。これについてはDIによる機能の変更の項で説明する。
features設定による機能のオン・オフ
JetstreamでもFortifyでも、カスタマイズされがちな機能については簡単にオン・オフの切り替えができるようになっている。
Jetstreamではプロフィール写真のアップロード、チーム機能などが、Fortifyではユーザ登録やパスワードリセットのような、以前はAuth::routes()
への引数で切り替えていた機能や、プロフィールの更新、2要素認証などが、簡単に切り替えられる。
それぞれ設定変数jetstream.features
, fortify.features
を使用する。
<?php
# config/jetstream.php
use Laravel\Jetstream\Features;
return [
'features' => [
# プロフィールの写真のアップロードを有効にする
Features::profilePhotos(),
],
];
ただし一部の機能は、features
の設定だけでは不完全だ。メールアドレスの確認には、7まで同様プロバイダクラスにMustVerifyEmailインターフェイスを実装する必要があるし、チーム機能に関しては、laravel new
やartisan jetstream:install
の時点で有効にしておかないと、features
で設定しただけでは動作しないどころかエラーになったりする。
参考:
認証 1.0 Laravel Jetstreamの"メール確認"以下。
プロファイル管理 1.0 Laravel Jetstreamの"プロフィール写真の有効化"以下。
API 1.0 Laravel Jetstreamの"APIサポートの有効化"以下。
DIによる機能の変更
Fortifyが提供するコントローラでは、アクションを直接差し替えることこそできないが、重要な部分はDIによって差し替えられるようになっている。
ログインフォームの表示やログイン・ログアウト処理のためのLaravel\Fortify\Http\Controllers\AuthenticatedSessionController
を見てみると、ログインフォームを表示するlogin()
メソッドはapp()
で解決したサービスを返している。
これらは単純に別のクラスをバインドすることで差し替えることもできるが、専用のメソッドも用意されている。Fortify::loginView()
は名前通りログインフォームのビューを差し替えることができるし、Fortify::createUsersUsing()
はユーザ作成オブジェクトを差し替えられる。前者は差し替えるクラス等を直接指定するのではなく、ビュー名を渡すこともできるのでバインドするよりも簡易だ。これらをApp\Providers\FortifyServiceProvider
などで実行する。
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Laravel\Fortify\Fortify;
class FortifyServiceProvider extends ServiceProvider
{
public function boot()
{
Fortify::loginView('myauth.login');
Fortify::createUsersUsing(MyCreateNewUser::class);
}
}
Jetstreamが提供するコントローラでも、主にチーム関係の機能を名前がuseで始まるメソッドや、Usingで終わるメソッドで差し替えられる。
参考:
認証 1.0 Laravel Jetstreamの"ビューレンダリングのカスタマイズ"以下、また"User認証のカスタマイズ"以下。
セキュリティ 1.0 Laravel Jetstreamの"パスワード確認方法のカスタマイズ"以下。
ログイン処理のカスタマイズ
現代のログイン処理は複雑だ。IDとパスワードによる認証だけではなく、ログイン試行回数をチェックしたり、追加の要素で認証したいかもしれない。そのようなニーズにも簡単に応えられるよう、Fortifyのログイン処理は非常にカスタマイズしやすく作られている。
具体的にはミドルウェアのような動作をする「ログインパイプライン」によって、指定した機能を連鎖的に実行する形になっている。
設定fortify.pipelines.login
の値として、handle()
メソッドを持ったクラスを指定すれば、それらのクラスが順番に実行される。
<?php
# config/fortify.php
use Laravel\Fortify\Actions\AttemptToAuthenticate;
use Laravel\Fortify\Actions\EnsureLoginIsNotThrottled;
use Laravel\Fortify\Actions\PrepareAuthenticatedSession;
use Laravel\Fortify\Actions\RedirectIfTwoFactorAuthenticatable;
return [
'pipelines' => [
'logins' => [
EnsureLoginIsNotThrottled::class,
RedirectIfTwoFactorAuthenticatable::class,
AttemptToAuthenticate::class,
PrepareAuthenticatedSession::class,
],
],
];
デフォルトでは2要素認証、通常のパスワード認証、認証時のセッション再生成が実行されるようになっている。このデフォルト値自体はfortify.pipelines.login
には設定されていないので注意が必要だ。
ドキュメントにあるように、Fortify::authenticateUsing()
を使って設定することも可能だ。
参考:
認証 1.0 Laravel Jetstreamの"認証プロセスのカスタマイズ"以下。
おまけ: auth:sanctum
について
最後に、Jetstreamを読み解いていく中できっと気になるauth:sanctum
について簡単に説明する。
artisan jetstream:install
すると、routes/web.php
にauth:sanctum
ミドルウェア(正確にはsanctum
ガードを使うよう指示されたauth
ミドルウェア)を使うルートが登録される。
このミドルウェアはなんだろうか。Sanctumは最初に書いたようにAPI認証用の機能だが、ここで登録されているルートはダッシュボードのもので、普通にセッションベースで認証できている。
結論を言うと、sanctum
ガードは、sanctum.guard
設定で指定したガードか未指定であればweb
ガードが有効な場合はそちらを見るようになっている。そのためログインしない状態で、Bearerトークンで認証するような場合以外は'auth'
とパラメータなしで指定する場合と変わらない。つまりダッシュボードでsanctum
ガードを使う必要はおそらくない。
sanctum
ガードは\Laravel\Sanctum\Guard
で実装されている。
最後に
Jetstreamによって導入される機能の変更方法を見てきた。ほとんどの機能は最終的には2つのサービスプロバイダ、JetstreamServiceProvider
とFortifyServiceProvider
(それぞれvendor
内の方)によって提供されている。そこだけ覚えておけば、自力で結論に辿り着くのも容易だ。
この記事のライセンス
この文書はCC BY(クリエイティブ・コモンズ表示4.0国際ライセンス)で公開する。
この文書内のサンプルコードのうち、はMITライセンスで公開する。