• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

FluidTYPO3 / vhs / 13566190336

27 Feb 2025 12:18PM UTC coverage: 72.127% (-0.6%) from 72.746%
13566190336

push

github

NamelessCoder
[TER] 7.1.0

5649 of 7832 relevant lines covered (72.13%)

20.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

51.14
/Classes/ViewHelpers/Iterator/SortViewHelper.php
1
<?php
2
namespace FluidTYPO3\Vhs\ViewHelpers\Iterator;
3

4
/*
5
 * This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
6
 *
7
 * For the full copyright and license information, please read the
8
 * LICENSE.md file that was distributed with this source code.
9
 */
10

11
use FluidTYPO3\Vhs\Traits\ArrayConsumingViewHelperTrait;
12
use FluidTYPO3\Vhs\Traits\CompileWithRenderStatic;
13
use FluidTYPO3\Vhs\Traits\TemplateVariableViewHelperTrait;
14
use FluidTYPO3\Vhs\Utility\ErrorUtility;
15
use TYPO3\CMS\Core\Utility\GeneralUtility;
16
use TYPO3\CMS\Extbase\Persistence\Generic\LazyObjectStorage;
17
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
18
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
19
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
20
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
21
use TYPO3Fluid\Fluid\Core\ViewHelper\Exception;
22

23
/**
24
 * Sorts an instance of ObjectStorage, an Iterator implementation,
25
 * an Array or a QueryResult (including Lazy counterparts).
26
 *
27
 * Can be used inline, i.e.:
28
 *
29
 * ```
30
 * <f:for each="{dataset -> v:iterator.sort(sortBy: 'name')}" as="item">
31
 *     // iterating data which is ONLY sorted while rendering this particular loop
32
 * </f:for>
33
 * ```
34
 */
35
class SortViewHelper extends AbstractViewHelper
36
{
37
    use TemplateVariableViewHelperTrait;
38
    use ArrayConsumingViewHelperTrait;
39
    use CompileWithRenderStatic;
40

41
    /**
42
     * @var boolean
43
     */
44
    protected $escapeChildren = false;
45

46
    /**
47
     * @var boolean
48
     */
49
    protected $escapeOutput = false;
50

51
    /**
52
     * Contains all flags that are allowed to be used
53
     * with the sorting functions
54
     *
55
     * @var array
56
     */
57
    protected static $allowedSortFlags = [
58
        'SORT_REGULAR',
59
        'SORT_STRING',
60
        'SORT_NUMERIC',
61
        'SORT_NATURAL',
62
        'SORT_LOCALE_STRING',
63
        'SORT_FLAG_CASE'
64
    ];
65

66
    public function initializeArguments(): void
67
    {
68
        $this->registerArgument('subject', 'mixed', 'The array/Traversable instance to sort');
7✔
69
        $this->registerArgument(
7✔
70
            'sortBy',
7✔
71
            'string',
7✔
72
            'Which property/field to sort by - leave out for numeric sorting based on indexes(keys)'
7✔
73
        );
7✔
74
        $this->registerArgument(
7✔
75
            'order',
7✔
76
            'string',
7✔
77
            'ASC, DESC, RAND or SHUFFLE. RAND preserves keys, SHUFFLE does not - but SHUFFLE is faster',
7✔
78
            false,
7✔
79
            'ASC'
7✔
80
        );
7✔
81
        $this->registerArgument(
7✔
82
            'sortFlags',
7✔
83
            'string',
7✔
84
            'Constant name from PHP for `SORT_FLAGS`: `SORT_REGULAR`, `SORT_STRING`, `SORT_NUMERIC`, ' .
7✔
85
            '`SORT_NATURAL`, `SORT_LOCALE_STRING` or `SORT_FLAG_CASE`. You can provide a comma seperated list or ' .
7✔
86
            'array to use a combination of flags.',
7✔
87
            false,
7✔
88
            'SORT_REGULAR'
7✔
89
        );
7✔
90
        $this->registerAsArgument();
7✔
91
    }
92

93
    /**
94
     * "Render" method - sorts a target list-type target. Either $array or
95
     * $objectStorage must be specified. If both are, ObjectStorage takes precedence.
96
     *
97
     * Returns the same type as $subject. Ignores NULL values which would be
98
     * OK to use in an f:for (empty loop as result)
99
     *
100
     * @return mixed
101
     */
102
    public static function renderStatic(
103
        array $arguments,
104
        \Closure $renderChildrenClosure,
105
        RenderingContextInterface $renderingContext
106
    ) {
107
        /** @var string|null $as */
108
        $as = $arguments['as'] ?? null;
7✔
109
        $candidate = $arguments['subject'] ?? $renderChildrenClosure();
7✔
110
        if ($candidate instanceof ObjectStorage) {
7✔
111
            $sorted = static::sortObjectStorage($candidate, $arguments);
×
112
        } elseif (!is_iterable($candidate)) {
7✔
113
            throw new Exception(
×
114
                'v:iterator.sort requires an "iterable" object or array, as "subject" argument or as child node.',
×
115
                1690469444
×
116
            );
×
117
        } else {
118
            $subject = static::arrayFromArrayOrTraversableOrCSVStatic($candidate);
7✔
119
            $sorted = static::sortArray($subject, $arguments);
7✔
120
        }
121

122
        return static::renderChildrenWithVariableOrReturnInputStatic(
×
123
            $sorted,
×
124
            $as,
×
125
            $renderingContext,
×
126
            $renderChildrenClosure
×
127
        );
×
128
    }
129

130
    /**
131
     * Sort an array
132
     *
133
     * @param array|\Iterator $array
134
     * @param array $arguments
135
     * @return array
136
     */
137
    protected static function sortArray($array, $arguments)
138
    {
139
        $sorted = [];
7✔
140
        foreach ($array as $index => $object) {
7✔
141
            if (isset($arguments['sortBy'])) {
7✔
142
                $index = static::getSortValue($object, $arguments);
×
143
            }
144
            while (isset($sorted[$index])) {
7✔
145
                $index .= '.1';
×
146
            }
147
            $sorted[$index] = $object;
7✔
148
        }
149
        if ('ASC' === $arguments['order']) {
7✔
150
            ksort($sorted, static::getSortFlags($arguments));
7✔
151
        } elseif ('RAND' === $arguments['order']) {
×
152
            $sortedKeys = array_keys($sorted);
×
153
            shuffle($sortedKeys);
×
154
            $backup = $sorted;
×
155
            $sorted = [];
×
156
            foreach ($sortedKeys as $sortedKey) {
×
157
                $sorted[$sortedKey] = $backup[$sortedKey];
×
158
            }
159
        } elseif ('SHUFFLE' === $arguments['order']) {
×
160
            shuffle($sorted);
×
161
        } else {
162
            krsort($sorted, static::getSortFlags($arguments));
×
163
        }
164
        return $sorted;
×
165
    }
166

167
    /**
168
     * Sort an ObjectStorage instance
169
     *
170
     * @param ObjectStorage<object> $storage
171
     * @param array $arguments
172
     * @return ObjectStorage
173
     */
174
    protected static function sortObjectStorage($storage, $arguments)
175
    {
176
        /** @var ObjectStorage $temp */
177
        $temp = GeneralUtility::makeInstance(ObjectStorage::class);
×
178
        foreach ($storage as $item) {
×
179
            $temp->attach($item);
×
180
        }
181
        $sorted = static::sortArray($storage, $arguments);
×
182
        /** @var ObjectStorage $storage */
183
        $storage = GeneralUtility::makeInstance(ObjectStorage::class);
×
184
        foreach ($sorted as $item) {
×
185
            $storage->attach($item);
×
186
        }
187
        return $storage;
×
188
    }
189

190
    /**
191
     * Gets the value to use as sorting value from $object
192
     *
193
     * @param array|object $object
194
     * @param array $arguments
195
     * @return mixed
196
     */
197
    protected static function getSortValue($object, $arguments)
198
    {
199
        $field = $arguments['sortBy'];
×
200
        $value = ObjectAccess::getPropertyPath($object, $field);
×
201
        if ($value instanceof \DateTimeInterface) {
×
202
            $value = (integer) $value->format('U');
×
203
        } elseif ($value instanceof ObjectStorage || $value instanceof LazyObjectStorage) {
×
204
            $value = $value->count();
×
205
        } elseif (is_array($value)) {
×
206
            $value = count($value);
×
207
        }
208
        return $value;
×
209
    }
210

211
    /**
212
     * Parses the supplied flags into the proper value for the sorting
213
     * function.
214
     *
215
     * @param array $arguments
216
     * @return int
217
     * @throws Exception
218
     */
219
    protected static function getSortFlags($arguments)
220
    {
221
        $constants = static::arrayFromArrayOrTraversableOrCSVStatic($arguments['sortFlags']);
7✔
222
        $flags = 0;
7✔
223
        foreach ($constants as $constant) {
7✔
224
            if (!in_array($constant, static::$allowedSortFlags)) {
7✔
225
                ErrorUtility::throwViewHelperException(
7✔
226
                    'The constant "' . $constant . '" you\'re trying to use as a sortFlag is not allowed. Allowed ' .
7✔
227
                    'constants are: ' . implode(', ', static::$allowedSortFlags) . '.',
7✔
228
                    1404220538
7✔
229
                );
7✔
230
            }
231
            $flags = $flags | constant(trim($constant));
×
232
        }
233
        return $flags;
×
234
    }
235
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc