kya
kya

Reputation: 1828

Many to many relationship Sync removes all selected options

I have a roles and permissions table in a many to many relationship, and pivot table permission_role. This issue I am experiencing when trying to update the permissions on the role.

For example: When creating the admin role: I added the permission: view_users to it. Now when trying to update it, giving it the additional permission create_team.

The code:

$roleUpdate = Role::where('id', $role->id)->update([
    'name' => $request->input('name'),
    'updated_at' => Carbon::now()
]);

$permissions = $request->input('permission');

//dd($permissions);

foreach ($permissions as $permission) {
    $role->permissions()->sync($permission, true);
}

if ($roleUpdate) {
    Alert::toast('Role updated successfully', 'success');
    return redirect()
        ->route('roles.index', ['role' => $role->id])
        ->with('success', 'Role Updated Successfully');
}

//redirect
return back()->withInput();

When I dd() on the permission to see which options are coming through, I get the expected and correct result

array:2 [▼
  0 => "3"
  1 => "4"
]

However, when the data get saved to the database, only the new value is stored and the old one is removed. I understand the issue may be caused by setting detach to true on this line:

$role->permissions()->sync($permission,true); 

But if I set it to false, then it doesn't work when I update the role's permission by removing one of the permissions. It doesn't detach. It doesn't seem to work according to the explanation given in the docs on the link below. Not sure what I'm missing Laravel Docs

Upvotes: 4

Views: 90

Answers (1)

Remul
Remul

Reputation: 8252

The problem is that you are using a foreach:

array:2 [▼
  0 => "3"
  1 => "4"
]

foreach ($permissions as $permission) {
    $role->permissions()->sync($permission, true);
}

On the first loop you remove all permissions from the role except the permission with the id 3, on the second loop you again remove all permissions from the role except the permission with the id 4, which means you have deleted the previously set permission with id 3.

Removing the foreach and passing the whole array should work:

// no need to pass 'true' as the second argument as it is the default value
$role->permissions()->sync($request->input('permission'));

From the docs:

You may also use the sync method to construct many-to-many associations. The sync method accepts an array of IDs to place on the intermediate table. Any IDs that are not in the given array will be removed from the intermediate table. So, after this operation is complete, only the IDs in the given array will exist in the intermediate table:

$user->roles()->sync([1, 2, 3]);

Upvotes: 4

Related Questions