Answers.Body: <p>Try to use EVENT_BEFORE_SAVE and try to do a query the user again to get the old user data, something like this:</p>
  <pre><code>Event::on(
          User::class, 
          Element::EVENT_BEFORE_SAVE, 
                function (ModelEvent $event) {
                  if (Craft::$app-&gt;getRequest()-&gt;getIsConsoleRequest()) {
                      return;
                  }
      
                  $user = $event-&gt;sender;
                  $userLocation = $user-&gt;userLocation-&gt;one();
      
                  $newLocationCategory = $user-&gt;userLocation-&gt;one();
                  $newLocationGroup = Craft::$app-&gt;getUserGroups()-&gt;getGroupByHandle($newLocationCategory-&gt;title);
                  $currentGroupIds = [];
                  if (!$event-&gt;isNew) {
                      // to get all location we need to do a query for user again.
                      $oldUserData = User::findOne($user-&gt;id);
                      $oldUserCategory = $oldUserData-&gt;userLocation-&gt;one();
                      $oldLocationGroup = Craft::$app-&gt;getUserGroups()-&gt;getGroupByHandle($oldUserCategory-&gt;title);
  
                      $currentGroupIds = \array_map(function($group) {
                          return (int)$group-&gt;id;
                      }, $user-&gt;getGroups());
  
  
                      // Remove the user from their old Location group - this is not working
                      if (($key = array_search($oldLocationGroup-&gt;id, $currentGroupIds)) !== false) {
                          unset($currentGroupIds[$key]);
                      }
                  }
      
                  $finalGroupIds = array_merge($currentGroupIds, [$newLocationGroup?-&gt;id]);
                  Craft::$app-&gt;getUsers()-&gt;assignUserToGroups($user-&gt;id, $finalGroupIds);
              }
      );
  </code></pre>
  <p>PS: I didn't test the code.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-11-06T09:23:15.970Z
Answers.Text: 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!
Answers.UserDisplayName: supazu
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-11-07T01:53:11.920Z
Answers.Text: Yes, because it's coming from $event->sender, and that user value you get from that has the updated one value.
Answers.UserDisplayName: aodihis
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-11-07T08:28:47.817Z
Answers.Text: 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?
Answers.UserDisplayName: supazu
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-11-08T01:56:57.470Z
Answers.Text: @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.
Answers.UserDisplayName: aodihis
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-11-08T02:58:54.163Z
Answers.Text: 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.
Answers.UserDisplayName: supazu
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-11-08T03:13:43.627Z
Answers.Text: Yes, you correct, how about to separate the process for the new user and old user, for the new user do on EVENT_AFTER_SAVE since you don't need to remove it from the old group only assign the user their registered group, and use EVENT_BEFORE_SAVE for old user.
Answers.UserDisplayName: aodihis
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-11-08T03:22:47.363Z
Answers.Text: Yep, I have that working now but with a bit of duplicated code. I'm going to try to learn about how to reuse things across different events. Thanks for your help!
Answers.UserDisplayName: supazu
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-11-05T11:42:20.233Z
Answers.LastActivityDate: 2022-11-08T01:55:49.197Z
Answers.LastEditDate: 2022-11-08T01:55:49.197Z
Answers.UserDisplayName: aodihis
Answers.Body: <p>So, are you sure you only have the necessary user group IDs at the end, in $finalGroupIds ?</p>
  <p>Can you try to drop the keys after deleting(unset) unnecessary items(old groupIds) in your array?</p>
  <pre><code>$finalGroupIds = array_values(array_merge($currentGroupIds, [$newLocationGroup-&gt;id]));
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-11-05T02:23:17.833Z
Answers.Text: 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.
Answers.UserDisplayName: supazu
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-11-04T22:52:13.020Z
Answers.LastActivityDate: 2022-11-04T22:52:13.020Z
Answers.UserDisplayName: RomanAvr
Body: <p>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.</p>
  <p>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.</p>
  <p>The complication is that they belong to other user groups, which they need to stay in.</p>
  <p>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!</p>
  <pre><code>Event::on(
      User::class, Element::EVENT_AFTER_SAVE, function (ModelEvent $event) {
          if (Craft::$app-&gt;getRequest()-&gt;getIsConsoleRequest()) {
              return;
          }
  
          $user = $event-&gt;sender;
          $userLocation = Craft::$app-&gt;getRequest()-&gt;getBodyParam('fields[userLocation][0]');
  
          if (!$userLocation) {
              return;
          }
  
          $newLocationCategory = Craft::$app-&gt;getCategories()-&gt;getCategoryById($userLocation);
          $newLocationGroup = Craft::$app-&gt;getUserGroups()-&gt;getGroupByHandle($newLocationCategory-&gt;title);
  
          $oldLocationCategory = Craft::$app-&gt;getCategories()-&gt;getCategoryById($user-&gt;userLocation-&gt;id[0]);
          $oldLocationGroup = Craft::$app-&gt;getUserGroups()-&gt;getGroupByHandle($oldLocationCategory-&gt;title);
  
          $currentGroupIds = \array_map(function ($group) {
              return (int)$group-&gt;id;
          }, $user-&gt;getGroups());
  
  
          // Remove the user from their old Location group - this is not working
          if (($key = array_search($oldLocationGroup-&gt;id, $currentGroupIds)) !== false) {
              unset($currentGroupIds[$key]);
          }
  
  
          $finalGroupIds = array_merge($currentGroupIds, [$newLocationGroup-&gt;id]);
          Craft::$app-&gt;getUsers()-&gt;assignUserToGroups($user-&gt;id, $finalGroupIds);
      }
  );
  </code></pre>
  
Tags: <modules>
Title: Add to / remove from user group
ThreadText: # Add to / remove from user group
  
  (Asked by: username_0 on 2022-11-04)
  
  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);
      }
  );
  
  ```
  
  **Tags:** modules
  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_1 on 2022-11-05. Score: 2 | Accepted answer
  
  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.
  
  > **Comments:**
  > **username_0**: 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!  
  > **username_1**: Yes, because it's coming from $event->sender, and that user value you get from that has the updated one value.  
  > **username_0**: 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?  
  > **username_1**: @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.  
  > **username_0**: 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.  
  > **username_1**: Yes, you correct, how about to separate the process for the new user and old user, for the new user do on EVENT_AFTER_SAVE since you don't need to remove it from the old group only assign the user their registered group, and use EVENT_BEFORE_SAVE for old user.  
  > **username_0**: Yep, I have that working now but with a bit of duplicated code. I'm going to try to learn about how to reuse things across different events. Thanks for your help!  
  
  ---
  
  ### Answer by username_2 on 2022-11-04. Score: 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]));
  
  ```
  
  > **Comments:**
  > **username_0**: 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.  
  
  ---
  
  