silverstripe-framework: SS4: Cannot sort `many_many` result when using `through`

Unsure if this is expected behaviour, or missing functionality. Please let me know 😃

Setup

I have a class (Locale) that has a many_many relationship to itself. This is being run through a class called FallbackLocale.

Locale:

class Locale extends DataObject
{
    private static $has_many = [
        'FallbackLocales' => FallbackLocale::class . '.Parent',
    ];

    private static $many_many = [
        'Fallbacks' => [
            'through' => FallbackLocale::class,
            'from' => 'Parent',
            'to' => 'Locale',
        ],
    ];
}

FallbackLocale:

class FallbackLocale extends DataObject
{
    private static $has_one = [
        'Parent' => Locale::class,
        'Locale' => Locale::class,
    ];

    private static $db = [
        'Sort' => 'Int',
    ];

    private static $default_sort = 'Sort';
}

Scenario

I create a new Locale through a very standard Model Admin with a Gridfield.

I add two Fallback() records, and I sort them.

Whenever I use $locale->Fallbacks() (or $locale->Fallback()->sort('Sort')), the records are always in ID order, not the Gridfield Sort order.

Workaround

If I use $locale->FallbackLocales() instead, I get the correct Gridfield sorting - however, I am then required to go an extra step to get the Object I’m actually interested in.

EG:

foreach ($locale->FallbackLocales() as $throughObject) {
    $throughObject->Locale();
}

Or: I need to add a join before I sort.

Pull requests

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 15 (15 by maintainers)

Most upvoted comments

You know, I had a brain wave…

default_sort is set by DataQuery::initialiseQuery().

We could build in a bit of detection for many_many through, so that if the target default_sort is empty, it could check and inherit default_sort from the mapping table.

if ($sort = singleton($this->dataClass)->config()->get('default_sort')) {
    $this->sort($sort);
} elseif ($sort = singleton($this->joinClass)->config()->get('default_sort')) {
    $this->sort($sort);
}

And have joinClass injected by many_many through list.

That would make your first example ā€œjust workā€.