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)我想返回一个关系,而不是查询结果,我知道如何查询这个,但我想要的力量渴望装载机。
答案 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_id
是null
它而是添加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
类上的addEagerContraints
和BelongsToManySiblings
方法。您可以查看laravel源代码中的BelongsToMany
类,以查看它如何急切加载关系的示例。