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

FluidTYPO3 / flux / 12237686280

09 Dec 2024 02:27PM UTC coverage: 92.9% (-0.5%) from 93.421%
12237686280

push

github

NamelessCoder
[TER] 10.1.0

7013 of 7549 relevant lines covered (92.9%)

56.22 hits per line

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

83.93
/Classes/ViewHelpers/Content/GetViewHelper.php
1
<?php
2
declare(strict_types=1);
3
namespace FluidTYPO3\Flux\ViewHelpers\Content;
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\Container\Column;
13
use FluidTYPO3\Flux\Form\Container\Grid;
14
use FluidTYPO3\Flux\Hooks\HookHandler;
15
use FluidTYPO3\Flux\Provider\AbstractProvider;
16
use FluidTYPO3\Flux\Service\WorkspacesAwareRecordService;
17
use FluidTYPO3\Flux\Utility\ColumnNumberUtility;
18
use FluidTYPO3\Flux\ViewHelpers\FormViewHelper;
19
use TYPO3\CMS\Backend\Utility\BackendUtility;
20
use TYPO3\CMS\Core\Context\Context;
21
use TYPO3\CMS\Core\Database\ConnectionPool;
22
use TYPO3\CMS\Core\Utility\GeneralUtility;
23
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
24
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
25
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
26
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
27
use TYPO3Fluid\Fluid\Core\ViewHelper\Exception;
28

29
/**
30
 * Gets all child content of a record based on area.
31
 *
32
 * The elements are already rendered, they just need to be output.
33
 *
34
 * ### Example: Render all child elements with a border
35
 *
36
 * `fluidcontent` element with one column of child elements.
37
 * Each element gets a red border:
38
 *
39
 *     <f:section name="Configuration">
40
 *      <flux:grid>
41
 *       <flux:grid.row>
42
 *        <flux:grid.column name="teaser" colPos="0"/>
43
 *       </flux:grid.row>
44
 *      </flux:grid>
45
 *     </f:section>
46
 *
47
 *     <f:section name="Main">
48
 *      <f:for each="{flux:content.get(area:'teaser')}" as="element">
49
 *       <div style="border: 1px solid red">
50
 *        <f:format.raw>{element}</f:format.raw>
51
 *       </div>
52
 *      </f:for>
53
 *     </f:section>
54
 */
55
class GetViewHelper extends AbstractViewHelper
56
{
57
    /**
58
     * @var boolean
59
     */
60
    protected $escapeOutput = false;
61

62
    protected static ?ConfigurationManagerInterface $configurationManager = null;
63
    protected static ?WorkspacesAwareRecordService $recordService = null;
64

65
    public function initializeArguments(): void
66
    {
67
        $this->registerArgument('area', 'string', 'Name or "colPos" value of the content area to render', true);
12✔
68
        $this->registerArgument('limit', 'integer', 'Optional limit to the number of content elements to render');
12✔
69
        $this->registerArgument('offset', 'integer', 'Optional offset to the limit', false, 0);
12✔
70
        $this->registerArgument(
12✔
71
            'order',
12✔
72
            'string',
12✔
73
            'Optional sort order of content elements - RAND() supported',
12✔
74
            false,
12✔
75
            'sorting'
12✔
76
        );
12✔
77
        $this->registerArgument('sortDirection', 'string', 'Optional sort direction of content elements', false, 'ASC');
12✔
78
        $this->registerArgument(
12✔
79
            'as',
12✔
80
            'string',
12✔
81
            'Variable name to register, then render child content and insert all results as an array of records'
12✔
82
        );
12✔
83
        $this->registerArgument('loadRegister', 'array', 'List of LOAD_REGISTER variable');
12✔
84
        $this->registerArgument('render', 'boolean', 'Optional returning variable as original table rows', false, true);
12✔
85
        $this->registerArgument('hideUntranslated', 'boolean', 'Exclude untranslated records', false, false);
12✔
86
    }
87

88
    /**
89
     * @return array|string|null
90
     */
91
    public function render()
92
    {
93
        return static::renderStatic($this->arguments, $this->buildRenderChildrenClosure(), $this->renderingContext);
54✔
94
    }
95

96
    /**
97
     * @return string|array|null
98
     */
99
    public static function renderStatic(
100
        array $arguments,
101
        \Closure $renderChildrenClosure,
102
        RenderingContextInterface $renderingContext
103
    ) {
104
        $contentObjectRenderer = static::getContentObjectRenderer();
54✔
105

106
        $registerVariables = (array) $arguments['loadRegister'];
54✔
107
        $loadRegister = false;
54✔
108
        if (!empty($registerVariables)) {
54✔
109
            $contentObjectRenderer->cObjGetSingle('LOAD_REGISTER', $registerVariables);
12✔
110
            $loadRegister = true;
12✔
111
        }
112
        $templateVariableContainer = $renderingContext->getVariableProvider();
54✔
113
        $record = $renderingContext->getViewHelperVariableContainer()->get(FormViewHelper::class, 'record');
54✔
114
        if (!is_array($record)) {
54✔
115
            return null;
×
116
        }
117

118
        /** @var Context $context */
119
        $context = GeneralUtility::makeInstance(Context::class);
54✔
120
        $workspaceId = $context->getPropertyFromAspect('workspace', 'id');
54✔
121

122
        if (is_numeric($workspaceId) && $workspaceId > 0) {
54✔
123
            $placeholder = BackendUtility::getWorkspaceVersionOfRecord(
×
124
                (integer) $workspaceId,
×
125
                'tt_content',
×
126
                $record['uid'] ?? 0
×
127
            );
×
128
            if ($placeholder) {
×
129
                // Use the move placeholder if one exists, ensuring that "pid" and "tx_flux_parent" values are taken
130
                // from the workspace-only placeholder.
131
                /** @var array $record */
132
                $record = $placeholder;
×
133
            }
134
        }
135

136
        /** @var AbstractProvider $provider */
137
        $provider = $renderingContext->getViewHelperVariableContainer()->get(FormViewHelper::class, 'provider');
54✔
138
        $grid = $provider->getGrid($record);
54✔
139
        $rows = static::getContentRecords($arguments, $record, $grid);
54✔
140

141
        $elements = false === (boolean) $arguments['render'] ? $rows : static::getRenderedRecords($rows);
54✔
142
        if (empty($arguments['as'])) {
54✔
143
            $content = $elements;
18✔
144
        } else {
145
            /** @var string $as */
146
            $as = $arguments['as'];
36✔
147
            if ($templateVariableContainer->exists($as)) {
36✔
148
                $backup = $templateVariableContainer->get($as);
×
149
                $templateVariableContainer->remove($as);
×
150
            }
151
            $templateVariableContainer->add($as, $elements);
36✔
152
            $content = $renderChildrenClosure();
36✔
153
            $templateVariableContainer->remove($as);
36✔
154
            if (isset($backup)) {
36✔
155
                $templateVariableContainer->add($as, $backup);
×
156
            }
157
        }
158
        if ($loadRegister) {
54✔
159
            $contentObjectRenderer->cObjGetSingle('RESTORE_REGISTER', []);
12✔
160
        }
161
        return $content;
54✔
162
    }
163

164
    protected static function getContentRecords(array $arguments, array $parent, Grid $grid): array
165
    {
166
        $columnPosition = $arguments['area'];
54✔
167
        if (!ctype_digit((string) $columnPosition)) {
54✔
168
            $column = $grid->get((string) $columnPosition, true, Column::class);
54✔
169
            if ($column instanceof Column) {
54✔
170
                $columnPosition = $column->getColumnPosition();
54✔
171
            } else {
172
                throw new Exception(
×
173
                    sprintf(
×
174
                        'Argument "column" or "area" for "flux:content.(get|render)" was a string column name "%s", ' .
×
175
                        'but this column was not defined',
×
176
                        $columnPosition
×
177
                    )
×
178
                );
×
179
            }
180
        }
181

182
        /** @var ConnectionPool $connectionPool */
183
        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
54✔
184
        $queryBuilder = $connectionPool->getQueryBuilderForTable('tt_content');
54✔
185

186
        $conditions = $queryBuilder->expr()->eq(
54✔
187
            'colPos',
54✔
188
            ColumnNumberUtility::calculateColumnNumberForParentAndColumn(
54✔
189
                ($parent['l18n_parent'] ?? null) ?: $parent['uid'],
54✔
190
                (int) $columnPosition
54✔
191
            )
54✔
192
        );
54✔
193

194
        $rows = static::getContentObjectRenderer()->getRecords(
54✔
195
            'tt_content',
54✔
196
            [
54✔
197
                'max' => $arguments['limit'],
54✔
198
                'begin' => $arguments['offset'],
54✔
199
                'orderBy' => $arguments['order'] . ' ' . $arguments['sortDirection'],
54✔
200
                'where' => $conditions,
54✔
201
                'pidInList' => $parent['pid'] ?? null,
54✔
202
                'includeRecordsWithoutDefaultTranslation' => !($arguments['hideUntranslated'] ?? false)
54✔
203
            ]
54✔
204
        );
54✔
205

206
        return HookHandler::trigger(
54✔
207
            HookHandler::NESTED_CONTENT_FETCHED,
54✔
208
            [
54✔
209
                'records' => $rows
54✔
210
            ]
54✔
211
        )['records'];
54✔
212
    }
213

214
    protected static function getContentObjectRenderer(): ContentObjectRenderer
215
    {
216
        return $GLOBALS['TSFE']->cObj;
60✔
217
    }
218

219
    /**
220
     * This function renders an array of tt_content record into an array of rendered content
221
     * it returns a list of elements rendered by typoscript RECORDS function
222
     */
223
    protected static function getRenderedRecords(array $rows): array
224
    {
225
        $elements = [];
48✔
226
        foreach ($rows as $row) {
48✔
227
            $conf = [
6✔
228
                'tables' => 'tt_content',
6✔
229
                'source' => $row['uid'],
6✔
230
                'dontCheckPid' => 1,
6✔
231
            ];
6✔
232
            $elements[] = static::getContentObjectRenderer()->cObjGetSingle('RECORDS', $conf);
6✔
233
        }
234
        return HookHandler::trigger(
48✔
235
            HookHandler::NESTED_CONTENT_RENDERED,
48✔
236
            [
48✔
237
                'rows' => $rows,
48✔
238
                'rendered' => $elements
48✔
239
            ]
48✔
240
        )['rendered'];
48✔
241
    }
242
}
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