🔄 Self-reference Laravel Model
Tags: php, laravel, sql, relational
Published: 2023-04-11 09:27:32

# 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](https://github.com/sergiog95) 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](https://www.notion.so/212a95c0f21f4d329c7d5c03cdc7ea8f)

# 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](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](https://laravel.com/docs/7.x/eloquent-relationships#defining-relationships) 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.

```php
class Department extends Model {
	use SelfReferenceTrait;
}
```

## 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.

```php
// 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 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.

```php
// Let's get 'Development'
$department = Department::find(3);

$children = $department->children; // Backend and Frontend
```

## 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! 😄

```php
// 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`.

```php
// 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! 👋