[READ ONLY] Subtree split of the Illuminate Database component (see laravel/framework)
Illuminate Database
The Illuminate Database component is a full database toolkit for PHP, providing an expressive query builder, ActiveRecord style ORM, and schema builder. It currently supports MySQL, Postgres, SQL Server, and SQLite. It also serves as the database layer of the Laravel PHP framework.
Usage Instructions
First, create a new "Capsule" manager instance. Capsule aims to make configuring the library for usage outside of the Laravel framework as easy as possible.
The Illuminate Database component is a full database toolkit for PHP, providing an expressive query builder, ActiveRecord style ORM, and schema builder. It currently supports MySQL, Postgres, SQL Server, and SQLite. It also serves as the database layer of the Laravel PHP framework.
Part of this toolkit available is called Eloquent, which is the ORM for the Laravel Framework.
tl;dr, it sucks because...
Active Record itself sucks
their actual implementation of Active Record is also bad and exposes a lot
models represent a table row, but can do so much beyond that
there's like 10 different ways to access model properties
expectations aren't managed around null vs '0' vs false
the illuminate/database code quality is low
mutators are gross (get<X>Attribute is unobviously a proxy method)
and of course it sucks because artisan code comes at an unknown cost to the unsuspecting
Bad Active Record implementation
For those unfamiliar with the design pattern, Active Record is a design pattern commonly associated with ORMs for relational databases.
It's often seen as subjective to dislike Active Record, but it objectively mixes concerns; which is something all developers should at very least understand what they're trading off.
Even better, Laravel's implementation of Active Record publicly exposes implementation details which is unnecessarily encouraging poor development choices to the unaware.
<?phpnamespaceApp\Models;useIlluminate\Database\Eloquent\Model;classUserextendsModel{/**
* Get the phone associated with the user.
*/publicfunctionphone(){return$this->hasOne(Phone::class);}}
$user=User::find(1);$phone=$user->phone;
^ Standard innocent usage of Eloquent, fetching a related model.
In order for $user to resolve the relational 1:1 model Phone, it needs to perform a database query. Again, standard for Active Record - but in other words, $user has been tightly coupled with your actual database connection.
With that in mind, and a single Eloquent model ($user) here's a list of functionality you can perform directly from your model instance:
You could... create a fresh QueryBuilder for subsequent unrelated queries
obviously no one will ever do this (please don't). The point is this functionality should not be exposed.
Active Record is bad enough without some unstable APIs sprinkled on top.
I'm not going to start on Entity Mapping vs Active Record today, but if you value testability and separating concerns I would recommend Laravel-doctrine.
The class Model has 112 public methods and attributes.
The class Builder has 1653 lines of code.
The class Connection has 35 public methods.
The class Connection has 43 non-getter and setter-methods.
The class Connection has 70 public methods and attributes.
The class Connection has 1385 lines of code.
The method addCastAttributesToArray() has an NPath complexity of 865.
The method withAggregate() has an NPath complexity of 1304.
The class Factory has 37 non-getter- and setter-methods.
... and much much more.
don't forget the many magic method usages too.
I would strongly recommend looking into PHPMD, and what NPath & cyclomatic complexity means if it's new to you.
Eloquent and associated database code is essentially complex, hard to work with bloated code. On a high level what this means for framework users is that your underlying database will find it hard to introduce more functionality, and find it hard to improve existing functionality, but will also be more likely to introduce new bugs, because complexity is already high.
useIlluminate\Database\Eloquent\Model;classPersonextendsModel{/**
* The table associated with the model.
*
* @var string
*/protected$table='Persons';publicfunctiongetIsOverweight($value){return!empty($value)?bool($value):null;}}
Laravel just miraculously happens know how to resolve the values from table columns.
Do you feel safe enough to trust Laravel will run the mutator (type cast '1' to true) with all of these methods? A sane person would say no, I don't trust like that.
Mutators themselves deeper the wound of model accessors, not knowing what types to expect, and handling null vs 0, etc.
Allowing such dynamic functionality, and duplicate ways to 'get' something is another hidden cost that may be forever unobvious to those choosing to stick within the Laravel ecosystem.
Redaction
After writing this post I came across a tweet by Laravel creator, which actually justifies some of my pain points