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

FluidTYPO3 / flux / 27753814608

18 Jun 2026 10:39AM UTC coverage: 89.162% (-3.5%) from 92.646%
27753814608

Pull #2288

github

web-flow
Merge 37edf9f2e into 2614049c6
Pull Request #2288: [FEATURE] Prepare for v14 support

210 of 348 new or added lines in 56 files covered. (60.34%)

121 existing lines in 9 files now uncovered.

6228 of 6985 relevant lines covered (89.16%)

40.84 hits per line

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

99.17
/Classes/ViewHelpers/Field/ControllerActionsViewHelper.php
1
<?php
2
namespace FluidTYPO3\Flux\ViewHelpers\Field;
3

4
/*
5
 * This file is part of the FluidTYPO3/Flux 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\Flux\Form\Field\ControllerActions;
12
use FluidTYPO3\Flux\Utility\RequestResolver;
13
use FluidTYPO3\Flux\Utility\VersionUtility;
14
use Psr\Http\Message\ServerRequestInterface;
15
use TYPO3\CMS\Extbase\Mvc\ExtbaseRequestParameters;
16
use TYPO3\CMS\Extbase\Mvc\Request;
17
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
18

19
/**
20
 * ControllerActions ViewHelper
21
 *
22
 * Renders a FlexForm select field with options fetched from
23
 * requested extensionName/pluginName and other settings.
24
 *
25
 * There are three basic ways of adding selection options:
26
 *
27
 * - You can use the "extensionName" and "pluginName" to render all
28
 *   possible actions from an Extbase plugin that you've defined. It
29
 *   doesn't have to be your own plugin - if for example you are
30
 *   rendering actions from EXT:news or another through your own plugin.
31
 * - Or you can use the "actions" argument which is an array:
32
 *   {ControllerName: 'action1,action2,action3', OtherControllerName: 'action1'}
33
 * - And you can extend any of the two methods above with the "subActions"
34
 *   parameter, which allows you to extend the allowed actions whenever
35
 *   the specified combination of ControllerName + actionName is encountered.
36
 *   Example:       actions="{ControllerName: 'action1,action2'}"
37
 *                  subActions="{ControllerName: {action1: 'action3,action4'}}"
38
 *   Gives options: ControllerName->action1,action3,action4 with LLL values based on "action1"
39
 *                  ControllerName->action2 with LLL values based on "action2"
40
 *   By default Flux will create one option per action when reading
41
 *   Controller actions - using "subActions" it becomes possible to add
42
 *   additional actions to the list of allowed actions that the option
43
 *   will contain, as opposed to having only one action per option.
44
 *
45
 * And there are a few ways to limit the options that are displayed:
46
 *
47
 * - You can use "excludeActions" to specify an array in the same
48
 *   syntax used by the "actions" argument, these are then excluded.
49
 * - You can specifiy the "controllerName" argument in which case
50
 *   only actions from that Controller are displayed.
51
 *
52
 * And there are a couple of ways to define/resolve labels for actions:
53
 *
54
 * - You can add an LLL label in your locallang_db file:
55
 *   lowercasepluginname.lowercasecontrollername.actionfunctionname
56
 *   example index: myext.articlecontroller.show
57
 * - You can do nothing, in which case the very first line of
58
 *   the PHP doc-comment of each action method is used. This value can
59
 *   even be an LLL:file reference (in case you don't want to use the
60
 *   pattern above - but beware this is somewhat expensive processing)
61
 * - Or you can do nothing at all, not even add a doc comment, in which
62
 *   case the Controller->action syntax is used instead.
63
 *
64
 * Marking actions that have required arguments (which cause errors if
65
 * rendered on a page that is accessible through a traditional menu) is
66
 * possible but is deactivated for LLL labels; if you use LLL labels
67
 * and your action requires an argument, be user friendly and note so
68
 * in the LLL label or docs as applies.
69
 *
70
 * Lastly, you can set a custom name for the field in which case the
71
 * value does not trigger the Extbase SwitchableControllerActions feature
72
 * but instead works as any other Flux FlexForm field would.
73
 *
74
 * To use the field just place it in your Flux form (but in almost all
75
 * cases leave out the "name" argument which is required on all other
76
 * field types at the time of writing this). Where the field is placed
77
 * is not important; the order and the sheet location don't matter.
78
 */
79
class ControllerActionsViewHelper extends SelectViewHelper
80
{
81
    public function initializeArguments(): void
82
    {
83
        parent::initializeArguments();
4✔
84
        $this->overrideArgument(
4✔
85
            'items',
4✔
86
            'mixed',
4✔
87
            'Optional, full list of items to display - note: if used, this overrides any automatic option filling!'
4✔
88
        );
4✔
89
        $this->overrideArgument('name', 'string', 'Name of the field', false, 'switchableControllerActions');
4✔
90
        $this->registerArgument(
4✔
91
            'controllerExtensionName',
4✔
92
            'string',
4✔
93
            'Name of the Extbase extension that contains the Controller to parse, ex. MyExtension. In vendor based ' .
4✔
94
            'extensions use dot, ex. Vendor.MyExtension'
4✔
95
        );
4✔
96
        $this->registerArgument(
4✔
97
            'pluginName',
4✔
98
            'string',
4✔
99
            'Name of the Extbase plugin that contains Controller definitions to parse, ex. MyPluginName'
4✔
100
        );
4✔
101
        $this->registerArgument(
4✔
102
            'controllerName',
4✔
103
            'string',
4✔
104
            'Optional extra limiting of actions displayed - if used, field only displays actions for this ' .
4✔
105
            'controller name - ex Article(Controller) or FrontendUser(Controller) - the Controller part is implied'
4✔
106
        );
4✔
107
        $this->registerArgument(
4✔
108
            'actions',
4✔
109
            'array',
4✔
110
            'Array of "ControllerName" => "csv,of,actions" which are allowed. If used, does not require the use of ' .
4✔
111
            'an ExtensionName and PluginName (will use the one specified in your current plugin automatically)',
4✔
112
            false,
4✔
113
            []
4✔
114
        );
4✔
115
        $this->registerArgument(
4✔
116
            'excludeActions',
4✔
117
            'array',
4✔
118
            'Array of "ControllerName" => "csv,of,actions" which must be excluded',
4✔
119
            false,
4✔
120
            []
4✔
121
        );
4✔
122
        $this->registerArgument(
4✔
123
            'prefixOnRequiredArguments',
4✔
124
            'string',
4✔
125
            'A short string denoting that the method takes arguments, ex * (which should then be explained in the ' .
4✔
126
            'documentation for your extension about how to setup your plugins',
4✔
127
            false,
4✔
128
            '*'
4✔
129
        );
4✔
130
        $this->registerArgument(
4✔
131
            'disableLocalLanguageLabels',
4✔
132
            'boolean',
4✔
133
            'If TRUE, disables LLL label usage and just uses the class comment or Controller->action syntax',
4✔
134
            false,
4✔
135
            false
4✔
136
        );
4✔
137
        $this->registerArgument(
4✔
138
            'localLanguageFileRelativePath',
4✔
139
            'string',
4✔
140
            'Relative (from extension $extensionName) path to locallang file containing the action method labels',
4✔
141
            false,
4✔
142
            '/Resources/Private/Language/locallang_db.xml'
4✔
143
        );
4✔
144
        $this->registerArgument(
4✔
145
            'subActions',
4✔
146
            'array',
4✔
147
            "Array of sub actions {ControllerName: {list: 'update,delete'}, OtherController: {new: 'create'}} which ' .
4✔
148
            'are also allowed but not presented as options when the mapped action is selected (in example: if ' .
149
            'ControllerName->list is selected, ControllerName->update and ControllerName->delete are allowed - but ' .
150
            'cannot be selected).",
4✔
151
            false,
4✔
152
            []
4✔
153
        );
4✔
154
        $this->registerArgument(
4✔
155
            'separator',
4✔
156
            'string',
4✔
157
            'Separator string (glue) for Controller->action values, defaults to "->". Empty values result in ' .
4✔
158
            'default being used.'
4✔
159
        );
4✔
160
    }
161

162
    public static function getComponent(
163
        RenderingContextInterface $renderingContext,
164
        iterable $arguments
165
    ): ControllerActions {
166
        /** @var array $arguments */
167
        $extensionName = $arguments['controllerExtensionName'];
20✔
168
        /** @var string|null $pluginName */
169
        $pluginName = $arguments['pluginName'];
20✔
170
        $actions = $arguments['actions'];
20✔
171
        $controllerName = $arguments['controllerName'];
20✔
172
        $separator = $arguments['separator'];
20✔
173

174
        if ($actions instanceof \Traversable) {
20✔
175
            $actions = iterator_to_array($actions);
4✔
176
        }
177
        $request = null;
20✔
178
        if (VersionUtility::isCoreAtLeast13()) {
20✔
179
            $request = $renderingContext->getAttribute(ServerRequestInterface::class);
15✔
180
        } elseif (method_exists($renderingContext, 'getRequest')) {
5✔
181
            $request = $renderingContext->getRequest();
5✔
182
        } else {
NEW
183
            $request = RequestResolver::getRequest();
×
184
        }
185
        if (empty($extensionName)) {
20✔
186
            $extensionName = static::getFullExtensionNameFromRequest($request);
12✔
187
        }
188
        if (empty($pluginName)) {
20✔
189
            $pluginName = static::getPluginNameFromRequest($request);
4✔
190
        }
191
        if ((empty($extensionName) || empty($pluginName)) && count($actions) < 1) {
20✔
192
            throw new \RuntimeException(
4✔
193
                'Either "actions", or both "extensionName" and "pluginName" must be used on ' .
4✔
194
                'flux:field.controllerActions. None were found and none were detected from the Request.',
4✔
195
                1346514748
4✔
196
            );
4✔
197
        }
198
        /** @var ControllerActions $component */
199
        $component = static::getPreparedComponent(ControllerActions::class, $renderingContext, $arguments);
16✔
200
        $component->setExtensionName(
16✔
201
            static::getExtensionNameFromRenderingContextOrArguments($renderingContext, $arguments)
16✔
202
        );
16✔
203
        $component->setItems($arguments['items']);
16✔
204
        $component->setControllerExtensionName($extensionName);
16✔
205
        $component->setControllerName($controllerName);
16✔
206
        $component->setActions($actions);
16✔
207
        $component->setExcludeActions($arguments['excludeActions']);
16✔
208
        $component->setPrefixOnRequiredArguments($arguments['prefixOnRequiredArguments']);
16✔
209
        $component->setDisableLocalLanguageLabels($arguments['disableLocalLanguageLabels']);
16✔
210
        $component->setLocalLanguageFileRelativePath($arguments['localLanguageFileRelativePath']);
16✔
211
        $component->setSubActions($arguments['subActions']);
16✔
212
        if (!empty($pluginName)) {
16✔
213
            $component->setPluginName($pluginName);
16✔
214
        }
215
        if (!empty($separator)) {
16✔
216
            $component->setSeparator($separator);
4✔
217
        }
218
        return $component;
16✔
219
    }
220

221
    protected static function getPluginNameFromRequest(Request|ServerRequestInterface $request): ?string
222
    {
223
        /** @var ExtbaseRequestParameters $extbaseParameters */
224
        $extbaseParameters = $request->getAttribute('extbase');
4✔
225
        return $extbaseParameters->getPluginName();
4✔
226
    }
227

228
    protected static function getFullExtensionNameFromRequest(Request|ServerRequestInterface $request): string
229
    {
230
        /** @var ExtbaseRequestParameters $extbaseParameters */
231
        $extbaseParameters = $request->getAttribute('extbase');
16✔
232
        return $extbaseParameters->getControllerExtensionName();
16✔
233
    }
234
}
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