Search code examples
phplaravellaravel-5middlewarelaravel-middleware

Laravel - Midlleware called after controller construct


EDIT: Better example

I have trouble with checkauth controller that are called after SectionsController constructor - from this is extended ReportController and I don't know why it works like this. I try add middleware at the start of SectionController costruct like $this->middleware('checkauth'); but not working too.

Middleware

<?php

namespace App\Http\Middleware;

use Closure;
use Auth;


class CheckAuth
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if (!Auth::check())
        {
            return redirect('/admin/login')->withErrors(['message' => 'You need to login before proceeding']);
        }

        return $next($request);
    }
}

SectionsController

<?php

namespace App\Http\Controllers\RMReport;

use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Facades\View;
use App\Http\Controllers\Controller;
use Illuminate\Http\UploadedFile;



class SectionsController extends BaseController
{
  use AuthorizesRequests, DispatchesJobs, ValidatesRequests;

   protected $user;

    public function __construct(){
      $this->middleware('checkauth');

      dump("SECTIONSCONTROLLER: ");
      dump(\Auth::check());

     //PROBLEM HERE - \Auth::Check is false , \Auth::user is null
     $this->user = \Auth::user();

     if( !$this->user->can('something') ) return redirect()->route('someroute');
}
}

ReportController

<?php

namespace App\Http\Controllers\RMReport\Form;

use App\Http\Controllers\RMReport\SectionsController;
use Illuminate\Http\Request;
use Illuminate\Support\Str;


class ReportController extends SectionsController
{

    public function form(){
      if( $this->user->status != 'Active'){
        return something;
      }
    return another something;
    }

}

Output from route:list

GET|HEAD | admin/remote-report/{id?} | rm.report | App\Http\Controllers\RMReport\Form\ReportController@form | web,checkauth,checkownerRMreport |  
POST | admin/remote-report/{id?} | rm.report | App\Http\Controllers\RMReport\Form\ReportController@store | web,checkauth,checkownerRMreport |

Output on call GET

"SECTIONSCONTROLLER: "

false

"CHECKAUTH:"

true

Solution

  • Understanding the Laravel lifecycle and the object lifecycle is important. When creating an new object the constructor will be called before anything else. When the action of new SectionsController() happens, it will trigger the constructor immediately, since the constructor calls the middleware, it has no way of knowing that it should run before.

    This means that in the request lifecycle, the controller constructor has to be before the middlewares. This luckily has been solved and if you use a callback for middleware dependent logic in the constructor, you will avoid this problem.

    $this->middleware(function ($request, $next) {
        dump("SECTIONSCONTROLLER: ");
        dump(\Auth::check());
    });
    

    EDIT Instead of doing all that you are doing in the constructor which is wrong, you can utilize the Laravel middleware for policies. This require you to use the Authorize middleware, which by standard is enabled.

    $this->middleware('can:something');