Reporting your Sync changes

Laravel

A Laravel many-to-many relationship uses a pivot table to connect many models to many other models. For example, a User might have many roles; he can be an Author, and an Editor. Likewise, a role of Author can have multiple users.

Creating or Removing a Single Relation

One side of the relationship can be attached to the other by using the attach() method on the relationship method, and it can be removed from the relationship by using the detach() method.

use App\Models\Role;
use App\Models\User;
 
$user = User::find(1);

$oldRole = Role::whereName(Role::EDITOR)->sole();
$newRole = Role::whereName(Role::AUTHOR)->sole(); 

$user->roles()->detach($oldRole);
$user->roles()->attach($newRole);

Updating a Relation List

Or you can synchronize a list of roles in one fell swoop with the sync() method. It will remove all relations not listed, and make sure that any listed relations are added.

// Using the same $user that has `Author` and doesn't have `Editor`, but
// it might have other roles attached that we don't want him to have. 

$roles = Role::whereIn(
    'name',
    [Role::AUTHOR, Role::MANAGER, Role::DEPARTMENT_HEAD]
)
->get();
$user->roles()->sync($roles);

We can also have extra data on the pivot table, perhaps we want to record the user who changed the permission. We can send that data to the sync() method with a larger array. This array needs the related model’s key, which is usually id, but it might uuid, or ulid or hash_id, so we can future-proof ourselves by using the getKey() method on the model:

$user->roles()->sync([
    $authorRole->getKey() => ['updated_by' => Auth::id()],
    $managerRole->getKey() => ['updated_by' => Auth::id()],
    $departmentHeadRole->getKey() => ['updated_by' => Auth::id()],
])

In this case, since all the extra data is the same, we can send it once and Eloquent will add it for us:

use Illuminate\Support\Facades\Auth;

$user->roles()->syncWithPivotValues($roles, ['updated_by' => Auth::id()]);

Enhancing the user experience

So how do we know what changed? It might be a use case that with your change, you send a flash message to the user telling them that the changed user no longer has the role of Super Admin, but now has the role Manager. We can do that by capturing the return value of sync().

The sync() method returns an array of arrays with three keys: attached, detached, and updated, each one containing an array of the affected relationships.

I this specific use case, I am attaching users to smaller groups. Here we see that my group now has a new attachment to User #82, is no longer attached to User #57, and a pivot attribute has changed on the relationship with User #54. Any existing relationships remain

With this data, I can create one or more flash messages telling the user of these changes.

$request->session()->flash('attached', 'Group now contains User(s) '
    . array_values($changes['attached']));
$request->session()->flash('detached', 'Group no longer contains User(s) '
    . array_values($changes['detached']));

Happy coding!

Weather in Charlotte, NC