musoftware/eloquent-has-many-deep

Laravel Eloquent HasManyThrough 关联,支持无限层级

v1.13.1 2020-11-22 18:51 UTC

This package is auto-updated.

Last update: 2024-09-18 12:49:32 UTC


README

CI Code Coverage Scrutinizer Code Quality Latest Stable Version Total Downloads License

介绍

这个扩展版本的 HasManyThrough 允许与无限中间模型建立关系。
它支持 多对多多态 关系及其所有可能的组合。

支持 Laravel 5.5.29+。

安装

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

版本

使用

该包提供了两种定义深度关系的方式
您可以手动指定中间模型、外键和本地键,或者拼接 现有关系

HasMany

考虑以下具有额外层级的 文档示例
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']);
    }
}

hasManyThrough() 类似,hasManyDeep() 的第一个参数是相关模型。第二个参数是从父模型(定义关系的模型)到相关模型的中介模型数组。

默认情况下,hasManyDeep() 使用 Eloquent 的外键和本地键约定。您也可以指定自定义外键作为第三个参数,自定义本地键作为第四个参数

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.
            ]
        );
    }
}

您可以使用 null 代替默认键

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 关系包括在中间路径中。

ManyToMany → HasMany

考虑以下具有额外 HasMany 层级的 文档示例
User → many to many → Role → has many → Permission

将连接表添加到中介模型中

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

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

如果您指定了自定义键,请记住在连接表的“右侧”交换外键和本地键

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 层级的 文档示例
User → many to many → Role → many to many → Permission

将连接表添加到中介模型中

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

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

MorphMany

您可以在中间路径中包括 MorphMany 关系。

考虑以下具有额外层级的 文档示例
User → has many → Post → morph many → Comment

将多态外键指定为数组,以 *_type 列开始

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 关系。

考虑以下具有额外层级的 文档示例
User → has many → Post → morph to many → Tag

将数据透视表添加到中间模型中,并以*_type列为首,将多态外键指定为数组。

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']
        );
    }
}

请记住,在数据透视表的“右侧”交换外键和本地键。

MorphedByMany

您可以在中间路径中包含MorphedByMany关系。

考虑以下具有额外层级的 文档示例
Tag → 多态关联 → Post → 多对一 → Comment

将数据透视表添加到中间模型中,并以*_type列为首,将多态本地键指定为数组。

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关系。
Tag → 多态关联 → Post → 属于 → User

交换外键和本地键。

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']
        );
    }
}

现有关系

您可以通过连接现有关系来定义HasManyDeep关系。

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关系。

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

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

中间和关联数据

使用withIntermediate()检索中间表的属性。

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

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

默认情况下,这将检索表的所有列。请注意,这将执行一个单独的查询以获取列的列表。

您可以将选定的列指定为第二个参数。

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

作为第三个参数,您可以指定一个自定义访问器。

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
}

如果您从多个表中检索数据,您可以使用嵌套访问器。

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
}

对于BelongsToManyMorphToMany/MorphedByMany关系的数据透视表,使用withPivot()

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
}

作为第三个参数,您可以指定一个自定义数据透视模型,作为第四个参数,指定一个自定义访问器。

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
}

表别名

如果您的路径包含相同的模型多次,您可以指定一个表别名。

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

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

在您要别名的模型中使用HasTableAlias特质。

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

对于数据透视表,这需要自定义模型。

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;
}

使用setAlias()在连接现有关系时指定表别名(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');
    }
}

软删除

默认情况下,软删除的中间模型将不包括在结果中。使用withTrashed()包括它们。

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;
}

贡献

请参阅贡献指南行为准则以获取详细信息。