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

FluidTYPO3 / flux / 15918415903

20 May 2025 10:36AM UTC coverage: 91.109% (-2.1%) from 93.21%
15918415903

push

github

NamelessCoder
[TASK] Lock phpstan version

6927 of 7603 relevant lines covered (91.11%)

9.53 hits per line

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

98.31
/Classes/ViewHelpers/Field/ControllerActionsViewHelper.php
1
<?php
2
declare(strict_types=1);
3
namespace FluidTYPO3\Flux\ViewHelpers\Field;
4

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

12
use FluidTYPO3\Flux\Form\Field\ControllerActions;
13
use TYPO3\CMS\Extbase\Mvc\Request;
14
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
15

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

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

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

215
    protected static function getFullExtensionNameFromRequest(Request $request): string
216
    {
217
        $vendorName = method_exists($request, 'getControllerVendorName') ? $request->getControllerVendorName() : null;
4✔
218
        $extensionName = (string) $request->getControllerExtensionName();
4✔
219
        return $vendorName ? $vendorName . '.' . $extensionName : $extensionName;
4✔
220
    }
221
}
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