Answers.Body: <p>This is probably not the answer you are looking for but it works. You can change the <code>Craft.BaseElementIndex</code>and the <a href="https://github.com/craftcms/cms/blob/develop-v2/src/resources/js/craft.js#L2081" rel="nofollow noreferrer">updateElements</a> function. All you have to do is to change the destination path to a custom controller</p>
  
  <pre><code>Craft.BaseElementIndex.prototype.updateElements = function()
  {
      // Ignore if we're not fully initialized yet
      if (!this.initialized)
      {
          return;
      }
  
      this.setIndexBusy();
  
      var params = this.getViewParams();
  
      Craft.postActionRequest('yourPluginHandle/controllerName/getElements', params, $.proxy(function(response, textStatus)
      {
          this.setIndexAvailable();
  
          if (textStatus == 'success')
          {
              this._updateView(params, response);
          }
          else
          {
              Craft.cp.displayError(Craft.t('An unknown error occurred.'));
          }
  
      }, this));
  },
  </code></pre>
  
  <p>Then you can create a custom controller that extends the <code>ElementIndexController</code> and can use it's <a href="https://github.com/craftcms/cms/blob/develop-v2/src/controllers/ElementIndexController.php#L102" rel="nofollow noreferrer">function</a> that calls the private function <code>_getElementResponseData</code>. All the elements that should be displayed are received in</p>
  
  <pre><code>$responseData['html'] = $this-&gt;_elementType-&gt;getIndexHtml(
      $this-&gt;_criteria,
      $disabledElementIds,
      $this-&gt;_viewState, &lt;-- this is the important variable
      $this-&gt;_sourceKey,
      $this-&gt;_context,
      $includeContainer,
      $showCheckboxes
  );
  </code></pre>
  
  <p>The sort order is defined <a href="https://github.com/craftcms/cms/blob/develop-v2/src/elementtypes/BaseElementType.php#L203" rel="nofollow noreferrer">here</a></p>
  
  <pre><code>if ($sortableAttributes)
  {
      $order = (!empty($viewState['order']) &amp;&amp; isset($sortableAttributes[$viewState['order']])) ? $viewState['order'] : ArrayHelper::getFirstKey($sortableAttributes);
      $sort  = (!empty($viewState['sort']) &amp;&amp; in_array($viewState['sort'], array('asc', 'desc'))) ? $viewState['sort'] : 'asc';
  
      $criteria-&gt;order = preg_replace('/^(.*?)(?:\s+(?:asc|desc))?(,.*)?$/i', "$1 {$sort}$2", $order);
  }
  </code></pre>
  
  <p>As you can easily see it checks if the <code>$viewState['order']</code> is empty or not. This variable is defined in your controller so all you have to do is to check if your javascript returns a <code>order</code> variable, get the field by handle and check if the field handle is <code>instance of BaseRelationField</code>. If so, you'll unset the variable in the <code>$viewState</code> and change the <code>$criteria</code> by yourself. </p>
  
  <p>Unfortunately the query is executed in <code>BaseElementType.php</code> so you have to implement a way to sort by eager loading elements (which I doubt is possible by default) or you can create a custom <code>element type</code> just for this case and overwrite the entire <code>getIndexHtml()</code> function.</p>
  
  <p>It might sound like a lot of work and effort but honestly it can be done in 5-10 minutes... I've done the same once within no time. It's 99% copy paste. Maybe someone  has a better answer but since there is no real event or hook I'm not sure if there is a real clean way to do this. Of course this is only for Craft 2. In Craft 3 it's much easier since there are more events and hooks and you can replace classes more easy.</p>
  
  <p>Another approach would be to overwrite the entire <code>BaseElementIndex</code> class with <a href="http://www.yiiframework.com/forum/index.php/topic/28979-how-to-override-yii-core-classes/" rel="nofollow noreferrer">Yii</a> or <a href="https://stackoverflow.com/questions/11254106/yii-how-to-override-extend-core-file">this</a>. But I never did that so I can't give much advice</p>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2018-03-04T18:16:35.317Z
Answers.Text: Thank you so much! That's exactly the type of work around I'm needing. This all makes sense, but how exactly do you alter the criteria to order by category? Would I need to write custom SQL outside of the ElementAPI?
Answers.UserDisplayName: iRector
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2018-03-04T18:56:45.620Z
Answers.Text: That's the question. I searched a little bit but the `Elements` `getEagerLoadingMap` includes the eager loading elements directly into the query. So I don't think you can include the sort order directly into the `ElementCriteriaModel`. Thus you'll need to create a custom element type in order to overwrite the entire `getIndexHtml` and reorder the entries before `$criteria->find()` is executed. I searched the last 30 minutes but every question of this type is unanswered at stack exchange. I think you have to create your own `Query` with `$db = craft()->elements->buildElementsQuery($criteria);`
Answers.UserDisplayName: Robin Schambach
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2018-03-04T19:01:07.863Z
Answers.Text: http://www.yiiframework.com/doc/guide/1.1/en/database.query-builder, join Crafts `relation` table with the correct field Id on your entry id and order your entries by the title. I can provide a working example in about 12h if you are not able to make it. Edit: you can see how to join related entries here https://craftcms.stackexchange.com/questions/22833/querying-channel-entries-hits-memory-limit/22844#22844
Answers.UserDisplayName: Robin Schambach
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2018-03-06T02:52:42.577Z
Answers.Text: Haven't actually tried this yet, but I know it would work. Accepting answer.
Answers.UserDisplayName: iRector
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2018-03-04T17:42:57.890Z
Answers.LastActivityDate: 2018-03-04T17:52:01.457Z
Answers.LastEditDate: 2018-03-04T17:52:01.457Z
Answers.UserDisplayName: Robin Schambach
Body: <p>I know it's possible, using <code>modifyEntrySortableAttributes</code>, to sort the CP entries table by a custom field, when that field is a number, string, date/time, or bool. Would it be possible to sort a CP table by category? If not, is there another hook, or some other way I could implement this functionality?</p>
  
  <p><br>
  Sorting by number, string, date/time, bool:<br>
  <a href="https://craftcms.stackexchange.com/questions/13472/in-craft-2-5-how-can-you-sort-the-entry-index-page-by-a-custom-field">In Craft 2.5, how can you sort the entry index page by a custom field?</a></p>
  
Tags: <plugin-development><entries><categories><plugins><control-panel>
Title: Sort CP Entries by Category
ThreadText: # Sort CP Entries by Category
  
  (Asked by: username_0 on 2018-03-04)
  
  I know it's possible, using `modifyEntrySortableAttributes`, to sort the CP entries table by a custom field, when that field is a number, string, date/time, or bool. Would it be possible to sort a CP table by category? If not, is there another hook, or some other way I could implement this functionality?
  
  Sorting by number, string, date/time, bool:  
  [In Craft 2.5, how can you sort the entry index page by a custom field?](https://craftcms.stackexchange.com/questions/13472/in-craft-2-5-how-can-you-sort-the-entry-index-page-by-a-custom-field)
  
  **Tags:** plugin-development, entries, categories, plugins, control-panel
  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2018-03-04. Score: 2 | Accepted answer
  
  This is probably not the answer you are looking for but it works. You can change the `Craft.BaseElementIndex`and the [updateElements](https://github.com/craftcms/cms/blob/develop-v2/src/resources/js/craft.js#L2081) function. All you have to do is to change the destination path to a custom controller
  
  ```
  Craft.BaseElementIndex.prototype.updateElements = function()
  {
      // Ignore if we're not fully initialized yet
      if (!this.initialized)
      {
          return;
      }
  
      this.setIndexBusy();
  
      var params = this.getViewParams();
  
      Craft.postActionRequest('yourPluginHandle/controllerName/getElements', params, $.proxy(function(response, textStatus)
      {
          this.setIndexAvailable();
  
          if (textStatus == 'success')
          {
              this._updateView(params, response);
          }
          else
          {
              Craft.cp.displayError(Craft.t('An unknown error occurred.'));
          }
  
      }, this));
  },
  
  ```
  
  Then you can create a custom controller that extends the `ElementIndexController` and can use it's [function](https://github.com/craftcms/cms/blob/develop-v2/src/controllers/ElementIndexController.php#L102) that calls the private function `_getElementResponseData`. All the elements that should be displayed are received in
  
  ```
  $responseData['html'] = $this->_elementType->getIndexHtml(
      $this->_criteria,
      $disabledElementIds,
      $this->_viewState, <-- this is the important variable
      $this->_sourceKey,
      $this->_context,
      $includeContainer,
      $showCheckboxes
  );
  
  ```
  
  The sort order is defined [here](https://github.com/craftcms/cms/blob/develop-v2/src/elementtypes/BaseElementType.php#L203)
  
  ```
  if ($sortableAttributes)
  {
      $order = (!empty($viewState['order']) && isset($sortableAttributes[$viewState['order']])) ? $viewState['order'] : ArrayHelper::getFirstKey($sortableAttributes);
      $sort  = (!empty($viewState['sort']) && in_array($viewState['sort'], array('asc', 'desc'))) ? $viewState['sort'] : 'asc';
  
      $criteria->order = preg_replace('/^(.*?)(?:\s+(?:asc|desc))?(,.*)?$/i', "$1 {$sort}$2", $order);
  }
  
  ```
  
  As you can easily see it checks if the `$viewState['order']` is empty or not. This variable is defined in your controller so all you have to do is to check if your javascript returns a `order` variable, get the field by handle and check if the field handle is `instance of BaseRelationField`. If so, you'll unset the variable in the `$viewState` and change the `$criteria` by yourself.
  
  Unfortunately the query is executed in `BaseElementType.php` so you have to implement a way to sort by eager loading elements (which I doubt is possible by default) or you can create a custom `element type` just for this case and overwrite the entire `getIndexHtml()` function.
  
  It might sound like a lot of work and effort but honestly it can be done in 5-10 minutes... I've done the same once within no time. It's 99% copy paste. Maybe someone has a better answer but since there is no real event or hook I'm not sure if there is a real clean way to do this. Of course this is only for Craft 2. In Craft 3 it's much easier since there are more events and hooks and you can replace classes more easy.
  
  Another approach would be to overwrite the entire `BaseElementIndex` class with [Yii](http://www.yiiframework.com/forum/index.php/topic/28979-how-to-override-yii-core-classes/) or [this](https://stackoverflow.com/questions/11254106/yii-how-to-override-extend-core-file). But I never did that so I can't give much advice
  
  > **Comments:**
  > **username_0**: Thank you so much! That's exactly the type of work around I'm needing. This all makes sense, but how exactly do you alter the criteria to order by category? Would I need to write custom SQL outside of the ElementAPI?  
  > **username_1**: That's the question. I searched a little bit but the `Elements` `getEagerLoadingMap` includes the eager loading elements directly into the query. So I don't think you can include the sort order directly into the `ElementCriteriaModel`. Thus you'll need to create a custom element type in order to overwrite the entire `getIndexHtml` and reorder the entries before `$criteria->find()` is executed. I searched the last 30 minutes but every question of this type is unanswered at stack exchange. I think you have to create your own `Query` with `$db = craft()->elements->buildElementsQuery($criteria);`  
  > **username_1**: http://www.yiiframework.com/doc/guide/1.1/en/database.query-builder, join Crafts `relation` table with the correct field Id on your entry id and order your entries by the title. I can provide a working example in about 12h if you are not able to make it. Edit: you can see how to join related entries here https://craftcms.stackexchange.com/questions/22833/querying-channel-entries-hits-memory-limit/22844#22844  
  > **username_0**: Haven't actually tried this yet, but I know it would work. Accepting answer.  
  
  ---
  
  