Hion Coding - Blogs share everything
  • HOME
  • ABOUT
  • POSTS
  • CONTACT
  • TOOLS
    CÂU HỎI VÕ ĐÀI TỐI THƯỢNG CONVERT IMAGE TO WEBP
Elevating Junior Laravel Developers: Learn about Service containers

Elevating Junior Laravel Developers: Learn about Service containers

Hion Coding - Blogs share everything
Admin Hion Coding Blogs vochilong.work@gmail.com
3rd January 2024

PHP/Laravel

Elevating Junior Laravel Developers: Learn about Service containers

Reference: Viblo

Index Elevating Junior Laravel Developers
 series:

  1. Elevating Junior Laravel Developers: Recipes & Best Practices
  2. Elevating Junior Laravel Developers: Learn about Service containers
  3. Elevating Junior Laravel Developers: Learn about Service Provider
  4. Elevating Junior Laravel Developers: Learn about Facade
  5. Elevating Junior Laravel Developers: Learn about Contract


In the previous article, I introduced Laravel, as well as some Best Practices that I often use. In this article, I will focus on introducing and explaining the core components of the Laravel Framework, hoping to help you better understand how Laravel works.
The first will be about a component considered the "heart" of Laravel, Service Container, which you may be using a lot but don't know where you are using it!


Dependency Injection and Inversion of Control

There is also an opinion that Inversion of Control and Dependency Injection are the same thing, and they are just two names used for one purpose. However, most people think that Dependency Injection is a small part of Inversion of Control, and it is one of the solutions to implement Inversion of Control. In general, we can understand it simply as follows:

  • Dependency Injection: I mentioned it in a previous article. If a Class A depends on several other Classes, then instead of creating instances of those Classes inside Class A, we will inject those instances through the constructor or setter. The instances of Classes that Class A needs to operate are called dependencies. For example:
class Monitor {}
class Keyboard {}
class Computer
{
    protected $monitor;
    protected $keyboard;
    public function __construct($monitor, $keyboard)
    {
        $this->monitor = $monitor;
        $this->keyboard = $keyboard;
    }
}

$computer = new Computer(new Monitor(), new Keyboard());


As in the example above, we can see that the Computer class needs dependencies which are instances of Monitor and Keyboard. Instead of initializing these dependencies inside the constructor of the Computer class, we will inject them when calling a new Computer.

  • Inversion of Control: Here is the definition of IoC on Wikipedia:
    In software engineering, inversion of control (IoC) describes a design in which custom-written portions of a computer program receive the flow of control from a generic, reusable library. A software architecture with this design inverts control as compared to traditional procedural programming: in traditional programming, the custom code that expresses the purpose of the program calls into reusable libraries to take care of generic tasks, but with inversion of control, it is the reusable code that calls into the custom, or task-specific, code.

It looks confusing and confusing, right? IoC is a programming technique that "reverses" the flow of traditional processing. Normally, our logic processing segment will call the classes and libraries it needs to use, but with IoC, we will send those logic processing segments what they need. In other words: Don't try to call around to create the things you need (dependency), we will give them to you when we need them!

With IoC, dependencies will be grafted onto the corresponding objects at run time instead of compile time.

If you still don't understand IoC clearly, let's look at the following example:

// With IoC
// First, we register IoC Container
IoC::register('computer', function() {
    $keyboard = new Keyboard();
    $monitor = new Monitor();
    $computer = new Computer($monitor, $keyboard);
    return $computer;
});

// get computer instance with dependency inject
$photo = IoC::resolve('computer');

// Without IoC
$keyboard = new Keyboard();
$monitor = new Monitor();
$computer = new Computer($monitor, $keyboard);

As we can see, when we need to initialize an instance of Computer, instead of normally initializing it with the new keyword and then passing dependencies to it through the constructor, with IoC, we will remove it from the Container using the resolve function, The processing of creating dependencies as well as putting dependencies into the class that needs to be initialized is all done in the callback function that we have registered with the IoC Container.

With this model, we can initialize the Computer at any time without having to remember what dependencies it needs. When you need to add a new module to the Computer, just edit the callback function that we registered with the IoC Container and you're done.

It's great that Laravel supports IoC out of the box, we just need to use it.


Laravel Service Container

Bind and Resolve

Laravel uses two concepts bind to refer to registering a class or interface with the Container and resolve to retrieve the instance from the Container. For example, we have an example of how to bind as well as resolve as follows:

// Binding
\App::bind('computer', function() {
    $keyboard = new Keyboard();
    $monitor = new Monitor();
    $computer = new Computer($monitor, $keyboard);
    return $computer;
}

// Resolving.
\App::make('computer');
app('computer');
app()->make('computer');
app()['computer'];

As an example in the previous section, we bind to the Container with a callback that handles initialization, then use make to retrieve it, which is a common usage. However, the following example will show you how powerful Laravel's Service Container is

class Computer
{
    public $monitor;
    public $keyboard;
    public function __construct(Monitor $monitor, Keyboard $keyboard)
    {
        $this->monitor = $monitor;
        $this->keyboard = $keyboard;
    }
}
$computer = app('Computer');
// OR
app()->bind('computer', 'Computer');
$computer = app('computer');

As you can see, we don't need to pass any callbacks to the bind function. We can even directly call app('Computer') to create an instance of a Computer without needing to bind anything, without needing to initialize dependencies, without needing to inject anything!

Let's analyze how Service Container performs that miracle.

  • First, let's look at the constructor of the Computer class. Here we have a type hint for the passed dependencies. Specifically, $monitor is an instance of the Monitor class, and $keyboard is an instance of the Keyboard class.
  • When we call app()->bind('computer', 'Computer'); That means we have registered an instance of the Computer class with the name computer. We can resolve the instance from that class using app('computer').
  • When we call app('Computer'), Laravel will first check to see if there is anything bound to the Container under the name Computer. If not, it will consider Computer as the class name and resolve to an instance from the Computer class.
  • The process of creating an instance is carried out as follows: The Container will first check that the Computer needs two dependencies, $monitor and $keyboard, and because we have type-hint these two dependencies, the Service Container will understand that it needs to be removed. Where do those 2 dependencies come from? Service Container resolves to 2 instances of Monitor and Keyboard (the resolution process is similar to resolving to Computer), then injects it into Computer via constructor injection. And so we will finally get an instance of Computer.
  • Note that the Service Container will give priority to what is bound to it. That is if you have the statement app()->bind('Computer', function(){return 'thang';}); When resolving Computer it will output the string 'thang' instead of an instance of the Computer class


One of the weaknesses of dependency injection is that programmers will encounter many difficulties when their dependencies depend on each other. As in the example above, the Computer has dependencies Monitor and Keyboard. But what happens if your Monitor has several dependencies? And those dependencies have a few other dependencies. You will have to prepare all those dependencies to create the computer. However, with Service Container, everything is much more comfortable. If your class's dependencies need other dependencies, it will be able to automatically inject them all for you.


Singleton binding, Instance binding, Interface binding and more

  • Singleton binding: As its name suggests, the instance will only be resolved once, subsequent calls will not create a new instance but will only return a previously resolved instance.
app()->singleton('now', function() {return time();});
app('now') === app('now'); // true
  • Instance binding: Similar to Singleton Binding. You have an instance and you bind it to the Service Container. Every time you take it out, you will get that instance back.
$now = time();
app()->instance('now', $now);
$now === app('now'); // true
  • Interface binding: As mentioned above, you can bind a Class under any name. And what happens if that name is an Interface? Well if you bind an Interface to its Implementation then you will be able to type-hint that Interface. Let's look at the example below
class MailerImplementation implements MailerInterface {}
// Binding Interface with Implementation
app()->bind('MailerInterface', 'MailerImplementation');
// In constructor a class, we can type-hint by interface
public function __construct(MailerInterface $mailer)
{
    $this->mailer = $mailer;
}

In the above example, when a class instance is resolved using the Service Container, $mailer will also be resolved, and it will be an instance of MailerImplementation. In Laravel, you will encounter many of the above cases when using Contracts. In essence, Contracts are simply Interfaces, and when you resolve or type-hint them in the constructor or methods, you will receive the corresponding Implementation registered with the Service Container. We will repeat more about this concept in the following articles.

But do you wonder why you need to bind an Interface to an Implementation?
Why do we need to type-hint an Interface? Why don't we directly use the class name as the implementation of that Interface?


The answer is that with type-hint interfaces, we can have the flexibility to change the Implementations we want or use an Implementation we write instead of the default Laravel one without any impact. to your Framework or service. That's the "program to an interface, not an implementation" principle!
For example, you need to write a package that uses the email-sending function. In other words, you will need a class whose dependency is a certain mail sending class, say MailerImplentation for example. If you use dependency injection through the constructor with the type-hint being MailerImplentation, your class will be completely dependent on that MailerImplentation and cannot be changed. Instead, you should let your class's dependency be MailerInterface, then any class that implements MailerInterface can be injected and used in your class. So in the service, there only needs to be an implementation of MailerInterface for your package to work, you don't need to know what that implementation is, whether it is the default or whether someone else wrote it.

  • Contextual Binding helps you solve the problem of using multiple Implementations in your service. For example, you have 2 or 3 classes that are implementations of an Interface. However, in one case you need to inject one implementation and in another case you need another implementation, then you will need Contextual Binding. In addition, you can also use tags to group many things into one at once, making it easier to resolve.
// Tag
app()->tag(['Foo', 'Bar'], 'example');
// Resolve instance of Foo and Bar
app()->tagged('example');


Service Container everywhere

Service Container is the central component of Laravel, and you may not notice it, but it is being used in every corner of your project. Do you remember in the previous article, the part about Dependency Injection, I introduced an example like this:

class UserController extends Controller
{
    public function update(\Illuminate\Http\Request $request, $id)
    {
        $all = $request->all();
    }
}

That is an example of method injection, which means injecting dependencies inside controller methods. The reason Laravel can help us do that is because it uses a Service Container to resolve every controller. If you have already used Service Container, as explained above, you just need to type-hint, and you will have the corresponding instance.
Not only the Controller but most other important parts of Laravel such as Middleware, Listener, Queue... are resolved from the Service Container. It also means you can perform dependency injection easily on them.

Epilogue
On Laravel's official documentation, there is an introduction to Service Container as follows:

A deep understanding of the Laravel service container is essential to building a powerful, large application, as well as for contributing to the Laravel core itself

In general, to be able to learn deeply about Laravel, Service Container is a concept that you cannot help but master. Hope this article can be of great help to you.

See you again in upcoming articles introducing concepts surrounding Service Containers such as Service Providers, Contracts, or Facades
 

Reference: Viblo

Thank you! Good luck! Hion Coding


Integrating CKEditor 5 In Laravel 10 Using Vite

9th April 2024

Integrating CKEditor 5 In Laravel 10 Using Vite

PHP/Laravel

Laravel Eloquent Tips🔥 🚀

3rd January 2024

Laravel Eloquent Tips🔥 🚀

PHP/Laravel

Elevating Junior Laravel Developers: Learn about Contract

3rd January 2024

Elevating Junior Laravel Developers: Learn about Contract

PHP/Laravel

Elevating Junior Laravel Developers: Learn about Service Provider

3rd January 2024

Elevating Junior Laravel Developers: Learn about Service Provider

PHP/Laravel

Hion Coding - Blogs share everything


© 2025 Hion Coding DMCA.com Protection Status