There were many discussions if #Laravel’s facades implement Gang of Four’s Facade Pattern, but I think it does not matter at this point - the Laravel team won’t change naming convention anyway. Naming is not a problem, I see other issues with facades - let’s look at it!
This is not critique
First of all, before stating anything about Laravel Facades, I would like to emphasise that I am not against both of them: framework and the pattern. The whole point of this article is to shed some light on the facades, provide different perspective. I hope this will spark some discussion, make developers think about it.
I am not saying you have to drop facades entirely and use only Dependency Injection - just choose wisely 😉 Do you want to be a PHP programmer or Laravel Developer? Because if you follow general programming principles, you won’t have problems after switching to another framework, but if you do “the Laravel Way”, you may encounter many problems outside the framework.
It’s a kind of magic
Some may see it as advantage, but I consider it as drawback. You don’t execute exact code you’re calling, but your call is proxied to some underlying service. It strictly couples your code with the framework, which handles it. Facades’ API has to be added as comments in PHPDoc (with
@method annotation) which is error-prone because it’s easy to forget to update facade’s phpDoc when underlying service (accessor) is changed. But even if autocompletion in IDE works, you just can’t simply navigate to method’s code.
There are of course tools like
barryvdh/laravel-ide-helper which can solve some (or even all) of those problems, but for me, it’s not a great developer experience that you need external tool installed just to work with the code in reasonable way.
As a side note: the same problem is with Laravel’s helper functions. Of course, they simplify code (well, it’s subjective), but at the expense of very strict coupling with the framework. The main problem I see with it, is that people learn how to use Laravel, not how to write in PHP.
They break Single Responsibility Principle
Laravel’s facades act as static proxy to service locator, but at the same time… they provide testing API. In theory, you end up on production with mockable code.
Since facades are used statically, they can’t be replaced with other implementation during object initialisation. That’s why base Facade class uses Mockery and provides a bunch of methods that allows testing. That overflows API: your IDE suggests methods that you don’t need for regular development since they’re only for testing purposes.
You may think: “But it works! I can test my code” and you’re somehow right. But since
Facade uses Mockery, while Laravel has it only in
require-dev, you may end up with “malfunctioning” facades when you install with
--no-dev option (e.g. on production). For me, it’s a code smell.
On the other hand, when you build/deploy improperly (
composer install without
--no-dev), you may end up with mockable facades on production - testing code should not reach this stage. Add to it “debug on production” approach and here you go, looking for troubles 😅
Before preparing thread on Twitter I wasn’t even aware of such concept in Laravel, but I did my homework before posting and read documentation. This one particular section struck me:
you may treat any class in your application as if it was a facade
I’m just amazed that someone had this idea and Laravel officially suggests wrapping any service with magic facade 🤯 It’s not even magic, it’s sorcery! 😅
But honestly, I don’t like this idea at all. If you want to use framework’s built-in facades - OK. If you want to provide your own facade - OK. But magically wrap external classes with non-existing namespace just to use class’ API statically? Nope. Again, some may say “It’s awesome! It simplifies a lot!” and again they’ll be somehow right. But also again: it breaks SOLID principles, covers explicit dependencies with magic, strictly couples code with Laravel, teaches wrong (IMHO) practices.
It was not covered in the Twitter thread, but there is one small detail related to facades - you can omit importing facade’s FQCN and treat them as they were in global namespace, and they still will work 😩
As you can see in the image above, the IDE suggests that class does not exist, but executing that code works.
It’s because of another magic approach - configurable aliasing (with defaults). Basically, you configure which aliases (consider them as non-existing classes from PHP’s global namespace) should be treated as facade and resolved as actual class.
This is magic behind another magic. Static proxy aliased through configuration. For me it’s too much 😅
Instead of using facades, you could use Dependency Injection approach and use facades’ underlying services directly. They’re listed on Laravel docs, most of them have Service Container Binding, that can be used for injecting.
I assure you, it will help you when you will need to work with other frameworks. Switching from Laravel to different framework is, in my opinion, harder than the other way around, so if there’s a chance that you will work with other frameworks, this approach will prepare you better for it. Sticking to Laravel’s conventions may lead to habits that are not applicable elsewhere.
But of course, it’s always your (or your team’s) choice 🙂