Laravel Eloquent“兄弟姐妹”作为一种关系?

时间:2018-06-14 08:19:55

标签: laravel eloquent

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

在我的设计中,sibling(正如您所料)是一个共享相同parent但不共享的记录(排除当前child)。我怎样才能做到这一点?

这不是Laravel Eloquent Relationships for Siblings的重复,因为1)结构不同,2)我想返回一个关系,而不是查询结果,我知道如何查询这个,但我想要的力量渴望装载机。

3 个答案:

答案 0 :(得分:4)

我不认为你可以用Laravel的内置关系来做到这一点。我建议做的是创建自己的关系类型,扩展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);
    }
}

通过扩展HasMany类并提供您自己的addConstraints实现,您可以控制添加到相关模型查询的内容。通常,Laravel会在这里添加where parent_id = <your model ID>,但我在此处更改了where parent_id = <your model PARENT ID>(如果您的模型parent_idnull它而是添加where parent_id is null)。我还添加了一个额外的子句,以确保调用模型不包含在结果集合中:and id != <your model ID>

您可以在Page模型中使用它:

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

现在你应该可以像这样加载兄弟姐妹了:

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

请注意,我只测试了这个用于检索相关模型,并且在将该关系用于其他目的时可能无效,例如保存相关模型等。

此外,请注意,在上面的示例中,我使用了parent_id代替parent,如您的问题所示。应该是直接交换。

答案 1 :(得分:0)

我不确定它是否适用于您的模型,因为您将相同的对象与中间表相关联。但是,

hasManyThrough()

可以解决这个问题。

“......通过父母有许多兄弟姐妹。”

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

答案 2 :(得分:0)

这是偏离主题的,但我对此表示不满。我对你处理这些关系的方式有这个建议。您不需要PageRelation模型,您可以直接在belongsToMany模型上定义Page关系。此外,你不需要额外的属性parent,这是一种不一致的,定义父母和孩子,只有孩子足以确定父母。因此,您可以在检索关系时反转键,而不是两个单独的列。让我举个例子来说明我的意思:

pages:
keep this table intact

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

然后在模型中定义两个关系:

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

你可以坚持任何对你感觉良好的事物。但是,我觉得这更加一致。因为,只有单一的事实来源。 如果A是B的孩子,那么B必须是A的父母,其显而易见的是,只有&#34; A是B&#34;的孩子。足以陈述&#34; B是A&#34;的父母。

我测试了这个,效果很好。

修改
您可以将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;
    }
}

然后,您可以在siblings型号上添加Page关系方法:

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

注意: 此案例不适用于预先加载,急切加载需要覆盖match类上的addEagerContraintsBelongsToManySiblings方法。您可以查看laravel源代码中的BelongsToMany类,以查看它如何急切加载关系的示例。