diff --git a/CHANGELOG b/CHANGELOG index 84c651c5f..f4e4c145c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,6 +19,8 @@ * 2.12.0 (2019-XX-XX) + * added the spaceship operator ("<=>"), useful when using an arrow function in the "sort" filter + * added support for an "arrow" function on the "sort" filter * added the CssInliner extension in the "extra" repositories: "inline_css" filter * added the Inky extension in the "extra" repositories: "inky" filter diff --git a/doc/filters/sort.rst b/doc/filters/sort.rst index f7c0329fe..bf3aab09f 100644 --- a/doc/filters/sort.rst +++ b/doc/filters/sort.rst @@ -1,6 +1,9 @@ ``sort`` ======== +.. versionadded:: 2.12 + The ``arrow`` argument was added in Twig 2.12. + The ``sort`` filter sorts an array: .. code-block:: twig @@ -15,4 +18,28 @@ The ``sort`` filter sorts an array: association. It supports Traversable objects by transforming those to arrays. +You can pass an arrow function to sort the array: + +.. code-block:: twig + + {% set list = [ + { name: 'Apples', quantity: 5 }, + { name: 'Oranges', quantity: 2 }, + { name: 'Grapes', quantity: 4 }, + ] %} + + {% for fruit in fruits|sort((a, b) => a.quantity <=> b.quantity)|column('name') %} + {{ fruit }} + {% endfor %} + + {# output in this order: Oranges, Grapes, Apples #} + +Note the usage of the `spaceship`_ operator to simplify the comparison. + +Arguments +--------- + +* ``arrow``: An arrow function + .. _`asort`: https://secure.php.net/asort +.. _`spaceship`: https://www.php.net/manual/en/language.operators.comparison.php diff --git a/doc/templates.rst b/doc/templates.rst index 28125bdee..d9243f6b8 100644 --- a/doc/templates.rst +++ b/doc/templates.rst @@ -504,9 +504,9 @@ Twig allows expressions everywhere. The operator precedence is as follows, with the lowest-precedence operators listed first: ``?:`` (ternary operator), ``b-and``, ``b-xor``, ``b-or``, - ``or``, ``and``, ``==``, ``!=``, ``<``, ``>``, ``>=``, ``<=``, ``in``, - ``matches``, ``starts with``, ``ends with``, ``..``, ``+``, ``-``, ``~``, - ``*``, ``/``, ``//``, ``%``, ``is`` (tests), ``**``, ``??``, ``|`` + ``or``, ``and``, ``==``, ``!=``, ``<=>``, ``<``, ``>``, ``>=``, ``<=``, + ``in``, ``matches``, ``starts with``, ``ends with``, ``..``, ``+``, ``-``, + ``~``, ``*``, ``/``, ``//``, ``%``, ``is`` (tests), ``**``, ``??``, ``|`` (filters), ``[]``, and ``.``: .. code-block:: twig diff --git a/src/Extension/CoreExtension.php b/src/Extension/CoreExtension.php index 823371de3..3c5d8d474 100644 --- a/src/Extension/CoreExtension.php +++ b/src/Extension/CoreExtension.php @@ -34,6 +34,7 @@ use Twig\Node\Expression\Binary\NotInBinary; use Twig\Node\Expression\Binary\OrBinary; use Twig\Node\Expression\Binary\PowerBinary; use Twig\Node\Expression\Binary\RangeBinary; +use Twig\Node\Expression\Binary\SpaceshipBinary; use Twig\Node\Expression\Binary\StartsWithBinary; use Twig\Node\Expression\Binary\SubBinary; use Twig\Node\Expression\Filter\DefaultFilter; @@ -273,6 +274,7 @@ final class CoreExtension extends AbstractExtension 'b-and' => ['precedence' => 18, 'class' => BitwiseAndBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], '==' => ['precedence' => 20, 'class' => EqualBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], '!=' => ['precedence' => 20, 'class' => NotEqualBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], + '<=>' => ['precedence' => 20, 'class' => SpaceshipBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], '<' => ['precedence' => 20, 'class' => LessBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], '>' => ['precedence' => 20, 'class' => GreaterBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], '>=' => ['precedence' => 20, 'class' => GreaterEqualBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], @@ -866,7 +868,7 @@ function twig_reverse_filter(Environment $env, $item, $preserveKeys = false) * * @return array */ -function twig_sort_filter($array) +function twig_sort_filter($array, $arrow = null) { if ($array instanceof \Traversable) { $array = iterator_to_array($array); @@ -874,7 +876,11 @@ function twig_sort_filter($array) throw new RuntimeError(sprintf('The sort filter only works with arrays or "Traversable", got "%s".', \gettype($array))); } - asort($array); + if (null !== $arrow) { + uasort($array, $arrow); + } else { + asort($array); + } return $array; } diff --git a/src/Node/Expression/Binary/SpaceshipBinary.php b/src/Node/Expression/Binary/SpaceshipBinary.php new file mode 100644 index 000000000..5245e4051 --- /dev/null +++ b/src/Node/Expression/Binary/SpaceshipBinary.php @@ -0,0 +1,22 @@ +raw('<=>'); + } +} diff --git a/tests/Fixtures/filters/sort_with_arrow.test b/tests/Fixtures/filters/sort_with_arrow.test new file mode 100644 index 000000000..a8f977d55 --- /dev/null +++ b/tests/Fixtures/filters/sort_with_arrow.test @@ -0,0 +1,16 @@ +--TEST-- +"sort" filter +--TEMPLATE-- +{{ fruits|sort((a, b) => a.quantity == b.quantity ? 0 : (a.quantity > b.quantity ? 1 : -1))|column('name')|join(', ') }} +{{ fruits|sort((a, b) => a.quantity <=> b.quantity)|column('name')|join(', ') }} +--DATA-- +return [ + 'fruits' => [ + [ 'name' => 'Apples', 'quantity' => 5 ], + [ 'name' => 'Oranges', 'quantity' => 2 ], + [ 'name' => 'Grapes', 'quantity' => 4 ], + ], +] +--EXPECT-- +Oranges, Grapes, Apples +Oranges, Grapes, Apples