🔄 Self-reference Laravel Model

João Brandão
3 min readFeb 14, 2021

--

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! 👊👏

Photo by Daiga Ellaby on Unsplash

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!

Departments Table

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! 👀

SelfReferenceTrait

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! 👋

--

--