🔄 Self-reference Laravel Model
First things first!
A year or two ago, when I was a Software Engineer at Bosch a colleague of mine came with this amazing solution of a self-reference trait to map relationships inside a single SQL database table. He wrote a small but very effective piece of code and then I just added the ability to change the name of the database column responsible to keep that relationship.
So, all props to Sérgio here! 👊👏
Use case
Big companies tend to have big nested levels of hierarchy organization. So the main goal here was to create a departments
database table to allow people to manage how many nested levels of departments they want. Let's see the example.
- Department
- Section 1
- Team 1
- Team 2
- Section 2
...
You can imagine this scaling up to Country Department, Europe Department, or even if they’d like to create groups of people inside a team with a large number of people. Well, we wanted to provide our end users the freedom to organize their teams as they wish!
Let’s use the next set of data for future examples!
Show me the code!
Let’s dive into the code then! The following Gist contains the Trait that will be used in the models on which we want to apply the self-reference relationship. Take a look! 👀
As you can see, it is a small piece of code but powerful enough to get the job done.
When using Laravel’s model relationship methods they will return the relationship itself (BelongsTo
, HasMany
, ...). But if we access it like a property, Laravel will automatically get us an instance of the related entity!
Let’s make use of our brand new Trait in our Department
model.
class Department extends Model {
use SelfReferenceTrait;
}
The parent method
The parent
method defines a belongsTo
relationship. This means that a department can belong to another one.
parent
will return a Department
instance, when the current department has another department above or it will return null
when there are no more departments above.
// Let's get 'Development'
$department = Department::find(3);$parent = $department->parent; // Product
The children method
The children
method defines a hasMany
relationship. This means that a department can have many other departments below it.
children
will return a Collection
containing all the departments on the next level below. In other words, a list of departments that parent_id
equals the id
of the current model. If there are no departments above, then the collection returned will be empty.
// Let's get 'Development'
$department = Department::find(3);$children = $department->children; // Backend and Frontend
The allChildren method
The allChildren
method takes advantage of Laravel's relationship method with
to eager load a nested relationship. But in this case, instead of redefining the relationship inside our new method, we use the already defined children
method to get the next nested level and then eager load our own allChildren
relationship, again! "Say whaaat?!" 🙃
Well, let me make it easier: we loop through all nested levels until all children are loaded! 😄
// Let's get 'Backend'
$department = Department::find(5);$allChildren = $department->allChildren; // Develpment > Backend and Frontend
The root method
The root
method is pretty simple: it checks if the current department belongs to any other and if yes, then it repeats that process until it finds the record on that tree that has a parent_id
value of null
.
// Let's get 'Backend'
$department = Department::find(5);$root = $department->root; // Product
I hope that this is as useful for you as it was for our team! Feel free to use it! 🚀
Cheers! 👋