Reputation: 405
Let's say i have three models User
Post
and Comment
and linked like so:
class User extends Model
{
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
}
class Comment extends Model
{
public function post(): BelongsTo
{
return $this->belongsTo(Post::class);
}
}
class Post extends Model
{
/**
* Get the comments for the blog post.
*/
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}
}
and i wanted to associate a post to comment, i would do:
$comment->post()->associate($post);
but if i mistakenly pass a different model to the associate function:
$comment->post()->associate($user);
then it will still work, it will associate the post that has the same id of the passed user, which is wrong.
basically the associate function doesn't check the type of the model that was passed before associating, is there something that can be enabled so that laravel checks the type of the model before associating?
Upvotes: 0
Views: 141
Reputation: 429
Laravel associate
itself does not perform a check on the type of the model being associated.
You can solve it by extending the relationship method, which can check related model dynamically.
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use InvalidArgumentException;
class TypedBelongsTo extends BelongsTo
{
public function associate($model)
{
$relatedModelClass = $this->getRelated()::class;
if (! $model instanceof $relatedModelClass) {
throw new InvalidArgumentException("Expected instance of $relatedModelClass.");
}
return parent::associate($model);
}
}
and for example, your Comment
model can have below relation.
public function post(): TypedBelongsTo
{
return new TypedBelongsTo(Post::query, $this, 'post_id', 'id', 'post');
}
Upvotes: 2
Reputation: 8308
The associate
method does not check for types nor do a parameter type-hinting, and it's expects a Model instance, string, int or null as shown:
/**
* Associate the model instance to the given parent.
*
* @param \Illuminate\Database\Eloquent\Model|int|string|null $model
* @return \Illuminate\Database\Eloquent\Model
*/
public function associate($model)
Basically, you must take care of this by yourself, as this is not a user input, so it's easy to make sure that you are passing the right parameters.
Otherwise, you will need to do a redundant work -IMO- to handle this.
First, you will override the belongsTo
method in your model that returns the new BelongsTo
class (in the next step).
Secondly, you will need to create a shadow version of the BelongsTo
concern, most likely you will inherit the Illuminate\Database\Eloquent\Relations\BelongsTo
and override the associate
method to type hint your parameter, give it a proper name, let's say CommentsBelongsTo
or something.
The final class would something like:
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use App\Models\Post;
class CommentsBelongsTo extends BelongsTo
{
...
public function associate(Post $model)
...
}
Upvotes: 0