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
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?
Then you need to define the binding of SomeOtherService in your ServiceProvider. By default, it wouldn't work, you can try it yourself.
Amm, ok but it works.. So maybe I'm missing something, there what I'm doing:
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?
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?
Sure, really excited about it!
There is a link to the repo https://github.com/underser/laravel10Sandbox/blob/main/app/Http/Controllers/UserController.php
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:
This doesn't work:
So, in Controller, you can do
store(UserService $userService)
, but in UserService you can't dostore(SomeOtherService $someOtherService)
.Sorry for misleading, and glad to clarify this detail myself with your help.
Thanks for helping with that! Happy to see that the article now is more accurate :)
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.