← Back to blog

bind vs extend in Laravel Service Container

| Laravel

This article was published over 2 years ago. Some information may be outdated.

According to Laravel's documentation, the extend method allows the modification of resolved services.

But what is the benefit of extending a class? And what is the difference between bind and extend?

Binding

The bind method registers a class into the service container.

Here is a real example from one of my projects:

use Google\Cloud\Translate\V2\TranslateClient;

class AppServiceProvider
{
    public function register()
    {
        $this->app->singleton(TranslateClient::class, function() {
            return new TranslateClient([
                'keyFilePath' => storage_path('security/google-vision-credentials.json'),
            ]);
        });
    }
}

The TranslateClient is ready to be injected without any additional configuration, since the config key keyFilePath was already set:

class Translate
{
    public function __construct(private TranslateClient $translateClient)
    {
    }

    // ...
}

That is the power of dependency injection.

But what if you want to add a new method to the TranslateClient? Something like isTranslatable which detects whether or not the given string is translatable.

You can do that by extending the TranslateClient and adding the isTranslatable method as follows:

use Google\Cloud\Translate\V2\TranslateClient;

class MyTranslateClient extends TranslateClient
{
    public function isTranslatable(string $string): bool
    {
        // ...
    }
}
$this->app->singleton(TranslateClient::class, function() {
    return new MyTranslateClient([
        'keyFilePath' => storage_path('security/google-vision-credentials.json'),
    ]);
});

That works perfectly.

Now try adding an isAdmin method to the Illuminate\Auth\AuthManager. This method checks the is_admin column on the users table; if it is set to 1, the user is an admin; otherwise, she is not.

As its name implies, the AuthManager has access to all the authentication features, such as login, logout, loginUsindId, user, etc.

You can access the AuthManager in different ways:

// auth() helper
auth()->user();

// Illuminate\Support\Facades\Auth facade
// Auth::user();

// Dependency injection
public function __construct(private AuthManager $authManager)
{
    // ...
}

Now add the isAdmin method:

namespace App\Support;

use Illuminate\Auth\AuthManager;
use Illuminate\Foundation\Application;

class MyAuthManager extends AuthManager
{
    public function __construct(private AuthManager $auth, Application $app)
    {
        parent::__construct($app);
    }

    public function isAdmin(): bool
    {
        return $this->auth->check() && $this->auth->user()->is_admin === 1;
    }
}

Bind MyAuthManager into the service container:

// AppServiceProvider
use Illuminate\Auth\AuthManager;
use Illuminate\Foundation\Application;

public function register()
{
    $this->app->bind(AuthManager::class, function(AuthManager $auth, Application $app) {
        return new MyAuthManager($auth, $app);
    });
}
// BadMethodCallException
// Method Illuminate\Auth\SessionGuard::isAdmin does not exist.

Auth::isAdmin();

That did not work, because the bind method cannot override or modify the built-in or third-party services.

Extending

Use the extend method whenever you want to modify a built-in or third-party service, for example the Illuminate\Auth\AuthManager:

// AppServiceProvider
use Illuminate\Auth\AuthManager;
use Illuminate\Foundation\Application;
public function register()
{
    $this->app->extend(AuthManager::class, function(AuthManager $auth, Application $app) {
        return new MyAuthManager($auth, $app);
    });
}

It should be working now:

Auth::isAdmin();

Summary

  • bind registers new services -- use it to register your own classes or override your own bindings in the service container.
  • extend modifies existing services -- use it when you need to override or decorate built-in or third-party services that are already registered.
  • Extending the AuthManager requires extend, not bind -- Laravel's internal services are resolved differently, and bind cannot replace them.
Share