Back to Course |
Design Patterns in Laravel 11

Singleton: in Auth, Mail, Cache

In some cases, Laravel needs to return the same object instance every time it is requested. For this, it uses the Singleton pattern in multiple places.


Authentication

In the case of authentication, you want to avoid fetching the user from the database or any other source every time you need to access the authenticated user.

Instead, you create a Singleton instance and store the authenticated user in it:

Illuminate/Auth/AuthServiceProvider.php

protected function registerAuthenticator()
{
$this->app->singleton('auth', fn ($app) => new AuthManager($app));
 
$this->app->singleton('auth.driver', fn ($app) => $app['auth']->guard());
}

This registers the auth (Auth Facade) and auth.driver services as singletons. Inside, it creates new instances of AuthManager and Guard, respectively.

Inside the AuthManager class, it will create a User resolver that will be used to resolve the authenticated user:

Illuminate/Auth/AuthManager.php

/**
* Create a new Auth manager instance.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function __construct($app)
{
$this->app = $app;
 
$this->userResolver = fn ($guard = null) => $this->guard($guard)->user();
}

And this then allows us to retrieve the authenticated user from the auth Singleton:

Auth::user()
auth()->user()

Which will always return the same instance of the authenticated user, even if the data in the database changes.

This allows us to be sure that the user has the correct information for that specific request.

Note: Singletons are per request, so they are not shared between requests. Reloading the page will create a new instance of the Singleton.


Mail

Another example we can find is the mailer singleton (Mail facade):

Illuminate/Mail/MailServiceProvider.php

/**
* Register the Illuminate mailer instance.
*
* @return void
*/
protected function registerIlluminateMailer()
{
$this->app->singleton('mail.manager', function ($app) {
return new MailManager($app);
});
 
$this->app->bind('mailer', function ($app) {
return $app->make('mail.manager')->mailer();
});
}

This registers the mail.manager and mailer services as singletons. And if you look at the mailer Singleton creation, we can see that it calls a function:

Illuminate/Mail/MailManager.php

/**
* Get a mailer instance by name.
*
* @param string|null $name
* @return \Illuminate\Contracts\Mail\Mailer
*/
public function mailer($name = null)
{
$name = $name ?: $this->getDefaultDriver();
 
return $this->mailers[$name] = $this->get($name);
}

This will create a new instance of the configured mailer and store it as a Singleton for later usage. This saves us from creating a new instance of the mailer whenever we need to send an email.


Cache

Then we can look at another typical Singleton - the cache:

Illuminate/Cache/CacheServiceProvider.php

/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('cache', function ($app) {
return new CacheManager($app);
});
 
$this->app->singleton('cache.store', function ($app) {
return $app['cache']->driver();
});
 
// ...
}

This registers the cache and cache.store services as singletons. And if you look at the cache.store Singleton creation, we can see that it calls a function:

Illuminate/Cache/CacheManager.php

/**
* Get a cache store instance by name, wrapped in a repository.
*
* @param string|null $name
* @return \Illuminate\Contracts\Cache\Repository
*/
public function store($name = null)
{
$name = $name ?: $this->getDefaultDriver();
 
return $this->stores[$name] ??= $this->resolve($name);
}

All the cache configuration is now stored in a Singleton instance, and we can access it by calling:

Cache::put()
Cache::get()

How to Retrieve Singleton Instance

To retrieve a Singleton instance, you can use the app() helper function or the resolve() method:

app('auth')
app('mailer')
app('cache')

But at the same time, Laravel uses Facades to access these Singletons:

Auth::user()
Mail::send()
Cache::get()

And this happens behind the scenes with a clever Facade implementation. Most of the time, they look like this:

Illuminate/Auth/AuthManager.php

class Cache extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'cache';
}
}

Once called, Laravel knows you want to access the cache Singleton and will return the instance of the CacheManager class.

If you are interested in how this technically works, you can check one of stackoverflow questions or dive into the Laravel source code!