Answers.Body: <p>To start, you should probably code a bit more defensively – i.e. add some checks to only execute the logic if there's actually a <code>location</code> POST param for the current request, if a group by that handle actually exist, etc.</p>
  <p>You should also add a check to avoid running this logic for console requests. As is, the current code would throw an exception if users are (re)saved via Craft's CLI, since console requests don't actually have a <code>getBodyParam()</code> method.</p>
  <p>And finally, it's probably good to know that the way <code>Craft::$app-&gt;getUsers()-&gt;assignUserToGroups()</code> works, is that it will actually <em>remove</em> any current group affiliations, if those groups are not also passed in. I.e. if a user is already in groups A and B, and you do <code>Craft::$app-&gt;getUsers()-&gt;assignUserToGroups($user-&gt;id, [$group-&gt;id])</code> to add the user to a group C, that user will be removed from groups A and B.</p>
  <p>I'm not sure if that's how you <em>want</em> it to work, but assuming it's not, you'll need to make sure to include the IDs to all other groups that the member is already a member of, as well.</p>
  <p>(The above is why you're seeing users losing their group affiliations when saved; you're probably passing an empty array to the <code>assignUserToGroups()</code> most of the time, which - by design - will remove all group memberships for that user.)</p>
  <p>Putting all of those together, here's how it could look:</p>
  <pre class="lang-php prettyprint-override"><code>Event::on(
      User::class, 
      Element::EVENT_AFTER_SAVE, 
      function (ModelEvent $event) {
  
          // Avoid running this code for console requests
          if (Craft::$app-&gt;getRequest()-&gt;getIsConsoleRequest()) {
              return;
          }
  
          // Get the &quot;location&quot; parameter
          $location = Craft::$app-&gt;getRequest()-&gt;getBodyParam('location');
  
          // Don't do anything if there isn't actually &quot;location&quot; POST parameter
          if (!$location) {
              return;
          }
  
          // Get the group to add the user to
          $group = Craft::$app-&gt;getUserGroups()-&gt;getGroupByHandle($location);
  
          // Don't do anything if no group was found by that &quot;location&quot; handle
          if (!$group) {
              return;
          }
  
          // Get the user
          $user = $event-&gt;sender;
  
          // Get the IDs for all the groups the user is currently a member of
          $currentGroupIds = \array_map(function ($group) {
              return (int)$group-&gt;id;
          }, $user-&gt;getGroups());
  
          // Don't do anything if the user is already in the given &quot;location&quot; group
          if (in_array((int)$group-&gt;id, $currentGroupIds)) {
              return;
          }
  
          // Add the user to the new group
          // If you want the user to *only* be a member of the given &quot;location&quot; group, skip this part and just do `Craft::$app-&gt;getUsers()-&gt;assignUserToGroups($user-&gt;id, [$group-&gt;id]);` like you had before
          $allGroupIds = array_merge($currentGroupIds, [$group-&gt;id]);
          Craft::$app-&gt;getUsers()-&gt;assignUserToGroups($user-&gt;id, $allGroupIds);
      
      }
  );
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-02-23T00:00:13.720Z
Answers.Text: This is amazing, exactly what I was looking for you perfectly answered what I'm still missing when thinking about these things in being defensive.
  
  The only minor issue, for anyone who stumbles on this, is that it won't work on new registration if you have a Default User Group selected in the User Settings. That wasn't essential for me, so I just removed it but I'm sure it would be easy enough to add to this.
  
  Thanks again!
Answers.UserDisplayName: supazu
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-02-22T11:36:24.607Z
Answers.LastActivityDate: 2022-02-22T11:52:25.240Z
Answers.LastEditDate: 2022-02-22T11:52:25.240Z
Answers.UserDisplayName: Mats Mikkel Rummelhoff
Body: <p>I'm trying to add my users to a group based on the option they select in a dropdown field. I feel like I'm close but so far - at the moment, it's not adding the new user to a group and it's clearing other user's groups on updating their profile.</p>
  <p>Can anyone point out where I'm going wrong?</p>
  <pre><code>Event::on(User::class, Element::EVENT_AFTER_SAVE, function (ModelEvent $event) {
      $user = $event-&gt;sender;
      $location = Craft::$app-&gt;getRequest()-&gt;getBodyParam('location');
      $group = Craft::$app-&gt;getUserGroups()-&gt;getGroupByHandle($location);
      Craft::$app-&gt;getUsers()-&gt;assignUserToGroups($user-&gt;id, [$group-&gt;id]);
  });
  </code></pre>
  <p>I'm new to all of this so trying to learn! There might be other checks I should be doing that I'm not sure of...</p>
  
Tags: <users><user-groups>
Title: Add user to group based on registration form field
ThreadText: # Add user to group based on registration form field
  
  (Asked by: username_0 on 2022-02-22)
  
  I'm trying to add my users to a group based on the option they select in a dropdown field. I feel like I'm close but so far - at the moment, it's not adding the new user to a group and it's clearing other user's groups on updating their profile.
  
  Can anyone point out where I'm going wrong?
  
  ```
  Event::on(User::class, Element::EVENT_AFTER_SAVE, function (ModelEvent $event) {
      $user = $event->sender;
      $location = Craft::$app->getRequest()->getBodyParam('location');
      $group = Craft::$app->getUserGroups()->getGroupByHandle($location);
      Craft::$app->getUsers()->assignUserToGroups($user->id, [$group->id]);
  });
  
  ```
  
  I'm new to all of this so trying to learn! There might be other checks I should be doing that I'm not sure of...
  
  **Tags:** users, user-groups
  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2022-02-22. Score: 3 | Accepted answer
  
  To start, you should probably code a bit more defensively – i.e. add some checks to only execute the logic if there's actually a `location` POST param for the current request, if a group by that handle actually exist, etc.
  
  You should also add a check to avoid running this logic for console requests. As is, the current code would throw an exception if users are (re)saved via Craft's CLI, since console requests don't actually have a `getBodyParam()` method.
  
  And finally, it's probably good to know that the way `Craft::$app->getUsers()->assignUserToGroups()` works, is that it will actually *remove* any current group affiliations, if those groups are not also passed in. I.e. if a user is already in groups A and B, and you do `Craft::$app->getUsers()->assignUserToGroups($user->id, [$group->id])` to add the user to a group C, that user will be removed from groups A and B.
  
  I'm not sure if that's how you *want* it to work, but assuming it's not, you'll need to make sure to include the IDs to all other groups that the member is already a member of, as well.
  
  (The above is why you're seeing users losing their group affiliations when saved; you're probably passing an empty array to the `assignUserToGroups()` most of the time, which - by design - will remove all group memberships for that user.)
  
  Putting all of those together, here's how it could look:
  
  ```php
  Event::on(
      User::class, 
      Element::EVENT_AFTER_SAVE, 
      function (ModelEvent $event) {
  
          // Avoid running this code for console requests
          if (Craft::$app->getRequest()->getIsConsoleRequest()) {
              return;
          }
  
          // Get the "location" parameter
          $location = Craft::$app->getRequest()->getBodyParam('location');
  
          // Don't do anything if there isn't actually "location" POST parameter
          if (!$location) {
              return;
          }
  
          // Get the group to add the user to
          $group = Craft::$app->getUserGroups()->getGroupByHandle($location);
  
          // Don't do anything if no group was found by that "location" handle
          if (!$group) {
              return;
          }
  
          // Get the user
          $user = $event->sender;
  
          // Get the IDs for all the groups the user is currently a member of
          $currentGroupIds = \array_map(function ($group) {
              return (int)$group->id;
          }, $user->getGroups());
  
          // Don't do anything if the user is already in the given "location" group
          if (in_array((int)$group->id, $currentGroupIds)) {
              return;
          }
  
          // Add the user to the new group
          // If you want the user to *only* be a member of the given "location" group, skip this part and just do `Craft::$app->getUsers()->assignUserToGroups($user->id, [$group->id]);` like you had before
          $allGroupIds = array_merge($currentGroupIds, [$group->id]);
          Craft::$app->getUsers()->assignUserToGroups($user->id, $allGroupIds);
      
      }
  );
  
  ```
  
  > **Comments:**
  > **username_0**: This is amazing, exactly what I was looking for you perfectly answered what I'm still missing when thinking about these things in being defensive.  The only minor issue, for anyone who stumbles on this, is that it won't work on new registration if you have a Default User Group selected in the User Settings. That wasn't essential for me, so I just removed it but I'm sure it would be easy enough to add to this.  Thanks again!  
  
  ---
  
  