Skip to content

Commit

Permalink
[BUGFIX] Preserve order of elements
Browse files Browse the repository at this point in the history
In order to prepare a future sorting of elements,
a usort function was used together with a new
sorting key to order the elements.

However, this led to arbitrary order of elements
in PHP version less than 8. In php 8 a stable
sorting was introduced.

To prevent a changing order when updating, a
stable usort has been added, which takes effect
on lower php versions.
  • Loading branch information
nhovratov committed Dec 22, 2021
1 parent 93fdf3d commit 5afeefe
Show file tree
Hide file tree
Showing 3 changed files with 241 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Classes/Definition/ElementDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public static function createFromArray(array $elementArray, string $table): Elem
$elementDefinition->descriptions = $elementArray['descriptions'] ?? [];
$elementDefinition->options = $elementArray['options'] ?? [];
$elementDefinition->hidden = !empty($elementArray['hidden']);
$elementDefinition->sorting = $elementArray['sorting'] ?? 0;
$elementDefinition->sorting = (int)($elementArray['sorting'] ?? 0);

return $elementDefinition;
}
Expand Down
33 changes: 29 additions & 4 deletions Classes/Definition/ElementDefinitionCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,37 @@ public function count(): int
return count($this->definitions);
}

protected function sort($definitions): array
private function sort($definitions): array
{
usort($definitions, static function ($a, $b) {
return $a->sorting <=> $b->sorting;
});
if (PHP_VERSION_ID < 80000) {
$this->stable_usort($definitions, static function ($a, $b) {
return $a->sorting <=> $b->sorting;
});
} else {
usort($definitions, static function ($a, $b) {
return $a->sorting <=> $b->sorting;
});
}

return $definitions;
}

/**
* Taken from https://wiki.php.net/rfc/stable_sorting
*/
private function stable_usort(array &$array, callable $compare): void
{
$arrayAndPos = [];
$pos = 0;
foreach ($array as $value) {
$arrayAndPos[] = [$value, $pos++];
}
usort($arrayAndPos, static function($a, $b) use($compare) {
return $compare($a[0], $b[0]) ?: $a[1] <=> $b[1];
});
$array = [];
foreach ($arrayAndPos as $elem) {
$array[] = $elem[0];
}
}
}
211 changes: 211 additions & 0 deletions Tests/Unit/Definition/ElementDefinitionCollectionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
<?php

declare(strict_types=1);

/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/

namespace MASK\Mask\Tests\Unit\Definition;

use MASK\Mask\Definition\ElementDefinitionCollection;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase;

class ElementDefinitionCollectionTest extends UnitTestCase
{
public function elementsAreSortedBySortingDataProvider(): iterable
{
yield 'Sorting of elements is preserved without sorting set' => [
'json' => [
'element1' => [
'key' => 'element1',
'label' => 'Element1'
],
'element2' => [
'key' => 'element2',
'label' => 'Element2'
],
'element3' => [
'key' => 'element3',
'label' => 'Element3'
],
],
'expected' => [
[
'key' => 'element1',
'label' => 'Element1',
'description' => '',
'shortLabel' => '',
'color' => '',
'icon' => '',
'columns' => [],
'labels' => [],
'descriptions' => [],
'sorting' => 0,
],
[
'key' => 'element2',
'label' => 'Element2',
'description' => '',
'shortLabel' => '',
'color' => '',
'icon' => '',
'columns' => [],
'labels' => [],
'descriptions' => [],
'sorting' => 0,
],
[
'key' => 'element3',
'label' => 'Element3',
'description' => '',
'shortLabel' => '',
'color' => '',
'icon' => '',
'columns' => [],
'labels' => [],
'descriptions' => [],
'sorting' => 0,
],
]
];

yield 'Sorting of elements is preserved with sorting set' => [
'json' => [
'element1' => [
'key' => 'element1',
'label' => 'Element1',
'sorting' => '0',
],
'element2' => [
'key' => 'element2',
'label' => 'Element2',
'sorting' => '0',
],
'element3' => [
'key' => 'element3',
'label' => 'Element3',
'sorting' => '0',
],
],
'expected' => [
[
'key' => 'element1',
'label' => 'Element1',
'description' => '',
'shortLabel' => '',
'color' => '',
'icon' => '',
'columns' => [],
'labels' => [],
'descriptions' => [],
'sorting' => 0,
],
[
'key' => 'element2',
'label' => 'Element2',
'description' => '',
'shortLabel' => '',
'color' => '',
'icon' => '',
'columns' => [],
'labels' => [],
'descriptions' => [],
'sorting' => 0,
],
[
'key' => 'element3',
'label' => 'Element3',
'description' => '',
'shortLabel' => '',
'color' => '',
'icon' => '',
'columns' => [],
'labels' => [],
'descriptions' => [],
'sorting' => 0,
],
]
];

yield 'Sorting of elements is preserved with real sorting set' => [
'json' => [
'element1' => [
'key' => 'element1',
'label' => 'Element1',
'sorting' => '2',
],
'element2' => [
'key' => 'element2',
'label' => 'Element2',
'sorting' => '1',
],
'element3' => [
'key' => 'element3',
'label' => 'Element3',
'sorting' => '0',
],
],
'expected' => [
[
'key' => 'element3',
'label' => 'Element3',
'description' => '',
'shortLabel' => '',
'color' => '',
'icon' => '',
'columns' => [],
'labels' => [],
'descriptions' => [],
'sorting' => 0,
],
[
'key' => 'element2',
'label' => 'Element2',
'description' => '',
'shortLabel' => '',
'color' => '',
'icon' => '',
'columns' => [],
'labels' => [],
'descriptions' => [],
'sorting' => 1,
],
[
'key' => 'element1',
'label' => 'Element1',
'description' => '',
'shortLabel' => '',
'color' => '',
'icon' => '',
'columns' => [],
'labels' => [],
'descriptions' => [],
'sorting' => 2,
],
]
];
}

/**
* @dataProvider elementsAreSortedBySortingDataProvider
* @test
*/
public function elementsAreSortedBySorting(array $json, array $expected): void
{
$elements = [];
foreach (ElementDefinitionCollection::createFromArray($json, 'tt_content') as $element) {
$elements[] = $element->toArray();
}
self::assertEquals($expected, $elements);
}
}

0 comments on commit 5afeefe

Please sign in to comment.