What Is the Concept of Service Container in Laravel

What is the concept of Service Container in Laravel?

The Service Container in Laravel is a Dependency Injection Container and a Registry for the application

The advantages of using a Service Container over creating manually your objects are:

Ability to manage class dependencies on object creation

You define how a object should be created in one point of the application (the binding) and every time you need to create a new instance, you just ask it to the service container, and it will create it for you, along with the required dependencies

For example, instead of creating objects manually with the new keyword:

//every time we need YourClass we should pass the dependency manually
$instance = new YourClass($dependency);

you can register a binding on the Service Container:

//add a binding for the class YourClass 
App::bind( YourClass::class, function()
{
//do some preliminary work: create the needed dependencies
$dependency = new DepClass( config('some.value') );

//create and return the object with his dependencies
return new YourClass( $dependency );
});

and create an instance through the service container with:

//no need to create the YourClass dependencies, the SC will do that for us!
$instance = App::make( YourClass::class );

Binding of interfaces to concrete classes

With Laravel automatic dependency injection, when an interface is required in some part of the app (i.e. in a controller's constructor), a concrete class is instantiated automatically by the Service Container. Changing the concrete class on the binding, will change the concrete objects instantiated through all your app:

//everityme a UserRepositoryInterface is requested, create an EloquentUserRepository 
App::bind( UserRepositoryInterface::class, EloquentUserRepository::class );

//from now on, create a TestUserRepository
App::bind( UserRepositoryInterface::class, TestUserRepository::class );

Using the Service Container as a Registry

You can create and store unique object instances on the container and get them back later: using the App::instance method to make the binding, and thus using the container as a Registry.

// Create an instance.
$kevin = new User('Kevin');

// Bind it to the service container.
App::instance('the-user', $kevin);

// ...somewhere and/or in another class...

// Get back the instance
$kevin = App::make('the-user');

As a final note, essentially the Service Container -is- the Application object: it extends the Container class, getting all the container's funtionalities

Why use service container instead of new class

The purpose of Dependency Injection, which is achieved via Laravels IoC container (service container), is to separate the creation and use of objects. So I would argue that having app(Employee::class); is no better than new Employee or Employee::create(). IoC is more than just registering objects with the container and pulling them out when you need them.

A practical example for this might be where you have a class which transmits SMS messages.

class Sms
{
private $smsProvider;

public function send()
{
$this->smsProvider->send();
}
}

So your SmsSender class requires a smsProvider to function. Rather than creating a new instance of the SMS provider in Sms class, you would inject the provider into the class (usually the constructor in such examples) and make use of it that way. If you wanted to make it even more flexible, you would define an interface for SMS providers and then allow the service container to inject the correct concrete class from your IoC mappings.

class Sms
{
private $smsProvider;

public __construct(ISmsProvider $smsProvider)
{
$this->smsProvider = $smsProvider;
}

public function send()
{
$this->smsProvider->send();
}
}

class SmsTwillio implements ISmsProvider
{
}

class SmsNexmo implements ISmsProvider
{
}

You would then define mappings in the service container to bind references to ISmsProvider to a concrete implementation such as SmsTwillio or SmsNexmo.

Using your example, you might define a PayrollService:

class PayrollService
{
public function process($employees, $start, $end = null)
{
foreach ($employees as $employee) {
$salary = $this->calculateNetSalary($employee);
}
}

private function calculateNetSalary(Employee $employee)
{
}
}

class ProcessPayrollController extends ProcessPayrollController
{
private $payrollService;

public __constructor(PayrollService $PayrollService)
{
$this->payrollService = $payrollService;
}

public function __invoke()
{
$result = $this->payrollService->process(Employee::all(), '2021-06-01');
}
}

I don't think your Payroll class needs Leave or Timesheet. I would define functions or scopes on your Employee class which returns that data:

$employee->daysUnpaidLeave('2021-06-01');

$employee->hoursLate('2021-06-01');

Where the dates are the start date to calculate from until today, or provide an optional end date as a second argument.

You could go even deeper down the rabbit hole and look at other creational design patterns for creating objects at runtime, however, design patterns can be quickly abused and add complexity rather than reduce it, so keep it simple.

The Registry in OpenCart is a similar principal. In that example $registry->get('language'); will always return the same instance of Language, so yes it would be a singleton.

Your Payroll could be a singleton as you don't need multiple instances of it as it is simply processing data you provide to it.

Where to find Service Containers in a Laravel project?

Service container is a core component of Laravel framework that is ready for you to use as it is, you are not supposed to create your own service containers like you do, for example, with service providers.

You can imagine service container being an associative array where you can store dependencies (services) and logic of how to resolve them. Then you can use service container to give you what you need based on what is there using provided logic.

It would be easier to imagine that service container is a black box that is always available. Your app at first registers (puts) certain rules in there (for example, if someone wants an object that implements PriceCalculator interface then give him object of a class MyPriceCalculator). It is done in register() method of your service providers:

$this->app->bind('App\Contracts\PriceCalculator', 'App\Shop\MyPriceCalculator');

Then this black box is always available for you, so if you ever need PriceCalculator object (for example, somewhere in your cart controller to calculate price of some order) you can now instead of doing:

$calculator = new \App\Shop\MyPriceCalculator;

Ask service container to make you a proper one:

$calculator = app()->make('App\Contracts\PriceCalculator');

Note how we are asking service container to give us implementation of an interface and will in turn give us new App\Shop\MyPriceCalculator object because that is how we defined (registered) App\Contracts\PriceCalculator service earlier.

Using service container is a great way to manage all dependencies of your application since your code will be working with abstractions and how those abstractions are resolved will be always defined in one place (which means it's easier to maintain if you want to change something later on).

If you are new to Laravel I would recommend to skip service containers for now since it's a little bit more advanced topic and you are required to have a better understanding of dependency injection pattern to fully grasp it and use properly.

You can read official documentation here.

Laravel What is the use of service providers for laravel

First of all, Laravel uses service container and service providers, not server container or server provider :)

Here are some benefits of using dependencies injection (DI):

Simplify the object creation

Because your Test class constructor is quite simple, you don't see the benefit of dependencies injection. Think about a class like this:

class Complex {
public function __construct(
FooService $fooService,
BarService $barService,
int $configValue
) {

}
}

Without DI, you have to get (or create) instances of $fooService and $barService, retrieve the value of $configValue from the configuration files every time you want a new instance of the Complex class.

With DI, you tell the service container how to create the Complex instance once, then the container can give the correct instance for you with one call (e.g. $container->make(Complex::class))

Manage the couplings between your classes

Continue with the previous example. What happens if the FooService and BarService depends on other classes, too?

Without DI, you have to create instances of the dependent objects (and hope that they do not depends on other classes). This usually ends with multiple instances of one class created, a waste of codes and computer memory.

With DI, all dependent objects are created by the container (you have to register those classes with the container before). The container also manages to keep only one instance of each class if you want, which save the amount of code as well as the amount of memory used by your program.

Only use one instance of your classes when registering with singleton

To keep only one instance of the class in the whole life of the current request, you can register your class creation process with singleton method instead of bind

the meaning of dependencies in composer and laravel service container

Composer deals with making copies of libraries available within the project at all. It ensures the libraries you have denoted as required are present in the vendor folder within your project. It doesn't know anything about you project or how these libraries are used, or if they are at all. Composer deals with which libraries your project depends on and makes sure they are available.

The Laravel service container deals with instantiating instances of classes at run time.

If you have a parameter to a constructor with a specific class type-hint, the service container is the piece which resolves that for you. e.g.

public function __construct(User $user)
{
$user;
}

The Laravel service container deals with run time dependencies and resolves classes in order to instantiates objects as needed.



Related Topics



Leave a reply



Submit