I shouldn't need "real life examples" to explain why using objects with magic dynamic properties instead of declaring the properties on the class is stupid. If I can't look at a model class and know what properties and methods it has, why is it even a class? With all the improvements made to PHP in the past decade, it feels like going back to the days when I was just using associative arrays to represent database objects.
> Have you worked with Laravel in practice?
Yes, I tried using it, was immediately put off by the issues I've described, then tried Symfony and never looked back. For every piece of dumb nonsensical magic Laravel does, you can look at Symfony to see how it should be done properly.
As for properties -- yes, they're not explicitly described but mapped to the database schema. Other ORMs, like that in Ruby on Rails, also do not explicitly set model properties and just map to database columns. People have been upset about this forever, but everyone else has happily used Active Record and had no major issues, all while enjoying not having to add a dozen lines of code to annotate props.
In both Rails and Laravel you can define accessors and setters, either overwriting the magic property for the column or defining new properties.
If not seeing the properties on the model is a huge problem, there are plugins to generate them and put them in the model. My IDE gives me direct insight by inspecting the table.
I'm happy not to have to write out all the props twice when there's no tangible performance issue and I've never had any other problems with dynamically getting the model attributes from the schema.
You're taking a matter of taste and context-based tradeoffs and acting like those reflect unchallengeable principles.
Symfony is a great piece of software. So is Laravel. Github numbers alone will show you how compelling so many developers find Laravel. Of course, there will be haters who think they're all stupid, uneducated, bad, idiotic, lazy, etc.
Everyone makes trade-offs based upon taste or prior decisions. Consider Django. There, you explicitly define properties, but link them to the type of database column they'll use. Then, you can generate a migration based upon the model, and then run that migration. I really like that pattern. Removes tedium of writing migrations, preserves ability to modify the migrations, and makes things explicit.
When I used SQL Alchemy in Python, it completely allowed me to not write migrations, only models. That was very nice, until it wasn't. But then I just had to do a few things manually -- a reasonable price to pay for saving a bunch of time in the other 19/20 of cases.
These are all just different ways of getting to the same place. Each has benefits, each has drawbacks, the extent of each which will depend on the project and the developers' tastes.
And to be clear, Laravel is a piece of Symfony software, and a good example of what you can do with Symfony packages and a bit of opinion.
There are 13 Symfony dependencies in https://github.com/laravel/framework/blob/9.x/composer.json alone, and many of the Laravel packages listed in there also depend on them; the Laravel Request class extends Symfony\Component\HttpFoundation\Request, the Response class extends Symfony\Component\HttpFoundation\Response, etc.
In any event, Laravel has never been shy about its debt to Symfony.
>Consider Django. There, you explicitly define properties, but link them to the type of database column they'll use. Then, you can generate a migration based upon the model, and then run that migration. I really like that pattern. Removes tedium of writing migrations, preserves ability to modify the migrations, and makes things explicit.
This is exactly what Symfony with Doctrine does, and it's the correct way to do it. The benefits vastly outweigh the "drawback" of having to write slightly more code.
In most cases, it is a matter of taste and tradeoffs that vary based upon circumstance.
You can keep saying "this is the correct way" with all the conviction in the world, but it won't turn matters of opinion and circumstance into universal fact.
Separating the data access layer, the models and the business logic (basically a 3-tier architecture) is a widely used and proven industry standard to keep a sane, maintainable and testable codebase.
Active record ORMs like Eloquent mixes everything in a single layer (business logic in the model events, data accesses are done by the models), but as soon as you reach a significant level of complexity, it is unmaintainable.
Even Laravel codebases that grow in complexity often ends-up wrapping Eloquent in a repository layer.
Facades are not global glorified variables with magic methods. Usually they're just a way to instantiate and access a class with less code. People complain that facades limit testability. In fact, they're easily mocked. Etc.
The docs do a very good job of explaining how things work and how you can override any "magic" you may not like. If something is doing something you didn't expect, you probably didn't read the docs. In fact, a delight of working with Laravel is it's so intuitive that you can often guess how to achieve something and be right.
Have you worked with Laravel in practice?