Laravel マルチログイン Lifecycle

マルチログイン関連ファイル

model、controller、view、route

providers/RouteServiceProvider.php

ログイン後のURL

config/auth.php

Guard設定 ログイン方法、どのテーブルを使うか

middleware/Authenticate.php

未認証時のリダイレクト処理

middleware/RedirectIfAuthenticated.php

ログイン済みユーザーのリダイレクト

ブラウザに表示されるまでの流れ

laravelで作成されてページへアクセスするとまず、index.phpにすべて集まる
WEBサーバがpublic/index.phpにリダイレクト

→ページが読み込まれたタイミングでサービスコンテナ、サービスプロバイダ、ミドルウェア、環境設定などを読み込んでリクエスト、レスポンスが帰ってくる。

  1. autoload読み込み→requireなしで別ファイルのクラスを利用可能
  2. Applicationインスタンス作成(サービスコンテナ)
  3. HttpKernelインスタンス作成
  4. Requestインスタンス作成
  5. HttpKernelがリクエストを処理してResponse取得
  6. レスポンス送信
  7. terminate()
1. autoload読み込み、2. Applicationインスタンス作成(サービスコンテナ)

autoload読み込み→bootstrap/app.php読み込み→サービスコンテナを読み込んでる

<?php
/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| this application. We just need to utilize it! We'll simply require it
| into the script here so we don't need to manually load our classes.
| requireなしで別ファイルクラスを利用可能→namespaceやuse文が使える
*/

require __DIR__.'/../vendor/autoload.php';

/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request using
| the application's HTTP kernel. Then, we will send the response back
| to this client's browser, allowing them to enjoy our application.
|
*/

$app = require_once __DIR__.'/../bootstrap/app.php';

$kernel = $app->make(Kernel::class);

$response = $kernel->handle(
    $request = Request::capture()
)->send();

$kernel->terminate($request, $response);

Illuminate\Foundation\Applicationインスタンス化し、
$appへ代入したものがサービスコンテナ

  • NULL合体演算子
    式(expr1)??(expr2) expr1がnullならexpr2と評価され、それ以外の場合expr1と評価されます。
    pathがnullだったらdirそうでなければpathが実行

singletonはサービスコンテナに紐付ける仕組みで一度だけインスタンス化するメソッド。→カーネルクラスをサービスコンテナに登録する
→3. HttpKernelインスタンス作成

<?php

$app = new Illuminate\Foundation\Application(
    $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);

$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class

$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);
);
サービスコンテナを使ってみる

ルーティングの定義

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ComponentTestController;
use App\Http\Controllers\LifeCycleTestController;

Route::get('/', function () {
    //prefix設定
    return view('user.welcome');
});

Route::get('/dashboard', function () {
    //prefix設定
    return view('user.dashboard');
    //guard権限:を付与、→usersの権限を持ってたらダッシュボードへ
})->middleware(['auth:users'])->name('dashboard');

Route::get('/component-test1', [ComponentTestController::class, 'showComponent1']);
Route::get('/component-test2', [ComponentTestController::class, 'showComponent2']);
Route::get('/servicecontainer-test', [LifeCycleTestController::class, 'showServiceControllerTest']);
Route::get('/serviceprovider-test', [LifeCycleTestController::class, 'showServiceProviderTest']);
require __DIR__.'/auth.php';

コントローラ作成
php aritsan make:controller LifeCycleTestController

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class LifeCycleTestController extends Controller
{
    //
    public function showServiceProviderTest()
    {
        $encrypt = app()->make('encrypter'); 
        $password = $encrypt->encrypt('password');
        $sample = app()->make('serviceProviderTest');
        dd($sample, $password, $encrypt->decrypt($password));


    }
    public function showServiceControllerTest()
    {
        //bindでサービスコンテナ登録
        app()->bind('lifeCycleTest', function(){
            return 'ライフサイクルテスト';
        });
        //makeでサービスコンテナから取り出す
        $test = app()->make('lifeCycleTest');
        //サービスコンテナなしのパターン
        // $message = new Message();
        // $sample = new Sample($message);
        // $sample->run();
        //サービスコンテナapp()ありのパターン
        app()->bind('sample', Sample::class);
        $sample = app()->make('sample');
        $sample->run();
        dd($test, app());
    }
}

class Sample
{
    public $message;
    public function __construct(Message $message){
        $this->message = $message;
    }
    public function run(){
        $this->message->send();
    }

}

class Message
{
    public function send(){
        echo('メッセージ表示');
    }
}

bindでサービスコンテナに登録したあとにDDでみると71→72にふえていてlifeCycleTestが追加されてる。

http://localhost:8005/servicecontainer-testへアクセスするとlifeCycleTestを確認でき、サービスコンテナに登録できていることがわかる。


サービスコンテナから取り出す場合はmakeを使用する。

再び確認すると、ライフサイクルテストと表示されサービスコンテナから取り出しができていることがわかる。

サービスプロバイダ

register()メソッドでサービスコンテナにサービスを登録
boot()メソッドですべてのサービスを登録した後に実行したい処理を書く
サービスプロバイダの読み込み箇所→bootstrap/app.php
Illuminate\Foundation\Application サービスコンテナの元であるcontainerクラスを継承しこの中にbindやsingletonなどはいってる

<?php
class Application extends Container implements ApplicationContract, CachesConfiguration, CachesRoutes, HttpKernelInterface
{
    /**
     * Register all of the configured providers.
     *
     * @return void
     */
    public function registerConfiguredProviders()
    {
        $providers = Collection::make($this->make('config')->get('app.providers'))
                        ->partition(function ($provider) {
                            return strpos($provider, 'Illuminate\\') === 0;
                        });

        $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);

        (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
                    ->load($providers->collapse()->toArray());
    }

->get('app.providers'))config/app.phpのなかのprovidersを読み込んでいる

<?php
'providers' => [

        /*
         * Laravel Framework Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,
        Illuminate\Bus\BusServiceProvider::class,
        Illuminate\Cache\CacheServiceProvider::class,
        Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
        Illuminate\Cookie\CookieServiceProvider::class,
        Illuminate\Database\DatabaseServiceProvider::class,
        Illuminate\Encryption\EncryptionServiceProvider::class,
        Illuminate\Filesystem\FilesystemServiceProvider::class,
        Illuminate\Foundation\Providers\FoundationServiceProvider::class,
        Illuminate\Hashing\HashServiceProvider::class,
        Illuminate\Mail\MailServiceProvider::class,
        Illuminate\Notifications\NotificationServiceProvider::class,
        Illuminate\Pagination\PaginationServiceProvider::class,
        Illuminate\Pipeline\PipelineServiceProvider::class,
        Illuminate\Queue\QueueServiceProvider::class,
        Illuminate\Redis\RedisServiceProvider::class,
        Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
        Illuminate\Session\SessionServiceProvider::class,
        Illuminate\Translation\TranslationServiceProvider::class,
        Illuminate\Validation\ValidationServiceProvider::class,
        Illuminate\View\ViewServiceProvider::class,

ためしに暗号機能のサービスプロバイダIlluminate\Encryption\EncryptionServiceProvider::class,の中をみてくと、registerメソッド内にサービスコンテナに登録する処理が書かれてる
$this->app->singleton('encrypter',でencrypterという名前でサービスコンテナに登録している

<?php

namespace Illuminate\Encryption;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
use Laravel\SerializableClosure\SerializableClosure;
use Opis\Closure\SerializableClosure as OpisSerializableClosure;

class EncryptionServiceProvider extends ServiceProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->registerEncrypter();
        $this->registerOpisSecurityKey();
        $this->registerSerializableClosureSecurityKey();
    }

    /**
     * Register the encrypter.
     *
     * @return void
     */
    protected function registerEncrypter()
    {
        $this->app->singleton('encrypter', function ($app) {
            $config = $app->make('config')->get('app');

            return new Encrypter($this->parseKey($config), $config['cipher']);
        });
    }
サービスプロバイダを使ってみる

サービスプロバイダのファイルを作成して、config/app.phpに書いてあげるとサービスコンテナの中に登録されて、 それをコントローラーからmakeでいつでも使えるようにする流れ

registerのほうにサービスコンテナに登録する処理を書く→bindで登録

$ php artisan make:provider SampleServiceProvider

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class SampleServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        app()->bind('serviceProviderTest', function(){
            return 'サービスプロバイダテスト';
        });
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}

SampleServiceProviderを起動するときに読み込むためにapp/config.phpprovidersに記述する

<?php
 /*
  * Application Service Providers...
  */
    App\Providers\AppServiceProvider::class,
    App\Providers\AuthServiceProvider::class,
    // App\Providers\BroadcastServiceProvider::class,
    App\Providers\EventServiceProvider::class,
    App\Providers\RouteServiceProvider::class,
    App\Providers\SampleServiceProvider::class,

サービスコンテナに登録したものを実際に表示する。
Controllers/LifeCycleTestController.phpでのapp()->make('serviceProviderTest');でサービスコンテナから取り出しDDで表示させる

<?php
class LifeCycleTestController extends Controller
{
    //
    public function showServiceProviderTest()
    {
        $encrypt = app()->make('encrypter'); 
        $password = $encrypt->encrypt('password');
        $sample = app()->make('serviceProviderTest');
        dd($sample, $password, $encrypt->decrypt($password));


    }

3. HttpKernelインスタンス作成

→Karnelクラスをみていくと$middleware,$middlewareGroup,$routeMiddlewareの3つが設定されてる
$routeMiddlewareにアクセスした際に認証されてるか確認する仕組み→AUTHが設定されてる

マルチログインだと$routeMiddleware
'auth' => \App\Http\Middleware\Authenticate::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,辺りが絡んでくる。

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array<int, class-string|string>
     */
    protected $middleware = [
        // \App\Http\Middleware\TrustHosts::class,
        \App\Http\Middleware\TrustProxies::class,
        \Fruitcake\Cors\HandleCors::class,
        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ];

    /**
     * The application's route middleware groups.
     *
     * @var array<string, array<int, class-string|string>>
     */
    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,
        ],

        'api' => [
            // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array<string, class-string|string>
     */
    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];
}

use Illuminate\Foundation\Http\Kernel as HttpKernel;Illuminate\Foundation\Http\Kernelを見ていく。
HTTPカーネルhandleメソッドはリクエストを受け取ってレスポンスを帰す→カーネルをアプリケーション全体で表す大きなブラックボックスと考える
HTTPリクエストを取り込み、HTTPレスポンスを返す。

4.Requestインスタンス作成、5. HttpKernelがリクエストを処理してResponse取得
<?php
 /**
     * Handle an incoming HTTP request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function handle($request)
    {
        try {
            $request->enableHttpMethodParameterOverride();

            $response = $this->sendRequestThroughRouter($request);
        } catch (Throwable $e) {
            $this->reportException($e);

            $response = $this->renderException($request, $e);
        }

        $this->app['events']->dispatch(
            new RequestHandled($request, $response)
        );

        return $response;
    }

sendRequestThroughRouterメソッドへジャンプ

<?php
/**
     * Send the given request through the middleware / router.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    protected function sendRequestThroughRouter($request)
    {
        $this->app->instance('request', $request);

        Facade::clearResolvedInstance('request');

        $this->bootstrap();

        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }

$this->app->instance('request', $request);でサービスコンテナを経由して必要なクラスをもってきて、returnでRouterに渡す。

dispatchToRouterメソッドへジャンプ

6.レスポンス送信

findRouteでルーティングを探してきてコントローラへ渡す

<?php
/**
     * Dispatch the request to a route and return the response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function dispatchToRoute(Request $request)
    {
        return $this->runRoute($request, $this->findRoute($request));
    }
7. terminate()

再びindex.phpに戻ると
サービスコンテナに登録したKernelをmakeでとってきて変数$Kernelにいれて、 $responseとして$kernelのなかの$handleメソッドでリクエストを設定し、レスポンスが帰ってきたらterminateでどちらとも削除する

<?php

require __DIR__.'/../vendor/autoload.php';

$app = require_once __DIR__.'/../bootstrap/app.php';

$kernel = $app->make(Kernel::class);

$response = $kernel->handle(
    $request = Request::capture()
)->send();

$kernel->terminate($request, $response);