Skip to content

Fixes #18325: Ensure existing permission group users are maintained when editing a group#18326

Merged
snipe merged 2 commits intogrokability:developfrom
dylang3:fix/users-groups-sync
Dec 19, 2025
Merged

Fixes #18325: Ensure existing permission group users are maintained when editing a group#18326
snipe merged 2 commits intogrokability:developfrom
dylang3:fix/users-groups-sync

Conversation

@dylang3
Copy link
Copy Markdown

@dylang3 dylang3 commented Dec 9, 2025

Fixes #18325

The existing associated users for a permission group are being passed to the view as a Collection in app/Http/Controllers/GroupsController.php:

public function edit(Group $group): View|RedirectResponse
    {
        $permissions = config("permissions");
        $groupPermissions = $group->decodePermissions();

        if (!is_array($groupPermissions) || !$groupPermissions) {
            $groupPermissions = [];
        }

        $selected_array = Helper::selectedPermissionsArray(
            $permissions,
            $groupPermissions,
        );

        $users_query = User::where("show_in_list", 1)->whereNull("deleted_at");
        $users_count = $users_query->count();

        $associated_users = collect();
        $unselected_users = collect();

        if ($users_count <= config("app.max_unpaginated_records")) {
            $associated_users = $group
                ->users()
                ->where("show_in_list", 1)
                ->orderBy("first_name", "asc")
                ->orderBy("last_name", "asc")
                ->get();
            // Get the unselected users
            $unselected_users = User::where("show_in_list", 1)
                ->whereNotIn("id", $associated_users->pluck("id")->toArray())
                ->orderBy("first_name", "asc")
                ->orderBy("last_name", "asc")
                ->get();
        }

        return view(
            "groups.edit",
            compact(
                "group",
                "permissions",
                "selected_array",
                "groupPermissions",
            ),
        )
            ->with("associated_users", $associated_users)
            ->with("unselected_users", $unselected_users)
            ->with("all_users_count", $users_count);
    }

However, in the groups.edit view, the value of the users_to_sync hidden field is checking if the $associated_users prop is an array. Since that prop is actually a Collection, this check fails, and the value defaults to an empty string even if there are users already associated with this group.

<input type="hidden" name="users_to_sync" id="hidden_ids_box" class="form-control" value="{{ ($associated_users && is_array($associated_users)) ? implode(",", $associated_users->pluck('id')->toArray()) :  '' }}"/>

Once the form is submitted, unless the users have been updated (i.e. users were added or removed to the group as part of the update -- I assume this is thanks to select2 pulling the existing members back in), all existing pivot records for the group are dropped from the users_groups table in the database when $group->users()->sync($associated_users) is called in the controller's update method since $associated_users consists solely of an empty string.

The fix removes the conditional and uses Collection methods directly: pluck('id')->implode(','). This works for both groups with no existing members (returning an empty string) and groups with existing members (returning a comma-separated list of user IDs), ensuring user-group associations are maintained when the group is updated.

@dylang3 dylang3 requested a review from snipe as a code owner December 9, 2025 23:39
@snipe
Copy link
Copy Markdown
Member

snipe commented Dec 10, 2025

I guess this seems fine, but all of our tests, we weren't dropping users in the first place. (See the piles of screencasts associated with those changes)

Copy link
Copy Markdown
Member

@snipe snipe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we have some failing tests here.

@dylang3
Copy link
Copy Markdown
Author

dylang3 commented Dec 10, 2025

Ah, it looks like the associated_users prop was being sent as an array in the create method. I've updated that to be consistent in sending the data as a collection in both methods now.

And apologies -- it completely slipped my mind to run the test suite before submitting the original PR -- we are all green now, though 🙂

@snipe snipe requested a review from uberbrady December 11, 2025 16:45
Copy link
Copy Markdown
Collaborator

@marcusmoore marcusmoore left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this locally and it works 👍🏾

@snipe snipe merged commit 755bb8f into grokability:develop Dec 19, 2025
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants