<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>

CC BY-SA 3.0
2018-03-04T18:16:35.317Z
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?
iRector
CC BY-SA 3.0
2018-03-04T18:56:45.620Z
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);`
Robin Schambach
CC BY-SA 3.0
2018-03-04T19:01:07.863Z
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
Robin Schambach
CC BY-SA 3.0
2018-03-06T02:52:42.577Z
Haven't actually tried this yet, but I know it would work. Accepting answer.
iRector
CC BY-SA 3.0
2018-03-04T17:42:57.890Z
2018-03-04T17:52:01.457Z
2018-03-04T17:52:01.457Z
Robin Schambach
<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>

<plugin-development><entries><categories><plugins><control-panel>
Sort CP Entries by Category
# 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.BaseElementIndexand the updateElements 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 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
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 or this. 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.  

---

