laravellaravel-service-container

Where to switch to other service depending on logged user field?


In laravel 9 app I try to switch to different service

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        if ($this->app->environment('local')) {
            $this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class);
            $this->app->register(TelescopeServiceProvider::class);

            $this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
        }
        
        // Error pointing at this line
        $loggedUser  = Auth::user();


        if( $loggedUser->membership_mark === 'G') { // G=>Gold Membership
            $this->app->bind('App\Interfaces\PageMembershipMarkStrategyInterface',
                'App\Implementations\PageGoldMemberStrategy');

        }
    }

But I got error :

 Target class [hash] does not exist. in...

Looks like register method is not proper place for app->bind when I need to get logged user. But where have I to make this binding? In custom service provider? Im middleware ?

MODIFIED BLOCK : I create a middleware with command :

php artisan make:middleware SetPageMembershipMarkService

and in app/Http/Kernel.php I added it :

protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,

    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,

    // I added this line : 
    \App\Http\Middleware\SetPageMembershipMarkService::class,

    'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,

But I got error :

Illuminate\Contracts\Container\BindingResolutionException: Target [App\Interfaces\PageMembershipMarkStrategyInterface] is not instantiable while building [App\Http\Controllers\InformOnPageInStockController]....

I am not sure in which place of Kernel.php I have to add ref to PageMembershipMarkStrategyInterface ? checking log it appears that my SetPageMembershipMarkService is not triggered before error...

MODIFIED BLOCK 2:

In file app/Http/Kernel.php I added my Middleware reference:

protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,

    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,

    // I set it AFTER auth
    'setPageMembership' =>\App\Http\Middleware\SetPageMembershipMarkService::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' => \Illuminat`enter code here`e\Auth\Middleware\EnsureEmailIsVerified::class,
];

abd in routes/api.php :

Route::middleware('auth:api')->prefix('v1') /*->name('api.v1')*/ ->group(function () {
    Route::get('/user', function (Request $request) {
        return $request->user();
    });

    ...
    
    Route::middleware(['setPageMembership'])->group(function () {
        Route::resource('/inform-on-page-in-stock', InformOnPageInStockController::class)->except(['edit', 'add']);
   });

and I still got error :

Illuminate\Contracts\Container\BindingResolutionException: Target [App\Interfaces\InformOnPageInStockStrategyInterface] is not instantiable while building [App\Http\Controllers\InformOnPageInStockController]. 
  1. Checking logs I do not find log message I set in SetPageMembershipMarkService middleware, so looks like it does not set before my controllers... What is wrong ?

  2. You mentioned using of RouteServiceProvider. Which is correct syntax ?

File app/Http/Middleware/SetPageMembershipMarkService.php has lines :

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Auth;

class SetPageMembershipMarkService
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse)  $next
     * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
     */
    public function handle(Request $request, Closure $next)
    {

        $loggedUser  = Auth::user();
        \Log::info(varDump($loggedUser, ' -1 SetPageMembershipMarkService $loggedUser::'));

        $membershipMark = $loggedUser->membership_mark;

        if( $membershipMark === 'S') { // G=>Silver Membership
            app()->bind('App\Interfaces\InformOnPageInStockStrategyInterface',
                'App\Implementations\HasNoInformOnPageInStockStrategy');
        }

        if( $membershipMark === 'G') { // G=>Gold Membership
            app()->bind('App\Interfaces\InformOnPageInStockStrategyInterface',
                'App\Implementations\InformOnPageInStockGoldMembersStrategy');
        }

        return $next($request);
    }
}

But in log file I do not see any log message I have in this Middleware...

Thanks!


Solution

  • You will not be able to get Auth:user() data in Service Provder. Because service provider runs before Laravel sets the Auth data, you'll have to use some hacky methods to handle it.

    I think better ways to handle data related to auth would be on middleware and not in Service Provider.

    Additional explanations can be found on this questions answer Laravel - How to get current user in AppServiceProvider

    EDIT for modifed block

    Your middleware registration is wrong.

    $routeMiddleware this array is used to register a custom middleware which you gonna use and define manually

    For example:

        protected $routeMiddleware = [
        .....
        // I added this line : 
        'setPageMembership' => \App\Http\Middleware\SetPageMembershipMarkService::class,
    

    Then you gonna use this middleware in your route groups like:

    Route::middleware(['auth', 'setPageMembership'])->group(function () {
        Route::get('test', [TestController::class, 'test']);
    });
    

    If you want the middleware to run for all the routes it is better to add it in the $middleware array or if you want in the certain route group (web/api) then in the $middlewareGroups individual web and api array.

    Adding the middleware in RouteServiceProvder also works if you want every route to have the middleware.

    However, for your use case, authentication is required before binding. So, you will need auth middleware to fire first before you perform those bind actions. So adding the middleware to your route file like I showed above will be enough for your use case.