首页 > 解决方案 > Laravel 雄辩的“兄弟姐妹”作为关系?

问题描述

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

在我的设计中, asibling是(如您所料)共享相同parent但不共享自身的记录(不包括 current child)。我怎样才能做到这一点?

这不是Laravel Eloquent Relationships for Siblings的副本,因为 1)结构不同,2)我想返回一个关系,而不是查询结果,我知道如何查询这个,但我想要 Eager loader 的强大功能。

标签: laraveleloquent

解决方案


我认为你不能用 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您的问题。不过应该是直接交换。


推荐阅读