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

FluidTYPO3 / vhs / 28588802516

02 Jul 2026 12:08PM UTC coverage: 70.321% (+0.4%) from 69.971%
28588802516

push

github

NamelessCoder
[TASK] Remove most of the remaining TSFE usages

21 of 32 new or added lines in 7 files covered. (65.63%)

4836 of 6877 relevant lines covered (70.32%)

4.41 hits per line

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

84.35
/Classes/ViewHelpers/Content/AbstractContentViewHelper.php
1
<?php
2
namespace FluidTYPO3\Vhs\ViewHelpers\Content;
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\Core\ViewHelper\AbstractViewHelper;
12
use FluidTYPO3\Vhs\Proxy\DoctrineQueryProxy;
13
use FluidTYPO3\Vhs\Traits\SlideViewHelperTrait;
14
use FluidTYPO3\Vhs\Utility\ContentObjectFetcher;
15
use FluidTYPO3\Vhs\Utility\RequestResolver;
16
use TYPO3\CMS\Core\Database\ConnectionPool;
17
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
18

19
/**
20
 * Base class: Content ViewHelpers
21
 */
22
abstract class AbstractContentViewHelper extends AbstractViewHelper
23
{
24
    use SlideViewHelperTrait;
25

26
    /**
27
     * @var ConfigurationManagerInterface
28
     */
29
    protected $configurationManager;
30

31
    /**
32
     * @var boolean
33
     */
34
    protected $escapeOutput = false;
35

36
    public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager): void
37
    {
38
        $this->configurationManager = $configurationManager;
30✔
39
    }
40

41
    public function initializeArguments(): void
42
    {
43
        $this->registerArgument('column', 'integer', 'Column position number (colPos) of the column to render');
10✔
44
        $this->registerArgument(
10✔
45
            'order',
10✔
46
            'string',
10✔
47
            'Optional sort field of content elements - RAND() supported. Note that when sliding is enabled, the ' .
10✔
48
            'sorting will be applied to records on a per-page basis and not to the total set of collected records.',
10✔
49
            false,
10✔
50
            'sorting'
10✔
51
        );
10✔
52
        $this->registerArgument('sortDirection', 'string', 'Optional sort direction of content elements', false, 'ASC');
10✔
53
        $this->registerArgument(
10✔
54
            'pageUid',
10✔
55
            'integer',
10✔
56
            'If set, selects only content from this page UID. Ignored when "contentUids" is specified.',
10✔
57
            false,
10✔
58
            0
10✔
59
        );
10✔
60
        $this->registerArgument(
10✔
61
            'contentUids',
10✔
62
            'array',
10✔
63
            'If used, replaces all conditions with an "uid IN (1,2,3)" style condition using the UID values from ' .
10✔
64
            'this array'
10✔
65
        );
10✔
66
        $this->registerArgument(
10✔
67
            'sectionIndexOnly',
10✔
68
            'boolean',
10✔
69
            'If TRUE, only renders/gets content that is marked as "include in section index"',
10✔
70
            false,
10✔
71
            false
10✔
72
        );
10✔
73
        $this->registerArgument('loadRegister', 'array', 'List of LOAD_REGISTER variable');
10✔
74
        $this->registerArgument('render', 'boolean', 'Render result', false, true);
10✔
75
        $this->registerArgument(
10✔
76
            'hideUntranslated',
10✔
77
            'boolean',
10✔
78
            'If FALSE, will NOT include elements which have NOT been translated, if current language is NOT the ' .
10✔
79
            'default language. Default is to show untranslated elements but never display the original if there is ' .
10✔
80
            'a translated version',
10✔
81
            false,
10✔
82
            false
10✔
83
        );
10✔
84
        $this->registerSlideArguments();
10✔
85
    }
86

87
    protected function getContentRecords(): array
88
    {
89
        /** @var int $limit */
90
        $limit = $this->arguments['limit'];
8✔
91
        /** @var int $slide */
92
        $slide = $this->arguments['slide'];
8✔
93

94
        $pageUid = $this->getPageUid();
8✔
95

96
        if ((int) $slide === 0) {
8✔
97
            $contentRecords = $this->getSlideRecordsFromPage($pageUid, $limit);
8✔
98
        } else {
99
            $contentRecords = $this->getSlideRecords($pageUid, $limit);
×
100
        }
101

102
        if ($this->arguments['render']) {
8✔
103
            $contentRecords = $this->getRenderedRecords($contentRecords);
4✔
104
        }
105

106
        return $contentRecords;
8✔
107
    }
108

109
    protected function getSlideRecordsFromPage(int $pageUid, ?int $limit): array
110
    {
111
        /** @var string $direction */
112
        $direction = $this->arguments['sortDirection'];
8✔
113
        /** @var string $order */
114
        $order = $this->arguments['order'];
8✔
115
        if (!empty($order)) {
8✔
116
            $sortDirection = strtoupper(trim($direction));
8✔
117
            if ('ASC' !== $sortDirection && 'DESC' !== $sortDirection) {
8✔
118
                $sortDirection = 'ASC';
×
119
            }
120
            $order = $order . ' ' . $sortDirection;
8✔
121
        }
122

123
        $contentUids = $this->arguments['contentUids'];
8✔
124
        if (is_array($contentUids) && !empty($contentUids)) {
8✔
NEW
125
            return ContentObjectFetcher::resolve()->getRecords(
×
126
                'tt_content',
×
127
                [
×
128
                    'uidInList' => implode(',', $contentUids),
×
129
                    'orderBy' => $order,
×
130
                    'max' => $limit,
×
131
                    // Note: pidInList must not use $pageUid which defaults to current PID. Use argument-passed pageUid!
132
                    // A value of zero here removes the "pid" from the condition generated by ContentObjectRenderer.
133
                    'pidInList' => (int)$pageUid,
×
134
                    'includeRecordsWithoutDefaultTranslation' => !$this->arguments['hideUntranslated']
×
135
                ]
×
136
            );
×
137
        }
138

139
        $conditions = '1=1';
8✔
140
        if (is_numeric($this->arguments['column'])) {
8✔
141
            $conditions = sprintf('colPos = %d', (int) $this->arguments['column']);
×
142
        }
143
        if ($this->arguments['sectionIndexOnly']) {
8✔
144
            $conditions .= ' AND sectionIndex = 1';
×
145
        }
146

147
        $rows = ContentObjectFetcher::resolve()->getRecords(
8✔
148
            'tt_content',
8✔
149
            [
8✔
150
                'where' => $conditions,
8✔
151
                'orderBy' => $order,
8✔
152
                'max' => $limit,
8✔
153
                'pidInList' => $pageUid,
8✔
154
                'includeRecordsWithoutDefaultTranslation' => !$this->arguments['hideUntranslated']
8✔
155
            ]
8✔
156
        );
8✔
157

158
        return $rows;
8✔
159
    }
160

161
    /**
162
     * Gets the configured, or the current page UID if
163
     * none is configured in arguments and no content_from_pid
164
     * value exists in the current page record's attributes.
165
     */
166
    protected function getPageUid(): int
167
    {
168
        /** @var array|null $contentUids */
169
        $contentUids = $this->arguments['contentUids'] ?? null;
8✔
170

171
        if (!empty($contentUids)) {
8✔
172
            return 0;
×
173
        }
174

175
        /** @var int $pageUid */
176
        $pageUid = $this->arguments['pageUid'];
8✔
177
        $pageUid = (int) $pageUid;
8✔
178

179
        $pageInformation = RequestResolver::getPageInformation();
8✔
180
        if (!$pageInformation) {
8✔
181
            return $pageUid;
4✔
182
        }
183

184
        if (!$pageUid && ($fromPid = $pageInformation->getContentFromPid())) {
4✔
185
            $pageUid = $fromPid;
4✔
186
        }
187

188
        return $pageUid;
4✔
189
    }
190

191
    /**
192
     * This function renders an array of tt_content record into an array of rendered content
193
     * it returns a list of elements rendered by typoscript RECORD function
194
     */
195
    protected function getRenderedRecords(array $rows): array
196
    {
197
        $contentObject = ContentObjectFetcher::resolve($this->configurationManager);
4✔
198

199
        /** @var array $loadRegister */
200
        $loadRegister = $this->arguments['loadRegister'];
4✔
201
        if (!empty($loadRegister)) {
4✔
202
            $contentObject->cObjGetSingle('LOAD_REGISTER', $loadRegister);
×
203
        }
204
        $elements = [];
4✔
205
        foreach ($rows as $row) {
4✔
206
            $elements[] = static::renderRecord($row);
×
207
        }
208
        if (!empty($loadRegister)) {
4✔
209
            $contentObject->cObjGetSingle('RESTORE_REGISTER', []);
×
210
        }
211
        return $elements;
4✔
212
    }
213

214
    /**
215
     * This function renders a raw tt_content record into the corresponding
216
     * element by typoscript RENDER function. We keep track of already
217
     * rendered records to avoid rendering the same record twice inside the
218
     * same nested stack of content elements.
219
     */
220
    protected static function renderRecord(array $row): ?string
221
    {
222
        $conf = [
2✔
223
            'tables' => 'tt_content',
2✔
224
            'source' => $row['uid'],
2✔
225
            'dontCheckPid' => 1
2✔
226
        ];
2✔
227
        $html = ContentObjectFetcher::resolve()->cObjGetSingle('RECORDS', $conf);
2✔
228
        return $html;
2✔
229
    }
230
}
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