Back to Course |
Laravel 11 Eloquent: Expert Level

Relationship Packages

Now, let's take a look at a few packages related to Eloquent relationships.


Jonas Staudenmeir: Eloquent HasManyDeep

Another package I would like to demonstrate is from Jonas Staudenmeir and is called eloquent-has-many-deep for has many with unlimited levels.

In this example, I have such a structure: Country -> has many -> User -> has many -> Project -> has many -> Transaction. The goal is to calculate each country's SUM of transactions amount columns.

After installing the package via composer, we must add the Staudenmeir\EloquentHasManyDeep\HasRelationships trait to the Model. In this example, it is the Country Model.

use Illuminate\Database\Eloquent\Relations\HasMany;
use Staudenmeir\EloquentHasManyDeep\HasRelationships;
 
class Country extends Model
{
use HasRelationships;
 
public function users(): HasMany
{
return $this->hasMany(User::class);
}
}

Now, we can use the hasManyDeep relationship to get transactions. This is similar to the has many through relationship but one deeper lever.

use Staudenmeir\EloquentHasManyDeep\HasManyDeep;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Staudenmeir\EloquentHasManyDeep\HasRelationships;
 
class Country extends Model
{
use HasRelationships;
 
public function users(): HasMany
{
return $this->hasMany(User::class);
}
 
public function transactions(): HasManyDeep
{
return $this->hasManyDeep(Transaction::class,
[User::class, Project::class],
['country_id', 'owner_id', 'project_id']
);
}
}

In the hasManyDeep() method, you first define the target Model. Then, as a second parameter in an array, you provide the intermediate Models. As a third parameter in an array, you provide primary keys if they differ from the default values.

To get the result, you can eager load a relationship as you would with any native Laravel relationship.

$countries = Country::with('transactions')->get();
 
foreach ($countries as $country) {
print '<div><strong>' . $country->name . '</strong>: ' . $country->transactions->sum('amount') . '</div>';
print '<hr />';
}

In the database, I have added some data to each table.

And in the browser, we have the result:

You can watch a video review of this package on YouTube.

I recommend checking other available relationships in this package by Jonas Staudenmeir and other packages by him.


Tighten: Parental

Another package that helps us with Eloquent is called tighten/parental. The package helps with Single Table Inheritance (STI). It might sound not very easy, but it isn't. It's a relationship to the same table.

For example, you have a users table and want to have admins but don't want a different table for them. Using this package, you create a Model Admin, extend the User model, and add the HasParent trait. By doing that, when you call the Admin Model, Laravel will call the users table instead of the admins table, which would be by default.

app/Models/User.php:

use Parental\HasChildren;
 
// The "parent" Model
class User extends Model
{
use HasChildren;
 
// ...
}

app/Models/Admin.php:

use Parental\HasParent;
 
// The "child" Model
class Admin extends User
{
use HasParent;
 
// ...
}

Type is the only column you must add to the users table.

Schema::table('users', function ($table) {
$table->string('type')->nullable();
});

Add type to the $fillable array on the User Model.

Now, in your Controller or anywhere, you can call the Admin Model, which will get records from the users table.

For more, check the official documentation.


Michael Dyrynda: Cascade Soft Deletes

The last interesting and handy Eloquent package is about cascading soft deletes. The problem is that if you use soft deletes on parent and child relationships if you soft delete the parent record, the child doesn't get automatically soft-deleted.

That happens because soft deletes are not the function of MySQL or database. It's a function of Laravel and Eloquent.

I have added softDeletes() to the User and Task Models. The User Model has many tasks. In the database, I have a user and two tasks that belong to that user.

If using Tinker, I delete this user, it gets soft deleted by Laravel, but the tasks don't.

This is where the michaeldyrynda/laravel-cascade-soft-deletes package comes in help. After installing the package via composer, you must add the CascadeSoftDeletes Trait and provide children relationships in the $cascadeDeletes array property.

app/Models/User.php:

use Illuminate\Database\Eloquent\SoftDeletes;
use Dyrynda\Database\Support\CascadeSoftDeletes;
 
class User extends Authenticatable
{
use HasFactory, Notifiable;
use SoftDeletes, CascadeSoftDeletes;
 
// ...
 
protected array $cascadeDeletes = ['tasks'];
 
// ...
 
public function tasks(): HasMany
{
return $this->hasMany(Task::class);
}
}

Now, after soft deleting the user, tasks are also soft deleted.

For more, read the official documentation.