1
|
|
|
<?php |
2
|
|
|
declare(strict_types=1); |
3
|
|
|
namespace TYPO3Fluid\Fluid\ViewHelpers; |
4
|
|
|
|
5
|
|
|
/* |
6
|
|
|
* This file belongs to the package "TYPO3 Fluid". |
7
|
|
|
* See LICENSE.txt that was shipped with this package. |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; |
11
|
|
|
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; |
12
|
|
|
use TYPO3Fluid\Fluid\Core\ViewHelper\Exception; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Loop view helper which can be used to iterate over arrays. |
16
|
|
|
* Implements what a basic foreach()-PHP-method does. |
17
|
|
|
* |
18
|
|
|
* = Examples = |
19
|
|
|
* |
20
|
|
|
* <code title="Simple Loop"> |
21
|
|
|
* <f:for each="{0:1, 1:2, 2:3, 3:4}" as="foo">{foo}</f:for> |
22
|
|
|
* </code> |
23
|
|
|
* <output> |
24
|
|
|
* 1234 |
25
|
|
|
* </output> |
26
|
|
|
* |
27
|
|
|
* <code title="Output array key"> |
28
|
|
|
* <ul> |
29
|
|
|
* <f:for each="{fruit1: 'apple', fruit2: 'pear', fruit3: 'banana', fruit4: 'cherry'}" as="fruit" key="label"> |
30
|
|
|
* <li>{label}: {fruit}</li> |
31
|
|
|
* </f:for> |
32
|
|
|
* </ul> |
33
|
|
|
* </code> |
34
|
|
|
* <output> |
35
|
|
|
* <ul> |
36
|
|
|
* <li>fruit1: apple</li> |
37
|
|
|
* <li>fruit2: pear</li> |
38
|
|
|
* <li>fruit3: banana</li> |
39
|
|
|
* <li>fruit4: cherry</li> |
40
|
|
|
* </ul> |
41
|
|
|
* </output> |
42
|
|
|
* |
43
|
|
|
* <code title="Iteration information"> |
44
|
|
|
* <ul> |
45
|
|
|
* <f:for each="{0:1, 1:2, 2:3, 3:4}" as="foo" iteration="fooIterator"> |
46
|
|
|
* <li>Index: {fooIterator.index} Cycle: {fooIterator.cycle} Total: {fooIterator.total}{f:if(condition: fooIterator.isEven, then: ' Even')}{f:if(condition: fooIterator.isOdd, then: ' Odd')}{f:if(condition: fooIterator.isFirst, then: ' First')}{f:if(condition: fooIterator.isLast, then: ' Last')}</li> |
47
|
|
|
* </f:for> |
48
|
|
|
* </ul> |
49
|
|
|
* </code> |
50
|
|
|
* <output> |
51
|
|
|
* <ul> |
52
|
|
|
* <li>Index: 0 Cycle: 1 Total: 4 Odd First</li> |
53
|
|
|
* <li>Index: 1 Cycle: 2 Total: 4 Even</li> |
54
|
|
|
* <li>Index: 2 Cycle: 3 Total: 4 Odd</li> |
55
|
|
|
* <li>Index: 3 Cycle: 4 Total: 4 Even Last</li> |
56
|
|
|
* </ul> |
57
|
|
|
* </output> |
58
|
|
|
*/ |
59
|
|
|
class ForViewHelper extends AbstractViewHelper |
60
|
|
|
{ |
61
|
|
|
protected $escapeOutput = false; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @return void |
65
|
|
|
*/ |
66
|
|
View Code Duplication |
public function initializeArguments() |
|
|
|
|
67
|
|
|
{ |
68
|
|
|
parent::initializeArguments(); |
69
|
|
|
$this->registerArgument('each', 'array', 'The array or \SplObjectStorage to iterated over', true); |
70
|
|
|
$this->registerArgument('as', 'string', 'The name of the iteration variable', true); |
71
|
|
|
$this->registerArgument('key', 'string', 'Variable to assign array key to'); |
72
|
|
|
$this->registerArgument('reverse', 'boolean', 'If TRUE, iterates in reverse', false, false); |
73
|
|
|
$this->registerArgument('iteration', 'string', 'The name of the variable to store iteration information (index, cycle, isFirst, isLast, isEven, isOdd)'); |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
public function evaluate(RenderingContextInterface $renderingContext) |
77
|
|
|
{ |
78
|
|
|
$templateVariableContainer = $renderingContext->getVariableProvider(); |
79
|
|
|
$arguments = $this->getArguments()->setRenderingContext($renderingContext)->getArrayCopy(); |
80
|
|
|
if (!isset($arguments['each'])) { |
81
|
|
|
return ''; |
82
|
|
|
} |
83
|
|
|
if (is_object($arguments['each']) && !$arguments['each'] instanceof \Traversable) { |
84
|
|
|
throw new Exception('ForViewHelper only supports arrays and objects implementing \Traversable interface', 1248728393); |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
if ($arguments['reverse'] === true) { |
88
|
|
|
// array_reverse only supports arrays |
89
|
|
|
if (is_object($arguments['each'])) { |
90
|
|
|
/** @var $each \Traversable */ |
91
|
|
|
$each = $arguments['each']; |
92
|
|
|
$arguments['each'] = iterator_to_array($each); |
93
|
|
|
} |
94
|
|
|
$arguments['each'] = array_reverse($arguments['each'], true); |
95
|
|
|
} |
96
|
|
|
$iterationData = []; |
97
|
|
|
if (isset($arguments['iteration'])) { |
98
|
|
|
$iterationData = [ |
99
|
|
|
'index' => 0, |
100
|
|
|
'cycle' => 1, |
101
|
|
|
'total' => count($arguments['each']) |
102
|
|
|
]; |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
$output = ''; |
106
|
|
|
foreach ($arguments['each'] as $keyValue => $singleElement) { |
107
|
|
|
$templateVariableContainer->add($arguments['as'], $singleElement); |
108
|
|
|
if (isset($arguments['key'])) { |
109
|
|
|
$templateVariableContainer->add($arguments['key'], $keyValue); |
110
|
|
|
} |
111
|
|
|
if (isset($arguments['iteration'])) { |
112
|
|
|
$iterationData['isFirst'] = $iterationData['cycle'] === 1; |
113
|
|
|
$iterationData['isLast'] = $iterationData['cycle'] === $iterationData['total']; |
114
|
|
|
$iterationData['isEven'] = $iterationData['cycle'] % 2 === 0; |
115
|
|
|
$iterationData['isOdd'] = !$iterationData['isEven']; |
116
|
|
|
$templateVariableContainer->add($arguments['iteration'], $iterationData); |
117
|
|
|
$iterationData['index']++; |
118
|
|
|
$iterationData['cycle']++; |
119
|
|
|
} |
120
|
|
|
$output .= $this->evaluateChildren($renderingContext); |
121
|
|
|
$templateVariableContainer->remove($arguments['as']); |
122
|
|
|
if (isset($arguments['key'])) { |
123
|
|
|
$templateVariableContainer->remove($arguments['key']); |
124
|
|
|
} |
125
|
|
|
if (isset($arguments['iteration'])) { |
126
|
|
|
$templateVariableContainer->remove($arguments['iteration']); |
127
|
|
|
} |
128
|
|
|
} |
129
|
|
|
return $output; |
130
|
|
|
} |
131
|
|
|
} |
132
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.