Laravel Middleware and Dependency Injection

Posted on

TLDR; In Laravel, middleware is bound in a way that you may not access their constructor. The best way I have found is to bind whatever the input you want, to the DI container and add it a parameter to the middlewares __construct() method.

The Scenario

Disclaimer: This is not the way to do auth. I have created this scenario to closely resemble a situation I came across in real life.

I had a problem in which I needed to use an env var variable in a middleware. Ideally, I wanted to inject into its __construct method. As this would make it easier to test. However I was struggling to find a way to access the middlewares construct as it is called by the router. In the end the only way I found was to create a complex type, bind it to the DI, and add it as a parameter to the constuctor method.

passwordAnswer = $passwordAnswer;
		if ($this->value === "") {
			// throw error PASSWORD NOT SET!!
		}
	}
	public function validate($input): bool
	{
		return $input === $this->passwordAnswer;
	}
}

The above code reads an env var, and throws an error if it is not set.

passwordValidator = $passwordValidator
    }

    public function handle($request, Closure $next)
    {
        $inputPassword = $request->input('password');
        if (! $inputPassword) {
            return ErrorService::respondBadRequest();
        }

        if (! $this->passwordValidator->validate($inputPassword)) {
            return ErrorService::respondErrorValidation('auth failed');
        }
        
        return $next($request);
    }
}

This is the middleware itself, if the password param does not match what we have in the env var, we will deny the user access.

app->bind(PasswordValidator::class, function ($app) {
            return new PasswordValidator(getenv("AUTH_PASSWORD"));
        });
    }
}

This is the main part, essentially anything that you want injecting into a middlewares construct has to go through Laravels DI container. I hope this saves someone some time.