PHPackages                             musoftware/eloquent-has-many-deep - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. musoftware/eloquent-has-many-deep

ActiveLibrary[Database &amp; ORM](/categories/database)

musoftware/eloquent-has-many-deep
=================================

Laravel Eloquent HasManyThrough relationships with unlimited levels

v1.13.1(5y ago)06MITPHPPHP ^7.3|^8.0

Since Jun 26Pushed 5y agoCompare

[ Source](https://github.com/musoftware/eloquent-has-many-deep)[ Packagist](https://packagist.org/packages/musoftware/eloquent-has-many-deep)[ Fund](https://paypal.me/JonasStaudenmeir)[ RSS](/packages/musoftware-eloquent-has-many-deep/feed)WikiDiscussions master Synced yesterday

READMEChangelogDependencies (4)Versions (19)Used By (0)

[![CI](https://github.com/staudenmeir/eloquent-has-many-deep/workflows/CI/badge.svg)](https://github.com/staudenmeir/eloquent-has-many-deep/workflows/CI/badge.svg)[![Code Coverage](https://camo.githubusercontent.com/d24dfa384db4fafaf1ec3463e7855a7e5a009af21270355101b231fa0e02d0c6/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f7374617564656e6d6569722f656c6f7175656e742d6861732d6d616e792d646565702f6261646765732f636f7665726167652e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/staudenmeir/eloquent-has-many-deep/?branch=master)[![Scrutinizer Code Quality](https://camo.githubusercontent.com/3c4909eff44e7a391a1a9443e4ca03a86722c39ab0b792c72433d2d0be20360f/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f7374617564656e6d6569722f656c6f7175656e742d6861732d6d616e792d646565702f6261646765732f7175616c6974792d73636f72652e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/staudenmeir/eloquent-has-many-deep/?branch=master)[![Latest Stable Version](https://camo.githubusercontent.com/d74d34d0697797ef953cbb7154fbd4af41081bb3e8a7299f7308e719e38d3e63/68747470733a2f2f706f7365722e707567782e6f72672f7374617564656e6d6569722f656c6f7175656e742d6861732d6d616e792d646565702f762f737461626c65)](https://packagist.org/packages/staudenmeir/eloquent-has-many-deep)[![Total Downloads](https://camo.githubusercontent.com/26783768c823e58afce42d8929a9c3dc59b81f9fdcf90692dacd48329137c4d2/68747470733a2f2f706f7365722e707567782e6f72672f7374617564656e6d6569722f656c6f7175656e742d6861732d6d616e792d646565702f646f776e6c6f616473)](https://packagist.org/packages/staudenmeir/eloquent-has-many-deep)[![License](https://camo.githubusercontent.com/3245ee1f73111c41d6352d38859ff61fdc85828e63040f7855f4410106f0dcc3/68747470733a2f2f706f7365722e707567782e6f72672f7374617564656e6d6569722f656c6f7175656e742d6861732d6d616e792d646565702f6c6963656e7365)](https://packagist.org/packages/staudenmeir/eloquent-has-many-deep)

Introduction
------------

[](#introduction)

This extended version of `HasManyThrough` allows relationships with unlimited intermediate models.
It supports [many-to-many](#manytomany) and [polymorphic](#morphmany) relationships and all their possible combinations.

Supports Laravel 5.5.29+.

Installation
------------

[](#installation)

```
composer require staudenmeir/eloquent-has-many-deep:"^1.7"

```

Versions
--------

[](#versions)

LaravelPackage5.5–5.71.75.81.86.x1.117.x1.128.x1.13Usage
-----

[](#usage)

- [HasMany](#hasmany)
- [ManyToMany](#manytomany)
- [MorphMany](#morphmany)
- [MorphToMany](#morphtomany)
- [MorphedByMany](#morphedbymany)
- [BelongsTo](#belongsto)
- [Existing Relationships](#existing-relationships)
- [HasOneDeep](#hasonedeep)
- [Intermediate and Pivot Data](#intermediate-and-pivot-data)
- [Table Aliases](#table-aliases)
- [Soft Deleting](#soft-deleting)

The package offers two ways of defining deep relationships:
You can specify the intermediate models, foreign and local keys manually or concatenate [existing relationships](#existing-relationships).

### HasMany

[](#hasmany)

Consider the [documentation example](https://laravel.com/docs/eloquent-relationships#has-many-through) with an additional level:
`Country` → has many → `User` → has many → `Post` → has many → `Comment`

```
class Country extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function comments()
    {
        return $this->hasManyDeep('App\Comment', ['App\User', 'App\Post']);
    }
}
```

Just like with `hasManyThrough()`, the first argument of `hasManyDeep()` is the related model. The second argument is an array of intermediate models, from the far parent (the model where the relationship is defined) to the related model.

By default, `hasManyDeep()` uses the Eloquent conventions for foreign and local keys. You can also specify custom foreign keys as the third argument and custom local keys as the fourth argument:

```
class Country extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function comments()
    {
        return $this->hasManyDeep(
            'App\Comment',
            ['App\User', 'App\Post'], // Intermediate models, beginning at the far parent (Country).
            [
               'country_id', // Foreign key on the "users" table.
               'user_id',    // Foreign key on the "posts" table.
               'post_id'     // Foreign key on the "comments" table.
            ],
            [
              'id', // Local key on the "countries" table.
              'id', // Local key on the "users" table.
              'id'  // Local key on the "posts" table.
            ]
        );
    }
}
```

You can use `null` placeholders for default keys:

```
class Country extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function comments()
    {
        return $this->hasManyDeep('App\Comment', ['App\User', 'App\Post'], [null, 'custom_user_id']);
    }
}
```

### ManyToMany

[](#manytomany)

You can include `ManyToMany` relationships in the intermediate path.

#### ManyToMany → HasMany

[](#manytomany--hasmany)

Consider the [documentation example](https://laravel.com/docs/eloquent-relationships#many-to-many) with an additional `HasMany` level:
`User` → many to many → `Role` → has many → `Permission`

Add the pivot table to the intermediate models:

```
class User extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function permissions()
    {
        return $this->hasManyDeep('App\Permission', ['role_user', 'App\Role']);
    }
}
```

If you specify custom keys, remember to swap the foreign and local key on the "right" side of the pivot table:

```
class User extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function permissions()
    {
        return $this->hasManyDeep(
            'App\Permission',
            ['role_user', 'App\Role'], // Intermediate models and tables, beginning at the far parent (User).
            [
               'user_id', // Foreign key on the "role_user" table.
               'id',      // Foreign key on the "roles" table (local key).
               'role_id'  // Foreign key on the "permissions" table.
            ],
            [
              'id',      // Local key on the "users" table.
              'role_id', // Local key on the "role_user" table (foreign key).
              'id'       // Local key on the "roles" table.
            ]
        );
    }
}
```

#### ManyToMany → ManyToMany

[](#manytomany--manytomany)

Consider the [documentation example](https://laravel.com/docs/eloquent-relationships#many-to-many) with an additional `ManyToMany` level:
`User` → many to many → `Role` → many to many → `Permission`

Add the pivot table to the intermediate models:

```
class User extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function permissions()
    {
        return $this->hasManyDeep('App\Permission', ['role_user', 'App\Role', 'permission_role']);
    }
}
```

### MorphMany

[](#morphmany)

You can include `MorphMany` relationships in the intermediate path.

Consider the [documentation example](https://laravel.com/docs/eloquent-relationships#polymorphic-relations) with an additional level:
`User` → has many → `Post` → morph many → `Comment`

Specify the polymorphic foreign keys as an array, starting with the `*_type` column:

```
class User extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function postComments()
    {
        return $this->hasManyDeep(
            'App\Comment',
            ['App\Post'],
            [null, ['commentable_type', 'commentable_id']]
        );
    }
}
```

### MorphToMany

[](#morphtomany)

You can include `MorphToMany` relationships in the intermediate path.

Consider the [documentation example](https://laravel.com/docs/eloquent-relationships#many-to-many-polymorphic-relations) with an additional level:
`User` → has many → `Post` → morph to many → `Tag`

Add the pivot table to the intermediate models and specify the polymorphic foreign keys as an array, starting with the `*_type` column:

```
class User extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function postTags()
    {
        return $this->hasManyDeep(
            'App\Tag',
            ['App\Post', 'taggables'],
            [null, ['taggable_type', 'taggable_id'], 'id'],
            [null, null, 'tag_id']
        );
    }
}
```

Remember to swap the foreign and local key on the "right" side of the pivot table:

### MorphedByMany

[](#morphedbymany)

You can include `MorphedByMany` relationships in the intermediate path.

Consider the [documentation example](https://laravel.com/docs/eloquent-relationships#many-to-many-polymorphic-relations) with an additional level:
`Tag` → morphed by many → `Post` → has many → `Comment`

Add the pivot table to the intermediate models and specify the polymorphic local keys as an array, starting with the `*_type` column:

```
class Tag extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function postComments()
    {
        return $this->hasManyDeep(
            'App\Comment',
            ['taggables', 'App\Post'],
            [null, 'id'],
            [null, ['taggable_type', 'taggable_id']]
        );
    }
}
```

### BelongsTo

[](#belongsto)

You can include `BelongsTo` relationships in the intermediate path:
`Tag` → morphed by many → `Post` → belongs to → `User`

Swap the foreign and local key:

```
class Tag extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function postAuthors()
    {
        return $this->hasManyDeep(
            'App\User',
            ['taggables', 'App\Post'],
            [null, 'id', 'id'],
            [null, ['taggable_type', 'taggable_id'], 'user_id']
        );
    }
}
```

### Existing Relationships

[](#existing-relationships)

You can also define a `HasManyDeep` relationship by concatenating existing relationships:

```
class Country extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function comments()
    {
        return $this->hasManyDeepFromRelations($this->posts(), (new Post)->comments());
    }

    public function posts()
    {
        return $this->hasManyThrough('App\Post', 'App\User');
    }
}

class Post extends Model
{
    public function comments()
    {
        return $this->hasMany('App\Comment');
    }
}
```

### HasOneDeep

[](#hasonedeep)

Define a `HasOneDeep` relationship if you only want to retrieve a single related instance:

```
class Country extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function latestComment()
    {
        return $this->hasOneDeep('App\Comment', ['App\User', 'App\Post'])
            ->latest('comments.created_at');
    }
}
```

### Intermediate and Pivot Data

[](#intermediate-and-pivot-data)

Use `withIntermediate()` to retrieve attributes from intermediate tables:

```
public function comments()
{
    return $this->hasManyDeep('App\Comment', ['App\User', 'App\Post'])
        ->withIntermediate('App\Post');
}

foreach ($country->comments as $comment) {
    // $comment->post->title
}
```

By default, this will retrieve all the table's columns. Be aware that this executes a separate query to get the list of columns.

You can specify the selected columns as the second argument:

```
public function comments()
{
    return $this->hasManyDeep('App\Comment', ['App\User', 'App\Post'])
        ->withIntermediate('App\Post', ['id', 'title']);
}
```

As the third argument, you can specify a custom accessor:

```
public function comments()
{
    return $this->hasManyDeep('App\Comment', ['App\User', 'App\Post'])
        ->withIntermediate('App\Post', ['id', 'title'], 'accessor');
}

foreach ($country->comments as $comment) {
    // $comment->accessor->title
}
```

If you retrieve data from multiple tables, you can use nested accessors:

```
public function comments()
{
    return $this->hasManyDeep('App\Comment', ['App\User', 'App\Post'])
        ->withIntermediate('App\Post')
        ->withIntermediate('App\User', ['*'], 'post.user');
}

foreach ($country->comments as $comment) {
    // $comment->post->title
    // $comment->post->user->name
}
```

Use `withPivot()` for the pivot tables of `BelongsToMany` and `MorphToMany`/`MorphedByMany` relationships:

```
public function permissions()
{
    return $this->hasManyDeep('App\Permission', ['role_user', 'App\Role'])
        ->withPivot('role_user', ['expires_at']);
}

foreach ($user->permissions as $permission) {
    // $permission->role_user->expires_at
}
```

You can specify a custom pivot model as the third argument and a custom accessor as the fourth:

```
public function permissions()
{
    return $this->hasManyDeep('App\Permission', ['role_user', 'App\Role'])
        ->withPivot('role_user', ['expires_at'], 'App\RoleUser', 'pivot');
}

foreach ($user->permissions as $permission) {
    // $permission->pivot->expires_at
}
```

### Table Aliases

[](#table-aliases)

If your relationship path contains the same model multiple times, you can specify a table alias:

```
class Post extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function commentReplies()
    {
        return $this->hasManyDeep('App\Comment', ['App\Comment as alias'], [null, 'parent_id']);
    }
}
```

Use the `HasTableAlias` trait in the models you are aliasing:

```
class Comment extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasTableAlias;
}
```

For pivot tables, this requires custom models:

```
class User extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function permissions()
    {
        return $this->hasManyDeep('App\Permission', ['App\RoleUser as alias', 'App\Role']);
    }
}

class RoleUser extends Pivot
{
    use \Staudenmeir\EloquentHasManyDeep\HasTableAlias;
}
```

Use `setAlias()` to specify a table alias when concatenating existing relationships (Laravel 6+):

```
class Post extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function commentReplies()
    {
        return $this->hasManyDeepFromRelations(
            $this->comments(),
            (new Comment)->setAlias('alias')->replies()
        );
    }

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}

class Comment extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasTableAlias;

    public function replies()
    {
        return $this->hasMany(self::class, 'parent_id');
    }
}
```

### Soft Deleting

[](#soft-deleting)

By default, soft-deleted intermediate models will be excluded from the result. Use `withTrashed()` to include them:

```
class Country extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function comments()
    {
        return $this->hasManyDeep('App\Comment', ['App\User', 'App\Post'])
            ->withTrashed('users.deleted_at');
    }
}

class User extends Model
{
    use SoftDeletes;
}
```

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](.github/CONTRIBUTING.md) and [CODE OF CONDUCT](.github/CODE_OF_CONDUCT.md) for details.

###  Health Score

31

—

LowBetter than 68% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity4

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity76

Established project with proven stability

 Bus Factor1

Top contributor holds 94.6% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~57 days

Recently: every ~128 days

Total

18

Last Release

1909d ago

PHP version history (7 changes)v1.0PHP &gt;=7.0

v1.8PHP ^7.1.3

v1.9PHP ^7.2

v1.12PHP ^7.2.5

v1.13PHP ^7.3

v1.13.1PHP ^7.3|^8.0

1.12.x-devPHP ^7.2.5|^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/a54722bbb289cee8229f0d071e5c39367e2a43ecd018c66599de880fa4c770eb?d=identicon)[musoftware](/maintainers/musoftware)

---

Top Contributors

[![staudenmeir](https://avatars.githubusercontent.com/u/1853169?v=4)](https://github.com/staudenmeir "staudenmeir (70 commits)")[![niels-numbers](https://avatars.githubusercontent.com/u/1765602?v=4)](https://github.com/niels-numbers "niels-numbers (2 commits)")[![musoftware](https://avatars.githubusercontent.com/u/4246716?v=4)](https://github.com/musoftware "musoftware (1 commits)")[![thijsdejong](https://avatars.githubusercontent.com/u/25135566?v=4)](https://github.com/thijsdejong "thijsdejong (1 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/musoftware-eloquent-has-many-deep/health.svg)

```
[![Health](https://phpackages.com/badges/musoftware-eloquent-has-many-deep/health.svg)](https://phpackages.com/packages/musoftware-eloquent-has-many-deep)
```

###  Alternatives

[owen-it/laravel-auditing

Audit changes of your Eloquent models in Laravel

3.4k33.0M95](/packages/owen-it-laravel-auditing)[staudenmeir/eloquent-json-relations

Laravel Eloquent relationships with JSON keys

1.1k5.8M24](/packages/staudenmeir-eloquent-json-relations)[bavix/laravel-wallet

It's easy to work with a virtual wallet.

1.3k1.1M11](/packages/bavix-laravel-wallet)[dragon-code/migrate-db

Easy data transfer from one database to another

15717.4k](/packages/dragon-code-migrate-db)[gearbox-solutions/eloquent-filemaker

A package for getting FileMaker records as Eloquent models in Laravel

6454.8k2](/packages/gearbox-solutions-eloquent-filemaker)[cybercog/laravel-ownership

Laravel Ownership simplify management of Eloquent model's owner.

9126.6k3](/packages/cybercog-laravel-ownership)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
