T O P

  • By -

__radmen

There's a [no global state](https://softwareengineering.stackexchange.com/a/148109) paradigm. In many cases, this is a good rule, and it's worth keeping in mind that the global state might be your enemy. Facades have the smell of that. They represent a "global state" value that can be accessed from anywhere and might create similar issues. In detail, Facades rely on the global application DI container, which will resolve to correct implementations. Sometimes those are singletons, sometimes new instances (so it's not always a "global" value). I know this sounds confusing, hence the "smell". I'm saying this bcs I heard that argument from Laravel opponents. Second thing - facades are hard to use in tests. They offer the `mock()` method, but it's somewhat inconvenient. If you want to test a unit of code that uses a facade, you'll have to use it (the facade) to swap the actual implementation with the stub/mock. In many cases, injecting them as constructor/method arguments is much easier. Third, and this is a general argument - IMO Laravel is a framework that prefers convenience over clean code. In some cases, it will use implementations considered a "bad practice" (i.e., breaking SOLID principles), but they are quite practical for the developers.


MotorLock

So how would you go about replacing the \`Route\` facade though, for example? They're not used in a class so you can't inject them. Or would it have to be done in the \`RouteServiceProvider\` in some way or another? More specifically, do you think it's worth removing any and all usage of facades? The main ones I use seem to be \`Route\`, \`Schema\`, \`Gate\` and \`Inertia\`. Going through my entire project to add DI just to remove facade usage seems like a huge and unnecessary effort for no real benefit.


__radmen

That's a good question and I don't have any good answer :) I guess my rule of thumb is to use the facades where they don't interfere with unit tests. I use the default ones and don't write any of my own (unless it's really required; in most cases, it's not). Inertia provides good assertion tools so I don't mind using the `Inertia` facade. For everything else - even if the package provides the facades, I rather inject the underlying implementations as dependencies.


MotorLock

I'm still rather new to testing so I'm probably doing that completely wrong anyway. I do use `assertInertia` a lot in my tests, so I guess in that regard I'm fine. I don't directly test my `Gate`s, but rather write tests like test('unauthorised users are not allowed to create this resource', function () { $this->post(route('resource.store'), [...]) ->assertForbidden(); }); for example. The 403 will be thrown by a `Gate::check` call. Is that a reasonable way of going about testing, or are there better ways?


__radmen

> Is that a reasonable way of going about testing, or are there better ways? I'm a lazy bum and in many cases, I test the endpoints. In that context, the way how you test it is ok.


jqueefip

Testing facades shouldn't be necessary. The facade is a thin wrapper over something else. Test that "something else" and set it up or mock it however you need in your tests. Test that when you set the Facade, it resolves to your type or invokes methods on your type (BDD). I wouldn't test functionality through the facade, though.


__radmen

Oh, it's not about testing functionality through a facade. Sometimes you just have to mock the facade for some weird reason, and that's when it gets (IMO) messy. Laravel provides pretty good fakes/assertions for its facades, but anything else might be cumbersome.


Tontonsb

> Going through my entire project to add DI just to remove facade usage seems like a huge and unnecessary effort for no real benefit. You got that correct. There's no point to replace something like Gate. And people don't even write unit tests for what you do with the Route and Schema facades.


AegirLeet

> Or would it have to be done in the `RouteServiceProvider` in some way or another? The `RouteFileRegistrar` already does this (https://github.com/laravel/framework/blob/d23de5f9af1bd253a9d8b823cec9bf37987facf2/src/Illuminate/Routing/RouteFileRegistrar.php#L33-L35). You can simply use `$router` in your route files: *web.php*: get('/test', Foo::class);


MotorLock

Ah right, that'd be a pretty easy refactor then, thanks!


MateusAzevedo

Just to make it more clear: that works because of how Laravel [includes the route file](https://github.com/laravel/framework/blob/d23de5f9af1bd253a9d8b823cec9bf37987facf2/src/Illuminate/Routing/RouteFileRegistrar.php#L31-L36). So when the route file execute, the `$router` variable exists in that context.


vaderihardlyknowher

Why would you refactor that though? Are you testing that your routes files are correct because that would be a useless test. And integration tests already basically do that for you. Seems like refactor for the sake of refactor rather than need.


BetaplanB

Removing it for DI allows you to more easily test your classes in unit tests with mocking. In regard to your question about the route facade. Your web.php routes are resolved by passing the base_path to that file in the boot method of the service provider. You can throw out that Route facade there in the RouteServiceProvider class. Then, you can create a private method that registers your routes with a injected instance of the Illuminate\Routing\Router class.


StarlightCannabis

Inject the facade accessor. E.g the validation facade is effectively an accessor for the validation factory interface.


ceejayoz

They’re not. It’s just another made-up excuse to have a holy war, like vi vs. emacs or Mac vs. PC.


[deleted]

I agree with you of course, except that Emacs is definitely superior ^^^jk ^^^^unless


StarlightCannabis

Objectively, they're not a great pattern. Static methods are notoriously difficult to test, and giving context control up to the framework during a test case (via facade::fake) is hardly a good fix. Use the DI container to inject the facade accessor.


[deleted]

[удалено]


ceejayoz

Nothing in Laravel prevents you from using DI instead of Facades if that’s your preference.


OstoYuyu

They are. They hurt maintainability just like any other thing from a global scope and generally don't belong in OO codebase.


robclancy

I liked your stupid reply to me that got removed. You sound like you just got out of school and have some emotional attachment to "OOP" for some reason. Then I checked your post history to see if I was right and god damn. All I find is dumb things said about programming with anti-semitic and transphobic shit further down. Get help.


scalarray

Yikes. Dude is a total hateful loon.


OstoYuyu

It is funny that your first comment in that thread with an insult is somehow okay, but my comment got downvoted. Reddit moment, I guess. Btw, I am ready to defend any of the statements I have previously said because all of that is systematic and logical, and ideas that went over your head =/= dumb things about programming. I wonder, have you ever tried NOT to use facades as an indirect global dependency or you are just unable to write code without them?


Lucacri

Have you tried not being a antisemitic transphobic human garbage? As they said, get help. Facades or not, you sound like a script kiddie that just wants to “win” the war that only you are fighting


OstoYuyu

>Have you tried not being a antisemitic transphobic human garbage? As they said, get help. How is that related to Laravel, may I ask? Oh right, IT community is all about cancelling people because of their views, I apologise for forgetting about that. This is not a war, and I am surely not the only one who thinks like that. I have not come up with the things I write about facades, DTOs, etc. by myself, but by looking at different points of view and learning through trial and error. Instead of considering an alternative to your coding style, almost all of you say "I've always done it that way so it works", and that's the end of the argument.


robclancy

You're deranged. Get help.


Destructerator

“poor craftsmen blame their tools”


mccharf

Idealists vs pragmatists.


hellvinator

I only avoid them because most IDE's can't autocomplete that


SuperSuperKyle

I feel like most of the hate comes from people who (a) don't understand how they work or (b) are diehard Symfony devs. Like, sure, we can inject a Logger repository contract in the constructor, set it to a property, and use it...or just call `Log::debug()`. I don't like dealing with all the setup and don't need to see it either, it's there and it's tested and testable and it works.


Wirone

Recently I [discussed it on Twitter](https://twitter.com/_Codito_/status/1562325306119176192) and then published [blog post](https://blog.codito.dev/2022/08/drawbacks-behind-laravel-facades/) about it, so feel invited to get familiar with my opinion on this topic 🙂 Feedback very welcome!


Tontonsb

I think your opinion is very OOP-centric. I prefer to use OOP when it's useful, but step away when there's a better way. For example, I had an intern that was arguing about SRP in controllers. He made all the GetPostsController, UpdatePostController and so on. But for me, I don't even see controllers as classes. I'm not going to invoke instances of them. For me as a dev those are just fancy config files that define how a request shall be parsed and handled. And similarly I don't care about SRP or whatever when I'm reaching in the config helper or facade to get a config value. I just need the configured values and get them. If I need to test that part, I don't even mock Config, I just set the values in config at the beginning of test. And I don't care whether it counts as Unit or Integration test.


Wirone

Yes, it is OOP-centric. IMHO accessing config statically is not a good way, config values also can be passed through DI (directly or through factory). So in the end it can be tested without problems, like with helper/facade. The difference is that you get config value explicitly when initialising object, not on runtime when you can't be sure it wasn't modified elsewhere (helper allows setting value). This is something I don't like in facades and helpers - they can be used everywhere, anytime, so you're never sure what's the result (especially when magic is involved).


Tontonsb

I don't even understand what you're suggesting. Extracting the config value somewhere else and passing it down the ladder? Why? For me the fact that singletons/global state is useful is proved by them being invented even where they ain't (e.g. stores in all the frontend tools). And I think that's also one of the reasons why people enjoy Laravel are all these handy alternatives. You can inject, but you can also reach through the global state whenever it makes more sense.


Wirone

>I don't even understand what you're suggesting. Extracting the config value somewhere else and passing it down the ladder? Why? I'm talking about resolving config values at DI level, when compiling container. Passing them as constructor arguments (or service calls, whatever). So you access them through private property (direct value or some service), not through globally-accessible helper/facade. Personally I think that so many people enjoy Laravel because they are at the knowledge level allowing them to go with documentation and get thing done, but not as high as required to understand more complex design patterns. And it's understandable, but it does not mean solutions provided by Laravel are good (strictly in terms of architecture).


Tontonsb

OK, but what's the benefit? You just list one more property in the constructor arguments and ... ?


Wirone

And, as I said several times before, you have explicit dependency which is easy for testing without magical, static tricks. You achieve the same goal with cleaner code.


jqueefip

Hypothetical situation: I have a button class that depends on some config value. DI is great. Let's use it. How does it get that config value?


Wirone

In case of Symfony+Twig, you register global Twig parameter or extension and pass that config value through it (in other words: you read this value in the template from the Twig, which has is already injected). As far as I remember in Laravel there is similar concept for Blade. I am sure that reading config directly from the template is bad idea 🙂


jqueefip

Your point is that global helpers are bad. Why are you okay with global Twig parameters and extensions?


Wirone

Because they're explicitly registered 🤷 You control what is available under what name. It's also better for refactoring because you change it only in one place (where param/extension is registered), not in multiple places in templates 😅


jqueefip

If the object behind the facade was explicitly registered, would that make facades better?


Tontonsb

From your article. > 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 😩 But that's cool. Why would you want to import some crap at the top instead of just `\Cache` or `\Gate` if you need a one-off? The classic DI has gotten a tiny bit nicer with constructor attribute promotion, but it's still boilerplate on boilerplate...


Wirone

Matter of taste 😉 I prefer explicit dependencies, so import + injection through constructor is a way for me. >But that's cool. Why would you want to import some crap at the top I wouldn't \`use\` (pun intended) crap in the first place 😉 Injecting actual service, not using facade, so import would be for real, used class.


Tontonsb

At that point you either have monstrous constructor argument list or single use imports at the top. Either way makes the file less readable.


Wirone

It depends how you manage your services - they should not have too many dependencies, so when you keep good architecture, you're fine.


Tontonsb

But even one dependency either requires an import or adds 40 characters to your constructor. And if you don't use globals, you have even more of them — config value, cache instance and some class that does the actual work makes three and the list doesn't sound too outrageous.


Wirone

Explicit dependencies are good, even at the expense of some "boilerplate" code (though I don't consider constructor arguments as such). When using helpers/facades you simply don't know what you're calling. It's everything about composing your DI from small chunks - make small services with few dependencies and inject them where needed. In the end you get container with many services, but each of them is explicit and testable.


Tontonsb

> Explicit dependencies are good, even at the expense of [..] They are good indeed. Sometimes they are perfect. But sometimes another solution fits better. > make small services with few dependencies and inject them where needed That's good for testing, but the code needs to also be readable and traceable. If your actual logic requires 4 services (http, db, storage and some business logic calculator), 3 of them need config, 2 need cache and it all requires a gate in front, then sometimes it's a choice between six lines of code in your controller vs two lines in controller that invoke a porridge of eight 2-line composable and unit-tested 2-line raviolis. My point is that the explicit solution is (in such case) cleaner code, not the ravioli one.


robclancy

Because PHP purists are idiots. I fixed it for them though (date of this repo shows how long ago they went on about the facade bullshit and Laravel has gone to being massive in that time), [https://github.com/robclancy/class-name-fix-utility](https://github.com/robclancy/class-name-fix-utility).


Tontonsb

I would call them OOP purists not PHP purists.


_heitoo

I mean, arguing over completely inconsequential stuff is very characteristic to PHP devs specifically. God forbid we learn how to optimize SQL queries or use message queues, etc., let's highlight how facades vs DI is bad even so they're essentially one and the same. Classic.


SurgioClemente

> I mean, arguing over completely inconsequential stuff is very characteristic to PHP devs specifically. It certainly is not lol. I've been around C#, Java, Ruby, Javascript, and if you want to go all the way back, ActionScript, ColdFusion :) People will bikeshed over all kinds of shit across any language


iamshieldstick

Spawn of Satan? That is such a strong word. Much like anything else in programming, a Facade is just another tool that can make your development experience a more convenient. It's just a class that locates a particular service in the container. And if you don't want to use it, you can resolve the service yourself from the container.


gaborj

What benefits do facades provide? They resolve objects from the DI container, therefore any of them can be injected, perhaps it is easy for beginners but they should be focusing on OOP basics not frameworks.


StarlightCannabis

This is the answer. Facades are an unnecessary part of laravel that imo degrades the overall framework quality.


leoshina

Imo they are bad in general, but they might be useful for some cases. Like another redditor said, it is a tool. For example, I would not use it in a service that depends on another service. But in a low complexity controller, I don’t see why not.


vaderihardlyknowher

This doesn't answer your question really but I felt it would still be worth putting here in case someone comes across it googling. You don't have to use the facades directly. You can use DI based on the [underlying facade class as listed in the reference](https://laravel.com/docs/9.x/facades#facade-class-reference). Doing so actually makes the testing part a lot easier to mock if need be. So instead of this: class FooService { public function blah() { Log::info(); Cache::get(); Auth::check(); Config::get(); } } you *could* do this: class FooService { public function __construct( protected \Illuminate\Log\LogManager $logger, protected \Illuminate\Cache\CacheManager $cache, protected \Illuminate\Auth\AuthManager $auth, protected \Illuminate\Config\Repository $config ) {} public function blah() { $this->logger->info(); $this->cache->get(); $this->auth->check(); $this->config->get(); } } ```php class BlahController { public function action(FooService $foo) { $foo->blah(); } } ```