Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

shiny's avatar

Why use service providers? Any examples?

What is the point of writing service providers? What functionality do they provide? I don't get it and would like a concrete example so I can see the reason.

0 likes
33 replies
HRcc's avatar

Imagine you have created a class which requires multiple dependencies and in general you use it like this:

$foo = new Foo(new Bar(config('some_secret_key')), new Baz(new Moo(), new Boo()), new Woo('yolo', 5));

it's doable, but you wouldn't want to figure out these dependencies every time you try to instantiate this class. That's why you want to use service provider where in the register method you can define this class as:

$this->app->singleton('My\Awesome\Foo', function ($app) {
   return new Foo(new Bar(config('some_secret_key')), new Baz(new Moo(), new Boo()), new Woo('yolo', 5));
});

This way, if you need to use this class, you can just type hint it in the controller (container will figure it out) or ask for it manually like $foo = app(My\Awesome\Foo::class). Isn't that easier to use? ;)

More info in the docs

9 likes
shiny's avatar

Thanks for helping, but I don't get it. I don't see a connection between your example, the description in the docs link, which I have read three times. Your example is good for reducing the code base size, but why not just make a factory method within the Foo class, like this?


public static  function Factory
{
    return new Foo(new Bar(config('some_secret_key')), new Baz(new Moo(), new Boo()), new Woo('yolo', 5));
}
Using a Factory method reduces the number of files in the codebase and, IMHO, makes maintenance easier since the code is in one file, for the same class. Seems simpler to me. What do you think?
3 likes
HRcc's avatar

I wouldn't be concerned with the amount of files at all, however you can always put it into AppServiceProvider which is already present, if you want.

Sure, you can do something like that, but it doesn't feel simpler to me personally and I don't like the idea, that my Foo class knows about dependencies of Baz class. Why should it care?

You might want to read through the IOC container docs where you can find the benefits of using it instead of creating custom factories. (since you use service providers mostly to add your class to the IOC container or bind an interface to a specific implementation) If you decide to avoid using the IOC container, you would be missing one of the best parts of Laravel.

shiny's avatar

Thanks for the discussion HRcc. Since I have done 20+ years of mainly maintenance programming reducing the number of files for a project makes it easier to understand, so it is important to me. Trying to trace data flow across many files is a generally frustrating experience.

So why foist knowing about dependencies of the Baz class into a Service Provider? How is that any different than having a factory method? The dependencies of Baz exist, why is it necessary to create a new class to handle creation? I am not saying you are wrong, I am just trying to understand why you would do it?

From my perspective in maintaining this fictional code base I would now have to open up another file and class in order to learn how an object is created, or if I want to make changes I would have to open another file within my source control. All this seems to be more overhead and a loss of efficiency. If you could explain your perspective I would like to see why the Service Provider design is superior.

Thanks

1 like
HRcc's avatar

The beauty of this approach is the fact, that once you create your service provider (which encapsulates logic of creating a specific class + binding it into the IOC container => follows SRP if you're into that stuff) or use AppServiceProvider, if you need just a simple binding, you are done with it most of the time. You don't need to worry anywhere in the application about the dependencies of your Foo class and if those dependencies eventually change, then you make the edit in one place - service provider, and rest of the application remains unchanged since automatic resolution is used elsewhere.

Actually the service provider is just a convenient place to put this sort of logic (binding to the IOC container). Imagine ignoring IOC container completely and using only the factory structure proposed by you. It works for simple cases, but if you need to allow for singleton approach => your class gets bigger (sure, you can abstract), if you want to provide different class when Foo class is requested => your class gets bigger again (more abstraction)... and this continues. Usually to the point, when you create your own IOC container ver. 0.1

Why bother? It's already been done :) The point is, if you want to use the IOC container (and you should), you need to bind classes into it and the best place to do that is a service provider.

Additionally if you ever try to create a Laravel specific package, you might notice, that the "Laravel specific" part is mostly a service provider wrapping some PHP library.

6 likes
shiny's avatar

Thanks for your well written answer.

1 like
pmall's avatar

@shiny Basically a service provider has no functionality : it configure/initialize things. It is like bootstrap class.

For example, you have an interface, many concrete implementations, and you want to bind the interface to the concrete implementation of your choice. Where would you put it ? In a service provider.

Same for example for associating views with view composers, register custom validator, model observers, etc etc...

@HRcc don't forget a class don't need to be binded to something for its dependencies to be injected.

class Foo
{
    public function __construct(Bar $bar) {}
}

$foo = app('Foo'); // Bar wil be injected even if Foo is registered nowhere
1 like
shiny's avatar

@pmall Thanks for weighing in. My grasp of your vernacular is inadequate to understand your example. I understand an interface to be a declaration of methods to be used to create a class where the method stubs will be coded for. Where my comprehension falls apart is by your term 'concrete implementations.' To me it means that the implementation(s) is(are) of the interface, but that cannot be correct because you discuss binding them together.

Would you be able to provide an example below using an interface with one method and a sample of what a 'concrete implementation' is?

My background is self taught so I figure I have missed out on some foundational terminology that I am working to learn. Thanks for the help.

alenabdula's avatar

@shiny

Where my comprehension falls apart is by your term 'concrete implementations.' To me it means that the implementation(s) is(are) of the interface, but that cannot be correct because you discuss binding them together.

Imagine that your application needs a way to send notifications, so you create SendNotificationInterface. So that you can code to interface and not to concrete implementation. Meaning you are not coding to a specific class, for example, SendNotificationsEmailsViaText. This is concrete, only way to send notifications is by text, that's it. What a service provider allows you to do is register, in service container, that your implementation should use this concrete implementation, and that implementation could be anything. To send notification via push messages, to send notifications via email... etc.

So your application could be:

  • Interface/SendNotificationInterface
  • Notifications/EmailNotifications
  • Notifications/TextNotifications
  • Notifications/PushMessageNotification

Service provider just tells your application. "Hey whenever you see SendNotificationInterface I want you to use this" and this can be any of the concrete implementations that are within Notifications. So it could be PushMessageNotification if that's the implementation you need.

This way whatever implementation you decide to use, it's just the matter of changing the registration in the iOC container (service container), and your application just works, there's no need to search/replace all instances of concrete class that you may have used.

At least that's my understanding of it. Hope that helps. Alen

9 likes
HRcc's avatar

@pmall thanks, in my mind I've been writing about those more complex constructors, singletons, decorators,... where it is useful. Maybe I was not clear enough.

webkenny's avatar

@alenabdula Thank you for that example! I am literally building my very first production Laravel project and it's just a small API to do exactly what you just outlined!! I can't believe my luck in stumbling here! I have some refactoring to do. :)

pmall's avatar

@alenabdula example is clear. But keep in mind service providers are not just here for binding things, you can also associate views to vews composers, setup custom validator, add another file system driver, etc... It is like a bootstrap class for your application (or part of your application).

1 like
shiny's avatar

@pmall Ok I know understand your response. Since your concrete implementations are are defined like

class EmailNotifications implements SendNotificationInterface {}
Why do you need to get involved with registering them with an IOC container? My understanding is that with namespaces php already knows what SendNotificationInterface is and can successfully create a EmailNotifications with this
$oEmailNote = new EmailNotifications();
2 likes
pmall's avatar
# In a service provider register method
$this->app->bind('\App\SendNotificationInterface', function () {
    return new \App\EmailNotifications(); // You can even put some params here
});

Then when you inject the interface in a class you get the specified concrete implementation.

namespace App;

class Foo
{
    public $notification_service;

    public function __construct(SendNotificationInterface $notification_service)
    {
        $this->notification_service = $notification_service;
    }
}
$foo = app('\App\Foo');

$foo->notification_service // This is an instance of \App\EmailNotifications

Now if for some reason you want to change the concrete implementation of the interface (by changing the code in service provider) hey, your app still works :p

You provide concrete implementation of some services, hence the name service provider.

2 likes
thepsion5's avatar

$oEmailNote = new EmailNotifications();

That's fine if EmailNotifications has no dependencies and you'll never need a different kind of notification service, but when either of those constraints aren't true, manually creating instances or creating them via a factory method doesn't scale well. The service provider acts as a single point of configuration and wiring up of dependencies.

1 like
shiny's avatar

@pmall How does $foo = $app('\App\Foo') pass a SendNotificationInterface object into the Foo class constructor?

@thepsion5 Since EmailNotifications implements SendNotificationInterface I would just declare a different class that implements SendNotificationInterface when I want a different kind of notification service just as @pmall wrote in his list of concrete implementations above. If EmailNotifications had dependencies they would be injected in the 'new' statement. My example just did not include any, for brevity.

Would you explain how factory methods don't scale well?

thepsion5's avatar

I would just declare a different class that implements SendNotificationInterface

Which then requires you to track down every location where you create an instance of the service and replace it, including all of it's dependencies, and the their dependencies.

Would you explain how factory methods don't scale well?

If your dependency requires any kind of configuration before it's given to the creating object, that requires you to duplicate code in each factory method. Furthermore, how do factory methods handle classes specific to Laravel, like the validation factory? Instantiating it directly means your code may break when upgrading Laravel despite semantic versioning constraints. The constructor isn't part of the interface it implements, and the factory isn't meant to be instantiated outside of the IoC container.

martinbean's avatar

@shiny Just look at the name literally: it provides a service. Under the hood, it adds services to the service container, so you can retrieve them in your app without having to do any real heavy lifting.

Think of geocoding. I create a geocoding service provider which registers a geocoder with the service container. I can then geocode addresses without having to worry about the actual implementation—the initialisation is done the service provider depending on my configuration. All I do is call methods on an interface. The implementation could be Google Maps, Bing Maps, or even just a plain ol’ array. All I know is, I’m working with a geocoding service.

The beauty of services is, they can be wrapped up and made into their own, discreet packages.

4 likes
shiny's avatar

@HRcc why do you call Service Containers IOC Containers?

@thepsion5 I am not understanding your response. The reason for an interface is to force the coding of the interface methods within each implementation. Each implementation would handle the interface methods the same or slightly different depending on the media it is using. @pmall's examples cited emails, SMS texts and several others, based upon the common interface.

I agree that if you changed the __constructor parameters then you would have to track down all instances within the code base in order to update them. Thus you are saying create a Service Container for these implementations so that if dependencies change then all you change is the Service Container declaration?

So how does the following example from @pmall pass a Bar object into class Foo?

class Foo
{
    public function __construct(Bar $bar) {}
}

$foo = app('Foo'); // Bar wil be injected even if Foo is registered nowhere

@martinbean I am looking for specific examples in order to understand. Using general declaratives is not working as I am not building the relationships in my mind in order to understand. It would be helpful if we had a whiteboard where all this could be diagrammed for me.

Your description is valid, however I have to write all these objects and cannot if I don't understand the architecture and relationships of the objects. I understand that your geo services are all based upon a common interface, but I am not seeing how the Service Container is creating the object when you use the following syntax

$Foo = app('foo');
And the Foo class has a Baz object dependency that must be injected at creation. In older syntax
$Foo = new Foo(new Baz()) ;
would work. So how does the Service Container get the Baz into the Foo?
alenabdula's avatar

So how does the Service Container get the Baz into the Foo?

You would define those dependencies only once, in the service provider.

Think of a service provider as a box that you define all your dependencies in. And in your application, instead of creating a whole new box, each time, you would just reference the existing service provider which resolves all your dependencies.

So your dependencies might look like this

$var = new MyCoolClass(new Baz(), new Foo(new SubFoo()), new Somethn());

As you can see if you needed this resolved somewhere in your application. You'll have to write it. Each. And. Every. Time. Instead of just referencing a service that resolves everything for you. So in the future if you need to change anything, you make the change in one place. So if we follow our example, lets say that new Somethin() needs another dependency.

You would have to search for all instances of $var = new MyCoolClass(new Baz(), new Foo(new SubFoo()), new Somethn()); just to add another dependency.

Just look at the name literally: it provides a service.

This 100%.

1 like
alenabdula's avatar

@shiny send me an email edited to remove email I have few resources you could read, might help.

zoransa's avatar

well if you do not use it your Woops page looks so unprofessional you have in backtrace just few nested calls but if you use service providers you get huge 3-4 letter pages long error message then you can say to your boss I have to fix this big error :)

martinbean's avatar

@shiny I gave you a specific example. I couldn’t be any more specific if I tried. If by “specific” you mean, “write some code for me” then I’m not going to do that.

There are plenty of resources on service providers, both in and outside of a Laravel context; there are even some on this very site. You’ve already had plenty of people explain service providers best you can but if you’re still not grasping it then I’d suggest consuming other resources on service providers until it starts making sense to yourself.

shiny's avatar

@zoransa - LOL

@pmall - I understand dependency injection and from the video that @alenabdula shared with me laravel uses reflection to handle the injection via type hints. My remaining question about that functionality is how does laravel handle this reflection/injection if the injected object has non-object dependencies?

@martinbean - I prefer code snippets for learning, which other contributors have offered, and that has been very helpful.

I have found a great source of enlightenment from the sharing of a video from @alenabdula and I would like to thank him for answering my question.

1 like
pmall's avatar

how does laravel handle this reflection/injection if the injected object has non-object dependencies?

Obviously laravel cant guess non object parameters. You have to manually bind the interface to an instance in service provider.

$param = config('foo.bar');

$this->app->bind('App\SomeInterface', function ($app) use ($param) {
    return new App\SomeImplementation($param);
});
1 like
shiny's avatar

@pmall - Why pass $app into your closure if it is not a dependency? Why use the 'use' keyword to make $param visible to the closure? I don't like the 'use' keyword when you can just pass the same data/variable/object into the closure as a parameter. How does the following code snippet, based on yours, fail?

$this->app->bind('App\SomeInterface', function ($param) {
    return new App\SomeImplementation($param);
});
squigg's avatar

Because you are not calling that function, Laravel is, and it will automatically pass in only one parameter, the Application Container ($app).

1 like
Next

Please or to participate in this conversation.