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

FluidTYPO3 / vhs / 28366223948

29 Jun 2026 10:41AM UTC coverage: 69.744% (-0.1%) from 69.872%
28366223948

push

github

NamelessCoder
[REMOVAL] Make resource ViewHelpers demand uid or record

FAL etc. resource ViewHelpers no longer allow calling without both
record and uid arguments, which previously would have attempted
to resolve the current record data from ContentObectRenderer.

Calling these ViewHelpers must now include either record or uid as
arguments. Argument descriptions updated accordingly.

Close: #1963

4 of 5 new or added lines in 1 file covered. (80.0%)

6 existing lines in 2 files now uncovered.

4790 of 6868 relevant lines covered (69.74%)

4.03 hits per line

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

92.0
/Classes/ViewHelpers/Resource/Record/AbstractRecordResourceViewHelper.php
1
<?php
2
namespace FluidTYPO3\Vhs\ViewHelpers\Resource\Record;
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\TemplateVariableViewHelperTrait;
14
use FluidTYPO3\Vhs\Utility\ErrorUtility;
15
use FluidTYPO3\Vhs\Utility\RequestResolver;
16
use TYPO3\CMS\Core\Database\Connection;
17
use TYPO3\CMS\Core\Database\ConnectionPool;
18
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
19
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
20
use TYPO3\CMS\Core\Utility\GeneralUtility;
21
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
22

23
/**
24
 * Base class: Record Resource ViewHelpers
25
 */
26
abstract class AbstractRecordResourceViewHelper extends AbstractViewHelper
27
{
28
    use TemplateVariableViewHelperTrait;
29

30
    protected string $table = '';
31
    protected string $field = '';
32
    protected string $idField = 'uid';
33

34
    /**
35
     * @var ConfigurationManagerInterface
36
     */
37
    protected $configurationManager;
38

39
    /**
40
     * @var boolean
41
     */
42
    protected $escapeOutput = false;
43

44
    public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager): void
45
    {
46
        $this->configurationManager = $configurationManager;
34✔
47
    }
48

49
    public function initializeArguments(): void
50
    {
51
        $this->registerArgument('table', 'string', 'The table to lookup records.', true);
12✔
52
        $this->registerArgument('field', 'string', 'The field of the table associated to resources.', true);
12✔
53
        $this->registerArgument(
12✔
54
            'record',
12✔
55
            'array',
12✔
56
            'The actual record. Alternatively you can use the "uid" argument; you must specify either one.'
12✔
57
        );
12✔
58
        $this->registerArgument(
12✔
59
            'uid',
12✔
60
            'integer',
12✔
61
            'The uid of the record. Alternatively you can use the "record" argument; you must specify either one.'
12✔
62
        );
12✔
63
        $this->registerArgument(
12✔
64
            'as',
12✔
65
            'string',
12✔
66
            'If specified, a template variable with this name containing the requested data will be inserted ' .
12✔
67
            'instead of returning it.'
12✔
68
        );
12✔
69
    }
70

71
    public function getResources(array $record): array
72
    {
73
        $field = $this->getField();
4✔
74

75
        if (!isset($record[$field])) {
4✔
UNCOV
76
            ErrorUtility::throwViewHelperException(
×
NEW
77
                'The field "' . $field . '" was not found on the selected record.',
×
UNCOV
78
                1384612728
×
UNCOV
79
            );
×
80
        }
81

82
        if (empty($record[$field])) {
4✔
83
            return [];
2✔
84
        }
85

86
        return GeneralUtility::trimExplode(',', $record[$field]);
2✔
87
    }
88

89
    public function getTable(): string
90
    {
91
        /** @var string|null $table */
92
        $table = $this->arguments['table'] ?? null;
14✔
93
        if (null === $table) {
14✔
94
            $table = $this->table;
4✔
95
        }
96

97
        if (empty($table) || !is_string($table)) {
14✔
98
            ErrorUtility::throwViewHelperException(
4✔
99
                'The "table" argument must be specified and must be a string.',
4✔
100
                1384611336
4✔
101
            );
4✔
102
        }
103

104
        return $table;
10✔
105
    }
106

107
    public function getField(): string
108
    {
109
        /** @var string|null $field */
110
        $field = $this->arguments['field'] ?? null;
10✔
111
        if (null === $field) {
10✔
112
            $field = $this->field;
2✔
113
        }
114

115
        if (empty($field) || !is_string($field)) {
10✔
116
            ErrorUtility::throwViewHelperException(
2✔
117
                'The "field" argument must be specified and must be a string.',
2✔
118
                1384611355
2✔
119
            );
2✔
120
        }
121

122
        return $field;
8✔
123
    }
124

125
    public function getRecord(int $id): ?array
126
    {
127
        $table = $this->getTable();
8✔
128
        $idField = $this->idField;
6✔
129

130
        /** @var ConnectionPool $connectionPool */
131
        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
6✔
132
        /** @var QueryBuilder $queryBuilder */
133
        $queryBuilder = $connectionPool->getQueryBuilderForTable($table);
6✔
134

135
        if (RequestResolver::isPreview()) {
6✔
136
            $queryBuilder->getRestrictions()->removeByType(HiddenRestriction::class);
2✔
137
        }
138

139
        $queryBuilder->createNamedParameter($id, Connection::PARAM_INT, ':id');
6✔
140

141
        $queryBuilder
6✔
142
            ->select('*')
6✔
143
            ->from($table)
6✔
144
            ->where(
6✔
145
                $queryBuilder->expr()->eq($idField, ':id')
6✔
146
            );
6✔
147
        $statement = DoctrineQueryProxy::executeQueryOnQueryBuilder($queryBuilder);
6✔
148
        $result = DoctrineQueryProxy::fetchAssociative($statement);
6✔
149
        return $result;
6✔
150
    }
151

152
    public function render()
153
    {
154
        /** @var int|null $uid */
155
        $uid = $this->arguments['uid'] ?? null;
12✔
156

157
        /** @var array|null $record */
158
        $record = $this->arguments['record'] ?? $this->getRecord((int) $uid);
12✔
159

160
        if ($record === null) {
10✔
161
            ErrorUtility::throwViewHelperException(
6✔
162
                'No record was found. The "record" or "uid" argument must be specified.',
6✔
163
                1384611413
6✔
164
            );
6✔
165
        }
166

167
        // attempt to load resources. If any Exceptions happen, transform them to
168
        // ViewHelperExceptions which render as an inline text error message.
169
        $content = null;
4✔
170
        try {
171
            $resources = $this->getResources((array) $record);
4✔
172
            $content = $this->renderChildrenWithVariableOrReturnInput($resources);
4✔
UNCOV
173
        } catch (\Exception $error) {
×
174
            // we are doing the pokemon-thing and catching the very top level
175
            // of Exception because the range of Exceptions that are possibly
176
            // thrown by the getResources() method in subclasses are not
177
            // extended from a shared base class like RuntimeException. Thus,
178
            // we are forced to "catch them all" - but we also output them.
UNCOV
179
            ErrorUtility::throwViewHelperException($error->getMessage(), $error->getCode(), $error);
×
180
        }
181
        return $content;
4✔
182
    }
183
}
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