我经常有两个1:x关系的模型:
class Child extends Model
{
public function parent()
{
return $this->belongsTo(Parent::class);
}
}
现在,有时我已经有一个相关模型的实例在一个函数中:
function f(Parent $parent): Child
{
// ...
$child = $parent->createChild();
// ...
return $child
}
另一个函数以Child
为参数,需要Parent
类的属性:
function g(Child $child)
{
$child->update([
'attribute' => h($child->parent->attribute)
]);
}
现在,如果使用从f
返回的Child
实例调用g,那么将从DB检索两次Parent
实例。除非我们添加Parent
参数并将实例传递给g
,或者按照如下方式修改f
:
function f(Parent $parent): Child
{
// ...
$child = $parent->createChild();
// ...
$child->parent = $parent;
return $child
}
我更喜欢第二种方法,而不是添加另一个参数,因为调用g
的上下文可能没有访问Parent
实例的权限。而且通常效果很好。但是,它在g
中的update语句上失败了:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'parent' in 'field list' (SQL: update `children`...
我的问题是:
是否有一种方法可以插入一个现有的模型实例到适当的关系中,而不需要模型将分配注册为列更新?
调用$child->parent = $parent
尝试设置parent
字段属性在$child
对象上。但是,您希望设置parent
关系属性。为此,您需要使用setRelation()
方法:
function f(Parent $parent): Child
{
// ...
$child = $parent->createChild();
// ...
$child->setRelation('parent', $parent);
return $child;
}
然而,在这一点上,您需要注意不要创建循环引用。如果将$parent->child
关系设置为$child
实例,然后将$child->parent
关系设置为$parent
实例,则创建了一个循环引用,如果试图将任一实例转换为json(或数组),该引用将爆炸。
也就是说,如果$parent->child->parent === $parent
,那么存在一个循环引用,并且为它创建json/数组将是一个无限循环。
在这种情况下,如果您想要特别小心,您可以使用withoutRelations()
方法将关系分配给父节点的克隆,该克隆不会加载任何关系。
function f(Parent $parent): Child
{
// ...
$child = $parent->createChild();
// ...
$child->setRelation('parent', $parent->withoutRelations());
return $child;
}
在此之后,$parent->is($parent->child->parent)
将为真,但$parent->child->parent === $parent
将为假,因此没有循环引用。