Lintume
6 min readAug 20, 2022

--

Laravel Contracts, Service Providers, Service Containers, Facades, Services, and more

If you are still afraid of such phrases, then do not worry, this is absolutely normal. If you are no longer new to Laravel and are still hiding from yourself the fact that all of the above systems still need to be recognized and understood, this is the norm. We were all there.

I myself recently realized the need for these patterns for development and contributed to this project on Symfony.
You can successfully code applications on Laravel of medium complexity for many years and safely do without service providers, custom facades and understanding of how it works. Tested in my experience :)
But at one point I felt ashamed of myself for such laziness and carelessness. And I decided to sit down and figure it out. This trick really works! In everything in programming, it is enough to sit down and figure it out. And everything is available for understanding.
All these terms are just beautiful names for simple mechanisms, and when you understand how simple these mechanisms are, you will not forgive yourself for such a long lack of acquaintance with them.
Don’t repeat my mistake. Sit down and figure it out. And now I will try to help you with this. Perhaps my style of articles is not distinguished by the accuracy of presentation but is more of a journalistic nature, but for those who need a textbook, welcome to official docs ;)

I will make a reservation right away — you can understand how all these mechanisms work with any level of knowledge and experience in programming. But to understand what to use it for, to really feel, feel the need and exclaim “How did I live without this before ?!” possible after only the experience of developing complex applications.

I will start simple so that subsequent examples become more complex and build up in complexity by including more and more abstraction mechanisms in Laravel.

Contracts

It’s just an interface. That's it. That’s all you need to know. If you know how interfaces and inheritance work in PHP, you know everything about contracts.
If you speak correctly, then contracts are a powerful tool that allows you to distinguish high-level logic from implementation details and performers.

Simple case example: you have to save pictures in the application via cloud storage and directly to disk. And it just so happens that you need to use both save modes at the same time. In order not to get confused, you create an interface with a list of persistence methods and then inherit it for different implementations. What if saving to another cloud is added? It doesn’t matter, you can always create another contract implementer.

The placement of contracts is arbitrary, but I would advise putting them in a separate folder in App/Contracts.
Contract example:
SaveImage.php

interface SaveImage{public static function save();public static function delete();}

Implementers: the placement is arbitrary, but I would advise putting them in a separate folder in App/Helpers:

Implementer for saving to disk
SaveImageDisk.php

class SaveImageDisk implements SaveImage{public static function save(){
// process saving on disk
}
public static function delete(){
//process deleting from disk
}
}

Implementer for saving to a cloud server:
SaveImageAws.php

class SaveImageAws implements SaveImage{public static function save(){
// process saving on aws
}
public static function delete(){
//process deleting from aws
}
}

Don’t forget the namespace and all use relative to your application!
If necessary, we simply use one or another implementer, for example:

SaveImageDisk::save();
SaveImageAws::delete();

Service Providers

Okay, Google, what if I don’t need to use both implementations of saving images in parallel, but I just need to switch easily and quickly the mode from “save to disk” to “save to the cloud” and vice versa and without this dull ctrl+shift +F throughout the project with autocorrect?

You are at the address :) Terrible words of Service Provider will now become manna from heaven for you.
The service provider just provides this very opportunity for easy switching between implementations. Don’t believe it? Well, look.

Let’s immediately create a service provider and see what’s in it:

php artisan make:provider SaveImageServiceProvider

In the Providers folder we will find a new class, we are interested in the register function, add the code to it:
SaveImageServiceProvider.php

public function register(){
$this->app->bind(‘App\Contracts\SaveImage’, function(){
return new saveImageAws();
//return new saveImageDisk();
});
}

We tell the application that when ‘App\Contracts\SaveImage’ is called, let it return a new object of the saveImageAws implementer class.

That is, in the code we use the following form:

App\Contracts\SaveImage::save();

And the save method will be from the saveImageAws class. In a provider, it’s easy to change the implementor to saveImageDisk by simply uncommenting it and commenting out the old one.
There is a kind of substitution going on here. We seem to be We go straight to the interface and there is something from polymorphism here, but no, everything is much banaler, we just refer to the key-string in the service container, and not to the interface in the Contracts folder.

Let’s complete the process of registering a new provider by adding it to the ‘providers’ array in app.php, just in the same service container, which is described below.
By the way, if you want to create only one object of the implementer class, you want to be sure of this — use instead of bind — a singleton.

Service Container

This is a global container array of all available service providers. The same as we created above. Yes, yes, here it is, dependency-injection, legendary. There are many ways to inject dependencies in a framework, and this is one of them.

Facades

But if you, my friend, are a picky pedant and know that writing static methods is not feng shui and you want your code to be easy to understand and intuitive — welcome to the world of facades!
This is right here my favorite. It is also connected with the substitution of concepts (well, or with syntactic sugar). Correctly speaking, facades provide a static interface to classes registered in a service container.
Simply put, if you have non-static functions in a class, you will be able to access them as static in shorthand syntax without creating a class object using facades. How does this happen? The Facade class uses the PHP __callStatic() magic method to redirect method calls from your facade to the resulting object.

Let’s remake our contract classes and implementers kosher:

SaveImage.php

interface SaveImage{public function save();public function delete();
}

SaveImageDisk.php

class SaveImageDisk implements SaveImage{public function save(){
// process saving on disk
}
public function delete(){
//process deleting from disk
}
}

SaveImageAws.php

class SaveImageAws implements SaveImage{public function save(){
// process saving on aws
}
public function delete(){
//process deleting from aws
}
}

And let’s start creating a facade in the App/Facades folder:
SaveImage.php

class SaveImage extends Facade{
protected static function getFacadeAccessor(){
return ‘App\Contracts\SaveImage’;
}
}

We are returning a string key to access a specific cell in the Service Container, which is what we have bound in the Service Provider, namely ‘App\Contracts\SaveImage’.

To access our new facade by a shortened alias, hide the implementation details and the explicit presence of the facade mechanism — in app.php in the ‘aliases’ array we add:

‘SaveImage’ => ‘App\Facades\SaveImage::class’

And now we use this mechanism as:

SaveImage::save();

This code is equivalent to the following:

$app = app();
$app->make(‘SaveImage’)->save();

The save function will still be taken from the saveImageAws implementor class, even though the function is not static — we now can refer to it as static.
Don’t forget, SaveImage is just an alias, a string. Must be added to use SaveImage.
If you do not add an entry in the ‘aliases’ array in app.php with the key SaveImage, then you can use your “overloaded” class through the facade directly:

App\Facades\SaveImage::save();

All of the above mechanisms are relevant for really large applications. In the vast majority of cases, dependency injection through the constructor is sufficient. Inept use of facade patterns and services can cause sleepless nights :) In general — experiment, learn, but use knowledge for good and appropriate. Remember that most likely after you there will be someone else to edit this code :)

--

--