DEV Community

João Brandão
João Brandão

Posted on

🔄 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 1

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 amount 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

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

https://gist.github.com/joaorbrandao/f23c8e4cdf776a2dfe7fd1f75f64f15e

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;
}
Enter fullscreen mode Exit fullscreen mode

The parent method

The parent method defines a belongsTo relationship. This means that a deparment 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
Enter fullscreen mode Exit fullscreen mode

The children method

The children method defines a hasMany relationship. This means that a department can have many other departments bellow it.

children will return a Collection containing all the departments on the next level bellow. In other words, a list of departments that their 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
Enter fullscreen mode Exit fullscreen mode

The allChildren method

The allChildren method takes advantage of the 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

I hope that this is as useful for you as it was for our team! Feel free to use it! 🚀

Cheers! 👋

Top comments (0)