Laravel relationships attach if not already attached

Viewed 27764

Is there a quick way of attaching relationships if they're not already attached. I am using this code to update relations of a model;

        if (!empty($request->get('roles')) && is_array($request->get('roles'))) {
            $message->Roles()->attach($request->get('roles'));
        }
        if (!empty($request->get('users')) && is_array($request->get('users'))) {
            $message->Users()->attach($request->get('users'));
        }

But I am getting this error which I am this error;

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1-41' for key 'PRIMARY' (SQL: insert into message_user (message_id, user_id) values (1, 41), (1, 42), (1, 43), (1, 44), (1, 45), (1, 46), (1, 47), (1, 48), (1, 49), (1, 50))

I want to avoid going through a very long list of array check which users are not already attached and attaching them. Also let me know if this is the only way.

I am thinking something like;

$message->Users()->attachIfNotAttached($request->get('users'));
3 Answers

Laravel has built-in method for this - syncWithoutDetaching:

$message->Users()->syncWithoutDetaching($request->get('users'));

Using syncWithoutDetaching or sync([arr], false) looks cleaner in code, but performs 25x slower than attach(). This is because it checks each id and performs individual database inserts per item.

I'd suggest building upon Paul Spiegel's answer by extending eloquent or creating a new method on your model class.

In my use case I have a Feed class and a Post class linked by a many to many pivot. I added the following method to my Feed class.

public function attachUniquePosts($ids)
{
    $existing_ids = $this->posts()->whereIn('posts.id', $ids)->pluck('posts.id');
    $this->posts()->attach($ids->diff($existing_ids));
}
  • Attaching 20 posts to a feed using syncWithoutDetaching() took on average 0.107s
  • Attaching 20 posts to a feed using attachUniquePosts() took on average 0.004s
Related