简体   繁体   English

Laravel Eloquent “兄弟姐妹”是一种关系吗?

[英]Laravel Eloquent “siblings” as a relationship?

class PageRelation extends Eloquent
{
    public $incrementing = false;
    public $timestamps = false;
    protected $table = 'page_relation';
    protected $casts = [
            'parent' => 'int', // FK to page
            'child' => 'int',  // FK to page
            'lpc' => 'int',
        ];

    protected $fillable = [
            'lpc',
        ];

    public function children()
    {
        return $this->hasMany(Page::class, 'category_id', 'child');
    }

    public function parents()
    {
        return $this->hasMany(Page::class, 'category_id', 'parent');
    }

    public function siblings()
    {
        // ...  return $this->hasMany(Page::class ...
        // how do I define this relationship?
    }
}

In my design a sibling is (as you might expect) a record that shares the same parent but not itself (exclude current child ).在我的设计中, sibling是(如您所料)共享同一parent但不共享自身(排除当前child )的记录。 How can I achieve this?我怎样才能做到这一点?

This is not a duplicate of Laravel Eloquent Relationships for Siblings because 1) the structure is different, 2) I would like to return a relationship, not a query result, I know how to query this, but I want the power of eager loader.这不是Laravel Eloquent Relationships for Siblings的副本,因为 1) 结构不同,2) 我想返回一个关系,而不是一个查询结果,我知道如何查询这个,但我想要 Eager loader 的力量。

I don't think you can do that with Laravel's in-built relations.我不认为你可以用 Laravel 的内置关系来做到这一点。 What I would suggest doing is creating your own relation type that extends HasMany and use that.我建议做的是创建自己的关系类型,扩展HasMany并使用它。

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class HasManySiblings extends HasMany
{    
    public function addConstraints()
    {
        if (static::$constraints) {
            if (is_null($foreignKeyValue = $this->getParentKey())) {
                $this->query->whereNull($this->foreignKey);
            } else {
                $this->query->where($this->foreignKey, '=', $foreignKeyValue);
                $this->query->whereNotNull($this->foreignKey);
            }

            $this->query->where($this->localKey, '!=', $this->parent->getAttribute($this->localKey));
        }
    }

    public function getParentKey()
    {
        return $this->parent->getAttribute($this->foreignKey);
    }
}

By extending the HasMany class and providing your own implementation of addConstraints you are able to control what gets added to the query for related models.通过扩展HasMany类并提供您自己的addConstraints实现,您可以控制添加到相关模型查询中的内容。 Usually, what Laravel would do here is add where parent_id = <your model ID> but I've changed it here to add where parent_id = <your model PARENT ID> (if your model's parent_id is null it will instead add where parent_id is null ).通常,Laravel 在这里会做的是添加where parent_id = <your model ID>但我已经在此处更改为添加where parent_id = <your model PARENT ID> (如果您的模型的parent_idnull ,它将改为添加where parent_id is null )。 I've also added an extra clause to ensure that the calling model is not included in the resulting collection: and id != <your model ID> .我还添加了一个额外的子句以确保调用模型不包含在结果集合中: and id != <your model ID>

You can use it like this in your Page model:您可以在Page模型中像这样使用它:

class Page extends Model
{
    public function siblings()
    {
        return new HasManySiblings(
            $this->newRelatedInstance(Page::class)->newQuery(), $this, 'parent_id', 'id'
        );
    }
}

Now you should be able to load the siblings like this:现在你应该能够像这样加载兄弟姐妹:

$page = Page::find(1);
dd($page->siblings);

Please note though, I have only tested this for retrieving related models and it may not work when using the relation for other purposes such as saving related models etc.不过请注意,我只测试了这个以检索相关模型,当将关系用于其他目的(例如保存相关模型等)时,它可能不起作用。

Also, please note that in my examples above I've used parent_id instead of parent as in your question.另外,请注意,在我上面的示例中,我在您的问题中使用了parent_id而不是parent Should be straight swap though.不过应该是直接交换。

I am not sure if it works with your model which is kinda marginal because you are relating same objects with a middle table.我不确定它是否适用于您的模型,这有点边缘,因为您将相同的对象与中间表相关联。 But,但,

hasManyThrough()

could be a solution for this.可能是一个解决方案。

"... has many siblings through parent." “……通过父母有很多兄弟姐妹。”

https://laravel.com/docs/5.6/eloquent-relationships#has-many-through https://laravel.com/docs/5.6/eloquent-relationships#has-many-through

This is off-topic but bare me with this.这是题外话,但让我感到厌烦。 I have this suggestion for the way you are handling these relations.我对你处理这些关系的方式有这个建议。 You don't need PageRelation model, you can define belongsToMany relation on Page model directly.你不需要PageRelation模型,你可以直接在Page模型上定义belongsToMany关系。 Moreover, you dont need extra attribute parent , this is kind of inconsistent, defining parent and child both, only children are enough to determine parents.此外,您不需要额外的属性parent ,这是一种不一致的,同时定义 parent 和 child ,只有 children 足以确定父母。 So Instead of two seperate columns, You can reverse the keys when you retrieve the relation.因此,您可以在检索关系时反转键,而不是两个单独的列。 Let me show you with an example what I mean:让我用一个例子向你展示我的意思:

pages:
keep this table intact

pages_relation:
- id
- page_id (foreign key to id on page)
- child_id (foreign key to id on page)

And then define two relations in your model:然后在您的模型中定义两个关系:

class Page extends Model
{
    public function children()
    {
        return $this->belongsToMany('App\Page', 'pages_relation', 'page_id', 'child_id');
    }

    public function parents()
    {
        return $this->belongsToMany('App\Page', 'pages_relation', 'child_id', 'page_id');
    }
}

You can stick to whatever feels good to you.你可以坚持任何让你感觉良好的事情。 But, I feel this is more consistent.但是,我觉得这更一致。 As, there is only single source of truth.因为,只有单一的事实来源。 If A is a child of B, then B has to be a parent of A, its obvious, only "A is child of B" is enough to state "B is a parent of A".如果 A 是 B 的孩子,那么 B 必须是 A 的父母,很明显,只有“A 是 B 的孩子”就足以说明“B 是 A 的父母”。

I have tested this, it works very well.我已经测试过这个,效果很好。

EDIT编辑
You can extend BelongsToMany relation to get BelongsToManySiblings realtionship, and just override the addWhereConstraints method.您可以扩展BelongsToMany关系以获得BelongsToManySiblings关系,只需覆盖addWhereConstraints方法。

class BelongsToManySiblings extends BelongsToMany
{
    protected function addWhereConstraints()
    {
        $parentIds = \DB::table($this->table)
            ->select($this->foreignPivotKey)
            ->where($this->relatedPivotKey, '=', $this->parent->{$this->parentKey})
            ->get()->pluck($this->foreignPivotKey)->toArray();

        $this->query->whereIn(
            $this->getQualifiedForeignPivotKeyName(),
            $parentIds
        )->where(
            $this->getQualifiedRelatedPivotKeyName(),
            '<>',
            $this->parent->{$this->parentKey}
        )->groupBy($this->getQualifiedRelatedPivotKeyName());

        return $this;
    }
}

Then you can add siblings relationship method on your Page model:然后你可以在你的Page模型上添加siblings关系方法:

public function siblings()
{
    return new BelongsToManySiblings(
        $this->newRelatedInstance(Page::class)->newQuery(),
        $this,
        'pages_relation',
        'parent_id',
        'child_id',
        'id',
        'id',
        $this->guessBelongsToManyRelation()
    );
}

Note: This case does not work for eager loads, eager load needs overriding match and addEagerContraints methods on the BelongsToManySiblings class.注:这种情况不工作负载心切,急于负荷需求压倒一切matchaddEagerContraints的方法BelongsToManySiblings类。 You can peek the BelongsToMany class on laravel source to see an example how it eager loads the relations.您可以偷看BelongsToMany上laravel源类查看示例如何渴望负荷的关系。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM