19

I am using Laravel-5.0's default Authentication Middleware, but I changed the signature of the handle function to have:

public function handle($request, Closure $next, AuthClientInterface $authClient)

I also registered AuthClientInterface in a Service Provider with:

public function register()
{
    $this->app->bind('App\Services\Contracts\AuthClientInterface', function()
    {
        return new AuthClient(
            env('AUTH_SERVER_URL'),
            env('AUTH_SESSION_URL'),
            env('AUTH_CLIENT_ID')
        );
    });
}

However, despite this, I am see the following error:

Argument 3 passed to HelioQuote\Http\Middleware\Authenticate::handle() 
must be an instance of 
HelioQuote\Services\Contracts\HelioAuthClientInterface, none given, 
called in C:\MyApp\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php on line 125 and defined...

Can anyone see what I am doing wrong?

EDIT: I did get it working by passing the HelioAuthClientInterface into the constructor of the middleware. However I thought the IoC container would also inject the dependency to methods in addition to the constructor.

ajon
  • 7,868
  • 11
  • 48
  • 86

4 Answers4

46

You cannot do dependency injection at handle method in a Request directly, do that in a constructor.

Middleware is invoked by call_user_func, so any injection here will not be work.

<?php

namespace App\Http\Middleware;

use Closure;
use App\Foo\Bar\AuthClientInterface; # Change this package name

class FooMiddleware
{
  protected $authClient;

  public function __construct(AuthClientInterface $authClient)
  {
    $this->authClient = $authClient;
  }

  public function handle(Request $request, Closure $next)
  {
    // do what you want here through $this->authClient
  }
}
JohnHoulderUK
  • 639
  • 2
  • 8
  • 27
Francis.TM
  • 1,761
  • 1
  • 18
  • 19
5

You are not allowed to change the method signature here. Simply you may use something like this:

public function handle($request, Closure $next) {

    // Get the bound object to this interface from Service Provider
    $authClient = app('App\Services\Contracts\AuthClientInterface');

    // Now you can use the $authClient
}

Also, you may use a __construct method to achieve that, check the answer given by - Francis.TM.

The Alpha
  • 143,660
  • 29
  • 287
  • 307
4

Laravel's IoC only handles constructor method injection for all objects by default. The IoC will only inject dependencies into functions/methods handled by the router. The can be a closure used to handle a route, or more commonly, Controller methods used to handle routes.

The IoC does not do method dependency injection for any other object by default. You can call methods yourself through the IoC and have it resolve dependencies, but the framework only does this itself for route handlers. You can take a look at this question/answer for a little more information on using method dependency injection outside of a controller: can I use method dependency injection outside of a controller?.

Handling this by injecting your dependency through your constructor is the correct way to go, if you want to continue to use dependency injection.

Community
  • 1
  • 1
patricus
  • 59,488
  • 15
  • 143
  • 145
0

The accepted answer doesn't work anymore since Laravel 5.3.4. See complain on GitHub and upgrade guide which provide an alternative.

For example, a snippet from what I implemented to solve the current problem:

<?php

namespace App\Http\Middleware;

use Closure;
use App\Models\Website;
use \Illuminate\Http\Request;

class CheckPropertyOfWebsite
{

    protected $website_owner_id;

    public function __construct(Request $request)
    {
        $this->website_owner_id = Website::findOrFail($request->website)->first()->user_id;
    }

    public function handle($request, Closure $next)
    {
        if ($request->user()->id !== $this->website_owner_id) {
            return response(null, 500);
        }
        return $next($request);
    }
}