Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel - do I need a service provider for each of my service containers / custom classes?

Service containers / providers are probably much simpler concepts than I imagine, but after several hours of reading I still don't get it, entirely.

I have created a simple DateFormat class within app/Library. After creating an alias for it inside \config\app.php I can use it right away in any controllers or blade templates.

<?php namespace App\Library;

class DateFormat {

    public static function getDate($timestamp){
      // processing the timestamp      
    }

}

Did I just create a Service Container? If yes, do I need to create a Service Provider as well? Where come bindings into the picture?

I would really appreciate some light on the subject.
Thanks

like image 723
Edmond Tamas Avatar asked Sep 20 '17 19:09

Edmond Tamas


People also ask

What is the difference between service container and service provider?

Service container is where your services are registered. Service providers provide services by adding them to the container.

Why do we need service provider in Laravel?

Service providers are the central place to configure your application. If you open the config/app. php file included with Laravel, you will see a providers array. These are all of the service provider classes that will be loaded for your application.

What is route service provider in Laravel?

All Laravel routes are defined in your route files, which are located in the routes directory. These files are automatically loaded by your application's App\Providers\RouteServiceProvider . The routes/web.php file defines routes that are for your web interface.

What is the use of service container in Laravel?

The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods.


1 Answers

No. What you created is simply an alias to your class. Services Providers are a way of binding a specific class, and often are used in conjuction with a Facade.

An alias is simply a convenient way to use a class without having to import the entire namespaced class every time.

For example, if you have a class \My\Very\Long\Class\Adapter, you could alias this in config/app.php:

// config/app.php
<?php
'aliases' => [
    // a bunch of aliases
    'MyAdapter' => My\Very\Long\Class\Adapter::class,
]

And now you can just do:

<?php

new MyAdapter();
... 

instead of:

<?php

use My\Very\Long\Class\Adapter;
...
new Adapter();
...

A Service Provider is often used when you want to resolve a dependency, most commonly through injection. This can be helpful when the class you want to resolve requires parameters to passed into the constructor or has a common setup every time. You can perform all that setup in the Provider.

Here's a scenario:

You have an API that you want to interact with. We'll call it SuperApi. The docs for SuperAPI say that to create an instance of the SuperApi class, you have to do something like:

<?php
// Some method (a controller or something)
public function index()
{
    $superApi = new \SuperApi\Connector($key, $secret);
    return $superApi->getCustomers();
}

Now, every time you want to create an instance of this, you'll have to do the same setup (or abstract it to some class, but the fact remains that you need to pass a $key and $secret to the constructor).

If you were to create an alias for this Connector class, maybe it would be:

// config/app.php
<?php
'aliases' => [
    // a bunch of aliases
    'SuperApi' => SuperApi\Connector::class,
]

So with that alias, you can now do this:

<?php

// Some method (a controller or something)
public function index()
{
    $superApi = new SuperApi($key, $secret);
    return $superApi->getCustomers();
}

But you see, that even with the alias, you still need to pass the $key and $secret.

This is where a Service Provider can help.

// app/Providers/SuperApiProvider.php
<?php

namespace App\Providers;

use SuperApi\Connector;
use Illuminate\Support\ServiceProvider;

class SuperApiProvider extends ServiceProvider
{
    /**
     * Register bindings in the container.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind('superApiConnector', function ($app) {
            return new ApiConnector($app['config']->get('super-api.key'), $app['config']->get('super-api.secret'));
        });
    }
}

// app/Providers/SuperApi.php (the Facade)
<?php

namespace App\Providers;

use Illuminate\Support\Facades\Facade;

class SuperApi extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'superApiConnector';
    }
}


// config/super-api.config
<?php

return [
    'key' => env('SUPER_API_KEY'),
    'secret' => env('SUPER_API_SECRET'),
];

// config/app.php
<?php
'providers' => [
    // a bunch of providers
    App\Providers\SuperApiProvider::class,
]

See that the string you bind to in the provider ('superApiConnector') is the same as what you return from the facade and the class name of the facade is how you'll actually call the binded class, in this case SuperApi.

Now, when you want to user the SuperApi\Connector class, you can do this:

<?php

// Some method (a controller or something)
public function index()
{
    return SuperApi::getCustomers();
}

And as I said above, where a provider really comes in handy is when you want to inject it and have Laravel's IoC Container automatically resolve the injected class:

<?php
// Some method (a controller or something)
public function index(SuperApi $api)
{
    return $api->getCustomers();
}

To be clear, you do NOT need a Service Provider to take advantage of dependency injection. As long as the class can be resolved by the application it can be injected. That means whatever arguments the constructor of the class you're injecting takes need to also be auto-resolvable.

like image 98
tptcat Avatar answered Nov 02 '22 10:11

tptcat