phplaravelmiddlewarecontracts

Laravel middleware throws error that depedency needs to implement contract, while it does


Type error: Argument 3 passed to App\Http\Middleware\UserAuthMiddleware::handle() must implement interface App\Contracts\UserAuth, none given, called in C:\wamp64\www\laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php on line 148

I'm getting this error from inside a middleware, but when I'm use the given contract in my controllers, it works just fine. Does anyone have a clue what is happening?

Middleware file

namespace App\Http\Middleware;

use Closure;
use App\Contracts\UserAuth;

class UserAuthMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next, UserAuth $user)
    {
        return $next($request);
    }
}

UserAuth service file

<?php

namespace App\Services;
use App\UsersModel;

class UserAuth implements \App\Contracts\UserAuth
{
    public $username;
    public $password;
    public $perm_level = "admin";
    public $guest = true;

    public function load()
    {
        if (session()->has("user"))
        {
            $user = UserModel::where([
                "username" => session("user"),
                "password" => session("password")
            ])->first();
            $this->username = $user->username;
            $this->password = $user->password;
            $this->guest = false;
        }
    }

    public function isLogged()
    {
        return $this->guest;
    }
}

AppServiceProvider register

public function register()
{
    $this->app->singleton(\App\Contracts\UserAuth::class, \App\Services\UserAuth::class);
}

Routes

//REGISTRATION ROUTES
Route::get("/register", "User@register")->middleware("user_auth");
Route::post("/register", "User@save_user")->middleware("user_auth");

User controller with working contract

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\UsersModel;
use App\Contracts\UserAuth;

class User extends Controller
{
    public function register(Request $request, UserAuth $user)
    {
        return view("registration.form", ["request" => $request]);
    }

    public function login(Request $request)
    {
        $user = UsersModel::where([
            "username" => $request->username,
            "password" => $request->password
        ])->first();
        if ($user) 
        {
            session(["user" => $user->username, "password" => $user->password]);
            return back();
        }
        else return back()->with("login_attempt", "failed");
    }

    public function logout()
    {
        session()->forget("user", "password");
        return back();
    }

    public function save_user(Request $request)
    {
        $errors = [];

        //Data validation
        $input = $request->all();

        if ( in_array(null, $input) ) 
            $errors["empty_fields"] = true;

        if ( !preg_match("/[A-Za-z0-9 ]{3,16}/", $input["username"]) ) 
            $errors["invalid_username"] = true;

        if ( $input["password"] != $input["password_confirm"] ) 
            $errors["unmatching_passwords"] = true;

        if ( !preg_match("/[A-Za-z0-9\-\.]{3,16}@[A-Za-z0-9](\.[a-z0-9]){1,2}/", $input["email"]) ) 
            $errors["invalid_email"] = true;

        if ( UsersModel::where("username", $input["username"])->first() ) 
            $errors["username_taken"] = true;

        if (count($errors) > 0) return view("registration.form", ["err" => $errors, "request" => $request]);
        else return view("registration.save", ["request" => $request]);
    }
}

Solution

  • You are incorrectly trying to pass an ad-hoc paramter to the handle function in your middleware parameter.

    When you define this:

    public function handle($request, Closure $next, UserAuth $user)
    

    That means that the middleware is expecting a parameter to be passed in there, it will not come from DI Container, hence the $user variable is null, failing to pass the type-hint constraint.

    The parameters that are allowed here are only for "roles", you cannot pass something random and expect the DI container to resolve it.

    I would suggest you try something like this instead:

    public function handle($request, Closure $next)
    {
        if($request->user()->isLogged()) {
            return $next($request);
        } else {
            return redirect('login'); // or whatever route
        }
    }
    

    For this to work, you would need to define the isLogged function as part of a Trait, that you would add to you App\User model.

    Please see:

    https://laracasts.com/discuss/channels/laravel/pass-variable-from-route-to-middleware

    https://laravel.com/docs/5.4/middleware#middleware-parameters