In a small project with Laravel 5.3 and Stripe, I am trying to force create a Subscription
on a User
through a hasOne
relationship:
// 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...
);
}
However, I get :
Call to undefined method Illuminate\Database\Query\Builder::forceCreate()
Even though forceCreate()
is a valid Eloquent method.
Any ideas how I can simulate this behavior? Or should I just save
a Subscription
manually assigning each field? The complication is that certain fields should be kept guarded
, e.g. stripe_id
.
My quick' n' dirty solution:
// User.php @ subscribe($data)
return (new Subscription())
->forceFill([
'user_id' => $this->id,
// $data with sensitive guarded data
])
->save();
I'm sure there is a better way though!
The call to $this->subscriptions()
returns an instance of HasMany
class, not a Model
.
Contrary to create
and createMany
, there's no forceCreate()
implemented in HasOneOrMany
class. And thus, there's no first-hand API to use in such situations.
You see? when you call $this->subscriptions()->create()
, you're calling the create
method on a HasMany
instance, not a Model
instance. The fact that Model
class has a forceCreate
method, has nothing to do with this.
You could use the getRelated()
method to fetch the related model and then call forceCreate()
or unguarded()
on that:
public function subscribe($data) {
return $this->subscription()
->getRelated()
->forceCreate($data);
}
But there's a serious downside to this approach; it does not set the relation as we're fetching the model out of the relation. To work around it you might say:
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);
}
Ehh, seems too hacky, not my approach. I prefer unguarding the underlying model, calling create
and then reguarding it. Something along the lines of:
public function subscribe($data)
{
$this->courses()->getRelated()->unguard();
$created = $this->courses()->create($data);
$this->courses()->getRelated()->reguard();
return $created;
}
This way, you don't have to deal with setting the foreign key by hand.
laravel/internals related discussion:
[PROPOSAL] Force create model through a relationship