[英]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_id
为null
,它将改为添加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.注:这种情况不工作负载心切,急于负荷需求压倒一切
match
和addEagerContraints
的方法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.