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

FluidTYPO3 / vhs / 29999067194

23 Jul 2026 10:22AM UTC coverage: 70.309% (-0.5%) from 70.857%
29999067194

push

github

NamelessCoder
[FEATURE] Declare support for TYPO3v14

6 of 8 new or added lines in 3 files covered. (75.0%)

50 existing lines in 2 files now uncovered.

4819 of 6854 relevant lines covered (70.31%)

6.54 hits per line

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

43.48
/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;
9✔
39
    }
40

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

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

UNCOV
94
        $pageUid = $this->getPageUid();
×
95

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

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

UNCOV
106
        return $contentRecords;
×
107
    }
108

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

UNCOV
123
        $contentUids = $this->arguments['contentUids'];
×
UNCOV
124
        if (is_array($contentUids) && !empty($contentUids)) {
×
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

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

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

UNCOV
158
        return $rows;
×
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 */
UNCOV
169
        $contentUids = $this->arguments['contentUids'] ?? null;
×
170

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

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

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

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

UNCOV
188
        return $pageUid;
×
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
    {
UNCOV
197
        $contentObject = ContentObjectFetcher::resolve($this->configurationManager);
×
198

199
        /** @var array $loadRegister */
UNCOV
200
        $loadRegister = $this->arguments['loadRegister'];
×
UNCOV
201
        if (!empty($loadRegister)) {
×
202
            $contentObject->cObjGetSingle('LOAD_REGISTER', $loadRegister);
×
203
        }
UNCOV
204
        $elements = [];
×
UNCOV
205
        foreach ($rows as $row) {
×
206
            $elements[] = static::renderRecord($row);
×
207
        }
UNCOV
208
        if (!empty($loadRegister)) {
×
209
            $contentObject->cObjGetSingle('RESTORE_REGISTER', []);
×
210
        }
UNCOV
211
        return $elements;
×
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 = [
3✔
223
            'tables' => 'tt_content',
3✔
224
            'source' => $row['uid'],
3✔
225
            'dontCheckPid' => 1
3✔
226
        ];
3✔
227
        $html = ContentObjectFetcher::resolve()->cObjGetSingle('RECORDS', $conf);
3✔
228
        return $html;
3✔
229
    }
230
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc