通过关系强制创建 eloquent 模型
Force create an eloquent model through a relationship
在一个使用 Laravel 5.3 和 Stripe 的小项目中,我试图通过 hasOne
关系在 User
上强制创建 Subscription
:
// User.php
public function subscription() {
return $this->hasOne('App\Subscription');
}
public function subscribe($data) {
return $this->subscription()->forceCreate(
// $data contains some guarded fields;
// create() will simply ignore them...
);
}
但是,我得到:
Call to undefined method Illuminate\Database\Query\Builder::forceCreate()
即使 forceCreate()
是 valid Eloquent method。
有什么办法可以模拟这种行为吗?或者我应该 save
a Subscription
手动分配每个字段?复杂的是某些字段应该保留 guarded
,例如stripe_id
.
编辑
我的快速解决方案:
// User.php @ subscribe($data)
return (new Subscription())
->forceFill([
'user_id' => $this->id,
// $data with sensitive guarded data
])
->save();
我相信还有更好的方法!
调用 $this->subscriptions()
returns HasMany
class, not a Model
的一个实例。
与create
and createMany
, there's no forceCreate()
implemented in HasOneOrMany
相反class。因此,在这种情况下没有第一手的 API 可以使用。
看到了吗?当您调用 $this->subscriptions()->create()
时,您是在 HasMany
实例而不是 Model
实例上调用 create
方法。事实上 Model
class 有一个 forceCreate
方法,与此无关。
您可以使用 getRelated()
方法获取相关模型,然后对其调用 forceCreate()
或 unguarded()
:
public function subscribe($data) {
return $this->subscription()
->getRelated()
->forceCreate($data);
}
但是这种方法有一个严重的缺点;当我们从关系中提取模型时,它不会设置关系。要解决它,您可以说:
public function subscribe($data)
{
// $data['user_id'] = $this->id;
// Or more generally:
$data[$this->courses()->getPlainForeignKey()] = $this->courses()->getParentKey();
return $this->courses()
->getRelated()
->forceCreate($data);
}
呃,好像太hacky了,不是我的做法。我更喜欢解除底层模型的保护,调用 create
然后重新保护它。大致如下:
public function subscribe($data)
{
$this->courses()->getRelated()->unguard();
$created = $this->courses()->create($data);
$this->courses()->getRelated()->reguard();
return $created;
}
这样,您就不必手动设置外键了。
laravel/internals相关讨论:
[PROPOSAL] Force create model through a relationship
在一个使用 Laravel 5.3 和 Stripe 的小项目中,我试图通过 hasOne
关系在 User
上强制创建 Subscription
:
// User.php
public function subscription() {
return $this->hasOne('App\Subscription');
}
public function subscribe($data) {
return $this->subscription()->forceCreate(
// $data contains some guarded fields;
// create() will simply ignore them...
);
}
但是,我得到:
Call to undefined method Illuminate\Database\Query\Builder::forceCreate()
即使 forceCreate()
是 valid Eloquent method。
有什么办法可以模拟这种行为吗?或者我应该 save
a Subscription
手动分配每个字段?复杂的是某些字段应该保留 guarded
,例如stripe_id
.
编辑
我的快速解决方案:
// User.php @ subscribe($data)
return (new Subscription())
->forceFill([
'user_id' => $this->id,
// $data with sensitive guarded data
])
->save();
我相信还有更好的方法!
调用 $this->subscriptions()
returns HasMany
class, not a Model
的一个实例。
与create
and createMany
, there's no forceCreate()
implemented in HasOneOrMany
相反class。因此,在这种情况下没有第一手的 API 可以使用。
看到了吗?当您调用 $this->subscriptions()->create()
时,您是在 HasMany
实例而不是 Model
实例上调用 create
方法。事实上 Model
class 有一个 forceCreate
方法,与此无关。
您可以使用 getRelated()
方法获取相关模型,然后对其调用 forceCreate()
或 unguarded()
:
public function subscribe($data) {
return $this->subscription()
->getRelated()
->forceCreate($data);
}
但是这种方法有一个严重的缺点;当我们从关系中提取模型时,它不会设置关系。要解决它,您可以说:
public function subscribe($data)
{
// $data['user_id'] = $this->id;
// Or more generally:
$data[$this->courses()->getPlainForeignKey()] = $this->courses()->getParentKey();
return $this->courses()
->getRelated()
->forceCreate($data);
}
呃,好像太hacky了,不是我的做法。我更喜欢解除底层模型的保护,调用 create
然后重新保护它。大致如下:
public function subscribe($data)
{
$this->courses()->getRelated()->unguard();
$created = $this->courses()->create($data);
$this->courses()->getRelated()->reguard();
return $created;
}
这样,您就不必手动设置外键了。
laravel/internals相关讨论:
[PROPOSAL] Force create model through a relationship