Get "PHP 8 in a Nuthshell" (Soon includes PHP 8.4)
Amit Merchant

Amit Merchant

A blog on PHP, JavaScript, and more

Container Attributes in Laravel

When you’re working with service classes in Laravel that you would inject into your controllers or anywhere else, you might need to pass some configuration values to them.

Let’s take the following example.

use App\Actions\DemoService;

Route::get('/demo-service', function (DemoService $demoService) {
    // do something with $demoService
});

As you can tell, we have an App\Actions\DemoService class that we’re injecting into a route. Now, let’s take a look at the DemoService class.

namespace App\Actions;

class DemoService
{
    public function __construct(
        string $appName
    )
    {
        dd($appName);
    }
}

As you can tell, the DemoService class has a constructor that takes a single parameter called $appName. And we’re dumping the value of this parameter in the constructor.

Now, if visit the /demo-service route, the app will throw the following exception.

Binding resolution exception

That exception is thrown because we haven’t provided a value for the $appName parameter in the DemoService class while injecting it into the route and Laravel can’t resolve it on its own.

Resolving the value of dependencies

To get around this, you would need to use a boilerplate code like the following in the boot method of your app’s AppServiceProvider class.

namespace App\Providers;

use App\Actions\DemoService;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->app->when(DemoService::class)
            ->needs('$appName')
            ->give(function () {
                return config('app.name');
            });
    }
}

This code will tell Laravel to resolve the $appName parameter in the DemoService class constructor using the config helper function.

Now, if you visit the /demo-service route, the app will dump the value of the app.name configuration value which is set in the config/app.php file.

Container Attributes

In the recent release of Laravel, they introduced a new feature called Container Attributes which allows you to resolve the dependencies of a class using a simple PHP attribute in the constructor itself reducing the boilerplate code that you need to write in the AppServiceProvider class.

The Config attribute

So, if we want to rewrite the previous example using Container Attributes, we can do so like so.

namespace App\Actions;

use Illuminate\Container\Attributes\Config;

class DemoService
{
    public function __construct(
        #[Config('app.name')]
        string $appName
    )
    {
        dd($appName);
    }
}

As you can tell, we now have a new attribute called Illuminate\Container\Attributes\Config which we’re using to resolve the $appName parameter in the DemoService class constructor.

We don’t need to do anything in the AppServiceProvider class anymore. And everything will work as before.

The CurrentUser attribute

The Illuminate\Container\Attributes\Config attribute is only one of the many attributes that you can use to resolve dependencies in your classes.

There are other attributes such as Illuminate\Container\Attributes\CurrentUser that can be used to resolve the current user of your application.

namespace App\Actions;

use Illuminate\Container\Attributes\CurrentUser;
use App\Models\User;

class DemoService
{
    public function __construct(
        #[CurrentUser]
        User $currentUser
    )
    {
        dd($currentUser->toArray());
        /*
            [
                'id' => 1,
                'name' => 'John Doe',
                'email' => '[email protected]',
                'email_verified_at' => null,
                'created_at' => '2021-09-08T13:22:34.000000Z',
            ]
        */
    }
}

The DB attribute

The Illuminate\Container\Attributes\DB attribute can be used to resolve the database connection of your application.

namespace App\Actions;

use Illuminate\Container\Attributes\DB;
use Illuminate\Database\Connection;

class DemoService
{
    public function __construct(
        #[DB('mysql')]
        Connection $db
    )
    {
        dd($db->getDatabaseName());
        /*
            'database'
        */
    }
}

Other attributes

Here is the list of the rest of the available attributes that you can use to resolve dependencies in your classes.

  • Illuminate\Container\Attributes\Auth
  • Illuminate\Container\Attributes\Cache
  • Illuminate\Container\Attributes\Log
  • Illuminate\Container\Attributes\Storage

Apart from these core Laravel attributes, you can also create your own custom container attributes by implementing the Illuminate\Contracts\Container\ContextualAttribute interface to make your code simpler and easier to read.

In closing

Container Attributes presents yet another real-world use case of the PHP attributes and how they can be used to write more fluent and expressive code.

If you’re interested in learning more about PHP attributes, you can check out the following resources.

Learn the fundamentals of PHP 8 (including 8.1, 8.2, and 8.3), the latest version of PHP, and how to use it today with my new book PHP 8 in a Nutshell. It's a no-fluff and easy-to-digest guide to the latest features and nitty-gritty details of PHP 8. So, if you're looking for a quick and easy way to PHP 8, this is the book for you.

Like this article?

Buy me a coffee

👋 Hi there! I'm Amit. I write articles about all things web development. You can become a sponsor on my blog to help me continue my writing journey and get your brand in front of thousands of eyes.

Comments?