bind vs extend in Laravel Service Container
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
?
Let’s dive into that.
Binding
The bind
method used to register a class into the service container.
Let’s see 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’s the power of dependency injection.
But what if I want to add a new method into the TranslateClient
? Something like isTranslatable
which detects whether or not the given string is translatable.
I can easily do that by extending the TranslateClient
and start 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 worked perfectly.
Let’s try to add isAdmin
method in the Illuminate\Auth\AuthManager
. This method checks the is_admin
column on the users
table; if it sets to 1
, the user is an admin; otherwise, she’s 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)
{
// ...
}
Ok. Let’s 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;
}
}
Let’s 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 didn’t work, because the bind
method can’t override/modify the builtin/3rd party services.
Extending
Use the extend
method whenever you want to modify a built-in/3rd 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();
I hope you enjoyed reading this post; keep an eye on the next upcoming ones.