Laravel Service Container: What Beginners Need to Know

Service Container is probably one of the most confusing topics for beginners in Laravel. The official docs explain it well but with a lot of "theoretical words". Let's get practical and I'll show the core practical examples you need to understand.

First thing: in most cases, you will never need to work directly with Service Container. For beginners, it's enough to understand that Service Container is a Laravel internal mechanism that allows us to do certain class injections "auto-magically", that's it.

In other words, you kinda need to forget about "service container" and learn the practical examples of its usage, instead. So, let's get to them.


Simple Example: Form Request Class

Let me show you that you probably have already used the Service Container, without even realizing it. Take a look at this example:

// UserController.php:
public function store(StoreUserRequest $request) {
User::create($request->validated());
// ...
}

See that StoreUserRequest as a type-hint in the method? It's a typical Form Request class. Notice that we don't initialize the instance of that class? Laravel does this for us, if we type-hint the class.

In other words, we don't need to do this manually inside the method:

$request = new StoreUserRequest();

So this is done by Service Container, it "resolves" the instance of that class, and auto-creates the object for us.


Another Typical Example: Controller Method Injection

We often want to use external classes in our Controllers. In my course about Laravel project structure I've discussed various classes to be used: Services, Actions, etc. Let's take Services as an example.

Question: how to initialize a Service class, to use it inside our Controller?

The most straightforward way:

// UserController.php:
public function store(StoreUserRequest $request) {
$userService = new UserService();
$userService->create($request->validated());
}

But what if I told you that you don't need to initialize the service with new UserService()? And maybe you would guess how exactly Service Container could help us?

The answer is this:

public function store(StoreUserRequest $request, UserService $userService) {
$userService->create($request->validated());
}

Yes, we just add the class that we want to use inside the method, with type-hinting it, and Laravel will automatically create its variable to be used inside the method.

And you can do that in any method in your Controllers.


Does It Work Only For Methods? What About Constructors?

I'm glad you asked. Quite often, you need to use the class in multiple methods of the Controller.

So, inconvenient way:

public function store(StoreUserRequest $request, UserService $userService) {
$userService->create($request->validated());
}
 
public function update(User $user, UpdateUserRequest $request, UserService $userService) {
$userService->update($user, $request->validated());
}

Can we initialize the UserService for the whole Controller? Sure!

public function __construct(public UserService $userService)
{
}
 
public function store(StoreUserRequest $request) {
$this->userService->create($request->validated());
}
 
public function update(User $user, UpdateUserRequest $request) {
$this->userService->update($user, $request->validated());
}

Notice: we're using PHP 8 constructor property promotion here, that's why constructor is empty.


Does It Work Only In Controllers?

No. According to the official documentation:

"You may type-hint the dependency in the constructor of a class that is resolved by the container, including controllers, event listeners, middleware, and more. Additionally, you may type-hint dependencies in the handle method of queued jobs."

Not sure exactly what that "and more" means, but you can try in any other Laravel class and see if it works.

Now, if you have your own custom PHP class and try to use this "trick", it would work only with Constructor, but if you try to type-hint a different class within any method, it wouldn't work because Laravel wouldn't know about it.

For example, if you have a PHP Service class:

class UserService {
 
public function store(SomeOtherService $someService) {
// ...
}
 
}

That wouldn't work.


Advanced Level: Manual Binding and More

Basically, the above information is enough for beginners to know, on a practical level. You don't necessarily need to know how Service Container works under the hood, or how to bind/resolve the dependant classes in more complex cases.

But if you do want to find out about that... you can, of course, read the full page of the official documentation.

We also have a full course with examples of design patterns applied based on Service Container: Design Patterns in Laravel

avatar

Why using SomeOtherService in UserService woudn't work? It will work if service container will be able to resolve SomeOtherService's dependencies or I'm missing something?

avatar

Then you need to define the binding of SomeOtherService in your ServiceProvider. By default, it wouldn't work, you can try it yourself.

avatar

Amm, ok but it works.. So maybe I'm missing something, there what I'm doing:

  1. I have regular Laravel controller with index methond. In it's construct methond I'm injecting \App\Services\UserService
  2. In \App\Services\UserService construct method I'm injecting \App\Services\SomeOtherService (which has only one public method and does not extend anything)
  3. In controller's index method I can access any public method from \App\Services\UserService

No exceptions in logs or on the page which is handled by index method in controller. I do not configure anything in ServiceProvider or somewhere else. Using latest fresh Laravel 10.

What exception I'm suppose to get?

avatar

Hmmm, would need to test it myself, maybe Laravel got better at auto-recognizing ANY class anywhere? Do you have that code somewhere on GitHub so I could play around and comment more?

avatar

Sure, really excited about it!

There is a link to the repo https://github.com/underser/laravel10Sandbox/blob/main/app/Http/Controllers/UserController.php

avatar

Hmm, that's really interesting, I have to admit you're right here! I meant another example by "not working", but written a bad example in the article, will fix now.

Apparently, it DOES work through the Constructor.

But it doesn't work if you type-hint in any method. What I meant was that in Controllers, ANY method would accept type-hinted service, but in the Service, if you type-hint in any method it wouldn't work.

This works:

class UserService
{
    public function __construct(private SomeOtherService $someOtherService)
    {
    }

    public function getUsersList(): array
    {
        return [
            'John Doe' => $this->someOtherService->getUserPassword(),
            'John Lewis' => $this->someOtherService->getUserPassword(),
        ];
    }
}

This doesn't work:

class UserService
{
    public function getUsersList(SomeOtherService $someOtherService): array
    {
        return [
            'John Doe' => $someOtherService->getUserPassword(),
            'John Lewis' => $someOtherService->getUserPassword(),
        ];
    }
}

So, in Controller, you can do store(UserService $userService), but in UserService you can't do store(SomeOtherService $someOtherService).

Sorry for misleading, and glad to clarify this detail myself with your help.

avatar

Thanks for helping with that! Happy to see that the article now is more accurate :)

avatar
Eugene van der Merwe

I think something that isn't discussed enough is "why" the whole service container idea is to prevelant in Laravel.

Apart from the obvious - you don't need to do so much instantiation - it also makes coding easier and more fluent. So code readability is a big side benefit of IoC.

Like our articles?

Become a Premium Member for $129/year or $29/month
What else you will get:
  • 68 courses (1188 lessons, total 43 h 18 min)
  • 90 long-form tutorials (one new every week)
  • access to project repositories
  • access to private Discord

Recent New Courses