1

I have a profile form with a select menu for a user to choose their nearest location (a Category group.) A user will select their nearest location, it will add the category to their profile and then add them to the corresponding User group.

I would also like to remove the user from their existing group. For example, if they change from London to New York, they should be removed from the London user group and placed in the New York group.

The complication is that they belong to other user groups, which they need to stay in.

Adding to the new location group is working well, but I'm looking for a hand in removing the exisiting one. If anyone has any thoughts on my code below, I would very much appreciate it!

Event::on(
    User::class, Element::EVENT_AFTER_SAVE, function (ModelEvent $event) {
        if (Craft::$app->getRequest()->getIsConsoleRequest()) {
            return;
        }

        $user = $event->sender;
        $userLocation = Craft::$app->getRequest()->getBodyParam('fields[userLocation][0]');

        if (!$userLocation) {
            return;
        }

        $newLocationCategory = Craft::$app->getCategories()->getCategoryById($userLocation);
        $newLocationGroup = Craft::$app->getUserGroups()->getGroupByHandle($newLocationCategory->title);

        $oldLocationCategory = Craft::$app->getCategories()->getCategoryById($user->userLocation->id[0]);
        $oldLocationGroup = Craft::$app->getUserGroups()->getGroupByHandle($oldLocationCategory->title);

        $currentGroupIds = \array_map(function ($group) {
            return (int)$group->id;
        }, $user->getGroups());


        // Remove the user from their old Location group - this is not working
        if (($key = array_search($oldLocationGroup->id, $currentGroupIds)) !== false) {
            unset($currentGroupIds[$key]);
        }


        $finalGroupIds = array_merge($currentGroupIds, [$newLocationGroup->id]);
        Craft::$app->getUsers()->assignUserToGroups($user->id, $finalGroupIds);
    }
);
0

2 Answers 2

2

Try to use EVENT_BEFORE_SAVE and try to do a query the user again to get the old user data, something like this:

Event::on(
        User::class, 
        Element::EVENT_BEFORE_SAVE, 
              function (ModelEvent $event) {
                if (Craft::$app->getRequest()->getIsConsoleRequest()) {
                    return;
                }
    
                $user = $event->sender;
                $userLocation = $user->userLocation->one();
    
                $newLocationCategory = $user->userLocation->one();
                $newLocationGroup = Craft::$app->getUserGroups()->getGroupByHandle($newLocationCategory->title);
                $currentGroupIds = [];
                if (!$event->isNew) {
                    // to get all location we need to do a query for user again.
                    $oldUserData = User::findOne($user->id);
                    $oldUserCategory = $oldUserData->userLocation->one();
                    $oldLocationGroup = Craft::$app->getUserGroups()->getGroupByHandle($oldUserCategory->title);

                    $currentGroupIds = \array_map(function($group) {
                        return (int)$group->id;
                    }, $user->getGroups());


                    // Remove the user from their old Location group - this is not working
                    if (($key = array_search($oldLocationGroup->id, $currentGroupIds)) !== false) {
                        unset($currentGroupIds[$key]);
                    }
                }
    
                $finalGroupIds = array_merge($currentGroupIds, [$newLocationGroup?->id]);
                Craft::$app->getUsers()->assignUserToGroups($user->id, $finalGroupIds);
            }
    );

PS: I didn't test the code.

7
  • Nice! Re-querying for the user seems to be the trick: $oldUserData = User::findOne($user->id);. I'm not totally sure why that wasn't available just from $user but I get because it's coming from $event->sender? Thanks for your help!
    – supazu
    Commented Nov 6, 2022 at 9:23
  • Yes, because it's coming from $event->sender, and that user value you get from that has the updated one value.
    – aodihis
    Commented Nov 7, 2022 at 1:53
  • Makes sense. However, it's introduced a new complication: this works for the profile edit form, but not for creating a new user. Using EVENT_BEFORE_SAVE means $oldUserData = User::findOne($user->id); returns null. Any thoughts on how I could get around that?
    – supazu
    Commented Nov 7, 2022 at 8:28
  • @brynli64 you can check if the events is new or not, if not new then run get the old category, I updated the answer for new user.
    – aodihis
    Commented Nov 8, 2022 at 1:56
  • Thank you, I appreciate your help on this but unfortunately the same thing exists. Because it's EVENT_BEFORE_SAVE, $user->id returns null. Changing to EVENT_AFTER_SAVE solves that, but then the group isn't removed properly (presumably because the old data is now the new data). I think I might need to think about how to set this up to handle both scenarios.
    – supazu
    Commented Nov 8, 2022 at 2:58
1

So, are you sure you only have the necessary user group IDs at the end, in $finalGroupIds ?

Can you try to drop the keys after deleting(unset) unnecessary items(old groupIds) in your array?

$finalGroupIds = array_values(array_merge($currentGroupIds, [$newLocationGroup->id]));
1
  • Thanks for the reply. It seems like $oldLocationCategory = Craft::$app->getCategories()->getCategoryById($user->userLocation->id[0]); is returning the new category, rather than the exisiting one. Changing to EVENT_BEFORE_SAVE doesn't help...so I'm not quite sure how to get the exisiting category on the user.
    – supazu
    Commented Nov 5, 2022 at 2:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.