Answers.Body: <p>For such large amounts of operations it's best to use a Job. You should only make sure to batch your results and don't fetch every user separately.</p>
  
  <pre class="lang-php prettyprint-override"><code>use Craft;
  use craft\base\Element;
  use craft\db\QueryAbortedException;
  use craft\elements\User;
  use craft\helpers\App;
  use craft\queue\BaseJob;
  use yii\base\Exception;
  
  class ResaveUsers extends BaseJob
  {
      // Properties
      // =========================================================================
  
      /**
       * @var int[] The user Ids
       */
      public $userIds;
  
      /**
       * @var string the reference number
       */
      public $refNumber;
  
      // Public Methods
      // =========================================================================
  
      /**
       * execute
       *
       * @param \craft\queue\QueueInterface|\yii\queue\Queue $queue
       *
       * @throws \Throwable
       * @throws \craft\errors\ElementNotFoundException
       * @throws \yii\base\Exception
       *
       * @author Robin Schambach
       * @since  07.05.2019
       */
      public function execute($queue)
      {
          $query = User::find()-&gt;id($this-&gt;userIds);
  
          $totalElements = $query-&gt;count();
          $currentElement = 0;
  
          $elements = \Craft::$app-&gt;getElements();
  
          try {
              $i = 0;
              foreach ($query-&gt;each() as $user) {
                  $i ++;
                  $this-&gt;setProgress($queue, $currentElement++ / $totalElements);
  
                  $tableValue   = $user-&gt;getFieldValue( 'aktionsRefNumber' );
                  $tableValue[] = [
                      'col0' =&gt; $this-&gt;refNumber . '-' . $i,
                  ];
                  $user-&gt;setFieldValue( 'aktionsRefNumber', $tableValue );
  
                  /** @var Element $element */
                  $user-&gt;setScenario(Element::SCENARIO_ESSENTIALS);
                  if (!$elements-&gt;saveElement($user)) {
                      throw new Exception('Couldn’t save element ' . $user-&gt;id . ' (' . get_class($user) . ') due to validation errors.');
                  }
              }
          } catch (QueryAbortedException $e) {
              // Fail silently
          }
      }
  
      // Protected Methods
      // =========================================================================
  
      /**
       * @inheritdoc
       */
      protected function defaultDescription(): string
      {
          return Craft::t('app', 'Resaving {class} elements', [
              'class' =&gt; App::humanizeClass(User::class)
          ]);
      }
  }
  </code></pre>
  
  <p>You can start your job with </p>
  
  <pre class="lang-php prettyprint-override"><code>Craft::$app-&gt;getQueue()-&gt;push(new ResaveUsers([
       'userIds' =&gt; [1,2,4,5,6],
       'refNumber' =&gt; 'foobar'
  ]));
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-05-07T07:51:10.187Z
Answers.Text: as always on point ...thanks robin
Answers.UserDisplayName: HendrikEng
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-05-07T07:49:20.773Z
Answers.LastActivityDate: 2019-05-07T07:49:20.773Z
Answers.UserDisplayName: Robin Schambach
Body: <p>I have to build a custom plugin for an internal user management system/data export that needs to be able to get triggered from the CP.</p>
  
  <p>I basically have to sort a big amount of users (1500+) in a specific plugin tab in the Control Panel. The sorting is done via Vue/JS. When I am done sorting the users I need to be able to trigger and save a field (a string I will enter in the CP Tab) for all of those users in the same order as it was sorted in the CP.</p>
  
  <p>For now, I have simply sent an array of User Ids to my plugin controller via post request:</p>
  
  <pre><code>public function actionSubmitRef()
  {
      $request = \Craft::$app-&gt;getRequest();
  
      $userIds = $request-&gt;getParam( 'userIds' );
      $refNumber = $request-&gt;getParam( 'refNumber' );
  
      $response = [
          'userIds' =&gt; $userIds,
          'refNumber' =&gt; $refNumber,
      ];
  
      WFWPlugin::getInstance()-&gt;saveNewReferenceNumber( $userIds, $refNumber );
  
      return $this-&gt;asJson( $response );
  }
  </code></pre>
  
  <p>The controller passes the array of Ids and the string to save to a function that saves the string to each user:</p>
  
  <pre><code>public function saveNewReferenceNumber( $userIds, $refNumber )
  {
      $i = 0;
  
      foreach ( $userIds as $userId ) {
          $i ++;
          $user         = Craft::$app-&gt;users-&gt;getUserById( $userId );
          $tableValue   = $user-&gt;getFieldValue( 'aktionsRefNumber' );
          $tableValue[] = [
              'col0' =&gt; $refNumber . '-' . $i,
          ];
  
          $user-&gt;setFieldValue( 'aktionsRefNumber', $tableValue );
          $success = Craft::$app-&gt;getElements()-&gt;saveElement( $user );
  
          if ( ! $success ) {
              Craft::error( 'Couldn’t save the entry "' . $user . '"', __METHOD__ );
          }
      }
  }
  </code></pre>
  
  <p>The problem is that with this huge amount of save requests, <code>PHP</code> either times out or it simply can't be processed. I end up with having a max of 40 saved uses with the right value in the field.</p>
  
  <p>How could i solve the issue? I have recently asked the question in the discord dev channel and using a console command has been suggested, but since the users need to be sorted in the CP, the string has to manually be entered and then triggered via a button, I couldn't get it to work.</p>
  
  <p>Another approach was to push each save request into a <code>yii2</code> job queue but that didn't help either...</p>
  
  <p>Thanks for the help, much appreciated!</p>
  
Tags: <plugin-development><craft3><elementtype><usermodel>
Title: Batch saving a custom field to multiple users via controller
ThreadText: # Batch saving a custom field to multiple users via controller
  
  (Asked by: username_0 on 2019-05-06)
  
  I have to build a custom plugin for an internal user management system/data export that needs to be able to get triggered from the CP.
  
  I basically have to sort a big amount of users (1500+) in a specific plugin tab in the Control Panel. The sorting is done via Vue/JS. When I am done sorting the users I need to be able to trigger and save a field (a string I will enter in the CP Tab) for all of those users in the same order as it was sorted in the CP.
  
  For now, I have simply sent an array of User Ids to my plugin controller via post request:
  
  ```
  public function actionSubmitRef()
  {
      $request = \Craft::$app->getRequest();
  
      $userIds = $request->getParam( 'userIds' );
      $refNumber = $request->getParam( 'refNumber' );
  
      $response = [
          'userIds' => $userIds,
          'refNumber' => $refNumber,
      ];
  
      WFWPlugin::getInstance()->saveNewReferenceNumber( $userIds, $refNumber );
  
      return $this->asJson( $response );
  }
  
  ```
  
  The controller passes the array of Ids and the string to save to a function that saves the string to each user:
  
  ```
  public function saveNewReferenceNumber( $userIds, $refNumber )
  {
      $i = 0;
  
      foreach ( $userIds as $userId ) {
          $i ++;
          $user         = Craft::$app->users->getUserById( $userId );
          $tableValue   = $user->getFieldValue( 'aktionsRefNumber' );
          $tableValue[] = [
              'col0' => $refNumber . '-' . $i,
          ];
  
          $user->setFieldValue( 'aktionsRefNumber', $tableValue );
          $success = Craft::$app->getElements()->saveElement( $user );
  
          if ( ! $success ) {
              Craft::error( 'Couldn’t save the entry "' . $user . '"', __METHOD__ );
          }
      }
  }
  
  ```
  
  The problem is that with this huge amount of save requests, `PHP` either times out or it simply can't be processed. I end up with having a max of 40 saved uses with the right value in the field.
  
  How could i solve the issue? I have recently asked the question in the discord dev channel and using a console command has been suggested, but since the users need to be sorted in the CP, the string has to manually be entered and then triggered via a button, I couldn't get it to work.
  
  Another approach was to push each save request into a `yii2` job queue but that didn't help either...
  
  Thanks for the help, much appreciated!
  
  **Tags:** plugin-development, craft3, elementtype, usermodel
  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2019-05-07. Score: 3 | Accepted answer
  
  For such large amounts of operations it's best to use a Job. You should only make sure to batch your results and don't fetch every user separately.
  
  ```php
  use Craft;
  use craft\base\Element;
  use craft\db\QueryAbortedException;
  use craft\elements\User;
  use craft\helpers\App;
  use craft\queue\BaseJob;
  use yii\base\Exception;
  
  class ResaveUsers extends BaseJob
  {
      // Properties
      // =========================================================================
  
      /**
       * @var int[] The user Ids
       */
      public $userIds;
  
      /**
       * @var string the reference number
       */
      public $refNumber;
  
      // Public Methods
      // =========================================================================
  
      /**
       * execute
       *
       * @param \craft\queue\QueueInterface|\yii\queue\Queue $queue
       *
       * @throws \Throwable
       * @throws \craft\errors\ElementNotFoundException
       * @throws \yii\base\Exception
       *
       * @author username_1 username_1
       * @since  07.05.2019
       */
      public function execute($queue)
      {
          $query = User::find()->id($this->userIds);
  
          $totalElements = $query->count();
          $currentElement = 0;
  
          $elements = \Craft::$app->getElements();
  
          try {
              $i = 0;
              foreach ($query->each() as $user) {
                  $i ++;
                  $this->setProgress($queue, $currentElement++ / $totalElements);
  
                  $tableValue   = $user->getFieldValue( 'aktionsRefNumber' );
                  $tableValue[] = [
                      'col0' => $this->refNumber . '-' . $i,
                  ];
                  $user->setFieldValue( 'aktionsRefNumber', $tableValue );
  
                  /** @var Element $element */
                  $user->setScenario(Element::SCENARIO_ESSENTIALS);
                  if (!$elements->saveElement($user)) {
                      throw new Exception('Couldn’t save element ' . $user->id . ' (' . get_class($user) . ') due to validation errors.');
                  }
              }
          } catch (QueryAbortedException $e) {
              // Fail silently
          }
      }
  
      // Protected Methods
      // =========================================================================
  
      /**
       * @inheritdoc
       */
      protected function defaultDescription(): string
      {
          return Craft::t('app', 'Resaving {class} elements', [
              'class' => App::humanizeClass(User::class)
          ]);
      }
  }
  
  ```
  
  You can start your job with
  
  ```php
  Craft::$app->getQueue()->push(new ResaveUsers([
       'userIds' => [1,2,4,5,6],
       'refNumber' => 'foobar'
  ]));
  
  ```
  
  > **Comments:**
  > **username_0**: as always on point ...thanks username_1  
  
  ---
  
  