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

CPS-IT / handlebars / 22660183928

04 Mar 2026 07:53AM UTC coverage: 90.285% (-8.8%) from 99.094%
22660183928

push

github

eliashaeussler
Merge branch '1.x'

1277 of 1418 new or added lines in 66 files covered. (90.06%)

2 existing lines in 1 file now uncovered.

1329 of 1472 relevant lines covered (90.29%)

6.92 hits per line

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

97.06
/Classes/View/HandlebarsViewFactory.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the TYPO3 CMS extension "handlebars".
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17

18
namespace CPSIT\Typo3Handlebars\View;
19

20
use CPSIT\Typo3Handlebars\Controller;
21
use Symfony\Component\DependencyInjection;
22
use TYPO3\CMS\Core;
23
use TYPO3\CMS\Extbase;
24
use TYPO3\CMS\Fluid;
25
use TYPO3\CMS\Frontend;
26

27
/**
28
 * HandlebarsViewFactory
29
 *
30
 * @author Elias Häußler <e.haeussler@familie-redlich.de>
31
 * @license GPL-2.0-or-later
32
 */
33
#[DependencyInjection\Attribute\AsAlias(Core\View\ViewFactoryInterface::class, public: true)]
34
final readonly class HandlebarsViewFactory implements Core\View\ViewFactoryInterface
35
{
36
    public function __construct(
12✔
37
        private Extbase\Configuration\ConfigurationManagerInterface $configurationManager,
38
        #[DependencyInjection\Attribute\Autowire(service: Fluid\View\FluidViewFactory::class)]
39
        private Core\View\ViewFactoryInterface $delegate,
40
        private Core\TypoScript\TypoScriptService $typoScriptService,
41
    ) {}
12✔
42

43
    public function create(Core\View\ViewFactoryData $data): Core\View\ViewInterface
12✔
44
    {
45
        return $this->resolveView($data) ?? $this->delegate->create($data);
12✔
46
    }
47

48
    private function resolveView(Core\View\ViewFactoryData $data): ?HandlebarsView
12✔
49
    {
50
        $request = $data->request;
12✔
51
        $contentObjectRenderer = $request?->getAttribute('currentContentObject');
12✔
52

53
        if ($request === null) {
12✔
54
            return null;
1✔
55
        }
56

57
        if (!($contentObjectRenderer instanceof Frontend\ContentObject\ContentObjectRenderer)) {
11✔
58
            return null;
2✔
59
        }
60

61
        if ($request instanceof Extbase\Mvc\RequestInterface) {
9✔
62
            $contentObjectConfiguration = $this->resolveExtbaseContentObjectConfiguration(
8✔
63
                $contentObjectRenderer,
8✔
64
                $request->getControllerObjectName(),
8✔
65
                $request->getControllerActionName(),
8✔
66
                $data->format ?? $request->getFormat(),
8✔
67
            );
8✔
68
        } else {
69
            $contentObjectConfiguration = array_filter(
1✔
70
                [
1✔
71
                    'templateName' => $data->templatePathAndFilename,
1✔
72
                    'templateRootPaths.' => $data->templateRootPaths,
1✔
73
                    'partialRootPaths.' => $data->partialRootPaths,
1✔
74
                    'layoutRootPaths.' => $data->layoutRootPaths,
1✔
75
                    'format' => $data->format,
1✔
76
                ],
1✔
77
                static fn(mixed $value) => $value !== null,
1✔
78
            );
1✔
79
        }
80

81
        if ($contentObjectConfiguration !== null) {
9✔
82
            return new HandlebarsView($contentObjectRenderer, $this->typoScriptService, $contentObjectConfiguration, $request);
8✔
83
        }
84

85
        return null;
1✔
86
    }
87

88
    /**
89
     * @return array<string, mixed>|null
90
     */
91
    private function resolveExtbaseContentObjectConfiguration(
8✔
92
        Frontend\ContentObject\ContentObjectRenderer $contentObjectRenderer,
93
        string $controllerObjectName,
94
        string $actionName,
95
        string $format,
96
    ): ?array {
97
        $configuration = $this->configurationManager->getConfiguration(
8✔
98
            Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK,
8✔
99
        );
8✔
100
        $controllerAlias = $configuration['controllerConfiguration'][$controllerObjectName]['alias'] ?? null;
8✔
101

102
        // Early return if controller is not properly registered
103
        if (!is_string($controllerAlias)) {
8✔
NEW
104
            return null;
×
105
        }
106

107
        // Use hbs as default format, can be overridden with TypoScript
108
        if ($format === 'html') {
8✔
NEW
109
            $format = 'hbs';
×
110
        }
111

112
        $handlebarsConfiguration = $configuration['handlebars'] ?? null;
8✔
113
        $defaultConfiguration = [
8✔
114
            'templateName' => $controllerAlias . '/' . $actionName,
8✔
115
            'format' => $format,
8✔
116
        ];
8✔
117

118
        // Early return if no handlebars configuration is available
119
        if (!is_array($handlebarsConfiguration)) {
8✔
120
            if (is_a($controllerObjectName, Controller\HandlebarsController::class, true)) {
2✔
121
                return $defaultConfiguration;
1✔
122
            }
123

124
            return null;
1✔
125
        }
126

127
        // HANDLEBARSTEMPLATE content object requires TypoScript configuration, so let's convert early
128
        $typoScriptConfiguration = $this->typoScriptService->convertPlainArrayToTypoScriptArray($handlebarsConfiguration);
6✔
129

130
        // Resolve TypoScript configuration based on controller context
131
        $resolvedConfiguration = [];
6✔
132
        $possibleConfigurationKeys = [
6✔
133
            // Fallback
134
            'default',
6✔
135
            // Controller only
136
            $controllerAlias,
6✔
137
            // Controller & action
138
            $controllerAlias . '::' . $actionName,
6✔
139
            // Controller FQCN
140
            $controllerObjectName,
6✔
141
        ];
6✔
142
        foreach ($possibleConfigurationKeys as $possibleConfigurationKey) {
6✔
143
            if (\array_key_exists($possibleConfigurationKey . '.', $typoScriptConfiguration)) {
6✔
144
                Core\Utility\ArrayUtility::mergeRecursiveWithOverrule(
5✔
145
                    $resolvedConfiguration,
5✔
146
                    $typoScriptConfiguration[$possibleConfigurationKey . '.'],
5✔
147
                );
5✔
148
            }
149
        }
150

151
        // Early return if no configuration was resolved
152
        if ($resolvedConfiguration === []) {
6✔
153
            return $defaultConfiguration;
1✔
154
        }
155

156
        // Add format
157
        if (!isset($resolvedConfiguration['format'])) {
5✔
158
            $resolvedConfiguration['format'] = $format;
5✔
159
        }
160

161
        return $resolvedConfiguration;
5✔
162
    }
163
}
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