AdministrationController::__construct()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 23
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 4
nop 14
dl 0
loc 23
rs 10
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php declare(strict_types=1);
2
3
namespace Shopware\Administration\Controller;
4
5
use Doctrine\DBAL\Connection;
6
use Shopware\Administration\Events\PreResetExcludedSearchTermEvent;
7
use Shopware\Administration\Framework\Routing\KnownIps\KnownIpsCollectorInterface;
8
use Shopware\Administration\Snippet\SnippetFinderInterface;
9
use Shopware\Core\Checkout\Customer\CustomerEntity;
0 ignored issues
show
Bug introduced by
The type Shopware\Core\Checkout\Customer\CustomerEntity was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use Shopware\Core\Defaults;
11
use Shopware\Core\DevOps\Environment\EnvironmentHelper;
12
use Shopware\Core\Framework\Adapter\Twig\TemplateFinder;
13
use Shopware\Core\Framework\Context;
14
use Shopware\Core\Framework\DataAbstractionLayer\DefinitionInstanceRegistry;
15
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
16
use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\AllowHtml;
17
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
18
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
19
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
20
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
21
use Shopware\Core\Framework\Feature;
22
use Shopware\Core\Framework\Log\Package;
23
use Shopware\Core\Framework\Routing\RoutingException;
24
use Shopware\Core\Framework\Store\Services\FirstRunWizardService;
25
use Shopware\Core\Framework\Util\HtmlSanitizer;
26
use Shopware\Core\Framework\Uuid\Uuid;
27
use Shopware\Core\Framework\Validation\Exception\ConstraintViolationException;
28
use Shopware\Core\PlatformRequest;
29
use Shopware\Core\System\Currency\CurrencyEntity;
0 ignored issues
show
Bug introduced by
The type Shopware\Core\System\Currency\CurrencyEntity was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
30
use Shopware\Core\System\SystemConfig\SystemConfigService;
31
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
32
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
33
use Symfony\Component\HttpFoundation\JsonResponse;
34
use Symfony\Component\HttpFoundation\Request;
35
use Symfony\Component\HttpFoundation\Response;
36
use Symfony\Component\Routing\Annotation\Route;
37
use Symfony\Component\Validator\ConstraintViolation;
38
use Symfony\Component\Validator\ConstraintViolationList;
39
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
40
41
#[Route(defaults: ['_routeScope' => ['administration']])]
42
#[Package('administration')]
43
class AdministrationController extends AbstractController
44
{
45
    private readonly bool $esAdministrationEnabled;
46
47
    private readonly bool $esStorefrontEnabled;
48
49
    /**
50
     * @internal
51
     *
52
     * @param array<int, int> $supportedApiVersions
53
     */
54
    public function __construct(
55
        private readonly TemplateFinder $finder,
56
        private readonly FirstRunWizardService $firstRunWizardService,
57
        private readonly SnippetFinderInterface $snippetFinder,
58
        private readonly array $supportedApiVersions,
59
        private readonly KnownIpsCollectorInterface $knownIpsCollector,
60
        private readonly Connection $connection,
61
        private readonly EventDispatcherInterface $eventDispatcher,
62
        private readonly string $shopwareCoreDir,
63
        private readonly EntityRepository $customerRepo,
64
        private readonly EntityRepository $currencyRepository,
65
        private readonly HtmlSanitizer $htmlSanitizer,
66
        private readonly DefinitionInstanceRegistry $definitionInstanceRegistry,
67
        ParameterBagInterface $params,
68
        private readonly SystemConfigService $systemConfigService
69
    ) {
70
        // param is only available if the elasticsearch bundle is enabled
71
        $this->esAdministrationEnabled = $params->has('elasticsearch.administration.enabled')
0 ignored issues
show
Bug introduced by
The property esAdministrationEnabled is declared read-only in Shopware\Administration\...dministrationController.
Loading history...
72
            ? $params->get('elasticsearch.administration.enabled')
73
            : false;
74
        $this->esStorefrontEnabled = $params->has('elasticsearch.enabled')
0 ignored issues
show
Bug introduced by
The property esStorefrontEnabled is declared read-only in Shopware\Administration\...dministrationController.
Loading history...
75
            ? $params->get('elasticsearch.enabled')
76
            : false;
77
    }
78
79
    #[Route(path: '/%shopware_administration.path_name%', name: 'administration.index', defaults: ['auth_required' => false], methods: ['GET'])]
80
    public function index(Request $request, Context $context): Response
81
    {
82
        $template = $this->finder->find('@Administration/administration/index.html.twig');
83
84
        /** @var CurrencyEntity $defaultCurrency */
85
        $defaultCurrency = $this->currencyRepository->search(new Criteria([Defaults::CURRENCY]), $context)->first();
86
87
        return $this->render($template, [
88
            'features' => Feature::getAll(),
89
            'systemLanguageId' => Defaults::LANGUAGE_SYSTEM,
90
            'defaultLanguageIds' => [Defaults::LANGUAGE_SYSTEM],
91
            'systemCurrencyId' => Defaults::CURRENCY,
92
            'disableExtensions' => EnvironmentHelper::getVariable('DISABLE_EXTENSIONS', false),
93
            'systemCurrencyISOCode' => $defaultCurrency->getIsoCode(),
94
            'liveVersionId' => Defaults::LIVE_VERSION,
95
            'firstRunWizard' => $this->firstRunWizardService->frwShouldRun(),
96
            'apiVersion' => $this->getLatestApiVersion(),
97
            'cspNonce' => $request->attributes->get(PlatformRequest::ATTRIBUTE_CSP_NONCE),
98
            'adminEsEnable' => $this->esAdministrationEnabled,
99
            'storefrontEsEnable' => $this->esStorefrontEnabled,
100
        ]);
101
    }
102
103
    #[Route(path: '/api/_admin/snippets', name: 'api.admin.snippets', methods: ['GET'])]
104
    public function snippets(Request $request): Response
105
    {
106
        $snippets = [];
107
        $locale = $request->query->get('locale', 'en-GB');
108
        $snippets[$locale] = $this->snippetFinder->findSnippets((string) $locale);
109
110
        if ($locale !== 'en-GB') {
111
            $snippets['en-GB'] = $this->snippetFinder->findSnippets('en-GB');
112
        }
113
114
        return new JsonResponse($snippets);
115
    }
116
117
    #[Route(path: '/api/_admin/known-ips', name: 'api.admin.known-ips', methods: ['GET'])]
118
    public function knownIps(Request $request): Response
119
    {
120
        $ips = [];
121
122
        foreach ($this->knownIpsCollector->collectIps($request) as $ip => $name) {
123
            $ips[] = [
124
                'name' => $name,
125
                'value' => $ip,
126
            ];
127
        }
128
129
        return new JsonResponse(['ips' => $ips]);
130
    }
131
132
    #[Route(path: '/api/_admin/reset-excluded-search-term', name: 'api.admin.reset-excluded-search-term', defaults: ['_acl' => ['system_config:update', 'system_config:create', 'system_config:delete']], methods: ['POST'])]
133
    public function resetExcludedSearchTerm(Context $context): JsonResponse
134
    {
135
        $searchConfigId = $this->connection->fetchOne('SELECT id FROM product_search_config WHERE language_id = :language_id', ['language_id' => Uuid::fromHexToBytes($context->getLanguageId())]);
136
137
        if ($searchConfigId === false) {
138
            throw RoutingException::languageNotFound($context->getLanguageId());
139
        }
140
141
        $deLanguageId = $this->fetchLanguageIdByName('de-DE', $this->connection);
142
        $enLanguageId = $this->fetchLanguageIdByName('en-GB', $this->connection);
143
144
        switch ($context->getLanguageId()) {
145
            case $deLanguageId:
146
                $defaultExcludedTerm = require $this->shopwareCoreDir . '/Migration/Fixtures/stopwords/de.php';
147
148
                break;
149
            case $enLanguageId:
150
                $defaultExcludedTerm = require $this->shopwareCoreDir . '/Migration/Fixtures/stopwords/en.php';
151
152
                break;
153
            default:
154
                /** @var PreResetExcludedSearchTermEvent $preResetExcludedSearchTermEvent */
155
                $preResetExcludedSearchTermEvent = $this->eventDispatcher->dispatch(new PreResetExcludedSearchTermEvent($searchConfigId, [], $context));
156
                $defaultExcludedTerm = $preResetExcludedSearchTermEvent->getExcludedTerms();
157
        }
158
159
        $this->connection->executeStatement(
160
            'UPDATE `product_search_config` SET `excluded_terms` = :excludedTerms WHERE `id` = :id',
161
            [
162
                'excludedTerms' => json_encode($defaultExcludedTerm, \JSON_THROW_ON_ERROR),
163
                'id' => $searchConfigId,
164
            ]
165
        );
166
167
        return new JsonResponse([
168
            'success' => true,
169
        ]);
170
    }
171
172
    #[Route(path: '/api/_admin/check-customer-email-valid', name: 'api.admin.check-customer-email-valid', methods: ['POST'])]
173
    public function checkCustomerEmailValid(Request $request, Context $context): JsonResponse
174
    {
175
        $params = [];
176
        if (!$request->request->has('email')) {
177
            throw RoutingException::missingRequestParameter('email');
178
        }
179
180
        $email = (string) $request->request->get('email');
181
        $isCustomerBoundSalesChannel = $this->systemConfigService->get('core.systemWideLoginRegistration.isCustomerBoundToSalesChannel');
182
        $boundSalesChannelId = null;
183
        if ($isCustomerBoundSalesChannel) {
184
            $boundSalesChannelId = $request->request->get('boundSalesChannelId');
185
            if ($boundSalesChannelId !== null && !\is_string($boundSalesChannelId)) {
186
                throw RoutingException::invalidRequestParameter('boundSalesChannelId');
187
            }
188
        }
189
190
        $customer = $this->getCustomerByEmail((string) $request->request->get('id'), $email, $context, $boundSalesChannelId);
191
        if (!$customer) {
192
            return new JsonResponse(
193
                ['isValid' => true]
194
            );
195
        }
196
197
        $message = 'The email address {{ email }} is already in use';
198
        $params['{{ email }}'] = $email;
199
200
        if ($customer->getBoundSalesChannel()) {
201
            $message .= ' in the Sales Channel {{ salesChannel }}';
202
            $params['{{ salesChannel }}'] = $customer->getBoundSalesChannel()->getName();
203
        }
204
205
        $violations = new ConstraintViolationList();
206
        $violations->add(new ConstraintViolation(
207
            str_replace(array_keys($params), array_values($params), $message),
208
            $message,
209
            $params,
210
            null,
211
            null,
212
            $email,
213
            null,
214
            '79d30fe0-febf-421e-ac9b-1bfd5c9007f7'
215
        ));
216
217
        throw new ConstraintViolationException($violations, $request->request->all());
218
    }
219
220
    #[Route(path: '/api/_admin/sanitize-html', name: 'api.admin.sanitize-html', methods: ['POST'])]
221
    public function sanitizeHtml(Request $request, Context $context): JsonResponse
0 ignored issues
show
Unused Code introduced by
The parameter $context is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

221
    public function sanitizeHtml(Request $request, /** @scrutinizer ignore-unused */ Context $context): JsonResponse

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
222
    {
223
        if (!$request->request->has('html')) {
224
            throw RoutingException::missingRequestParameter('html');
225
        }
226
227
        $html = (string) $request->request->get('html');
228
        $field = (string) $request->request->get('field');
229
230
        if ($field === '') {
231
            return new JsonResponse(
232
                ['preview' => $this->htmlSanitizer->sanitize($html)]
233
            );
234
        }
235
236
        [$entityName, $propertyName] = explode('.', $field);
237
        $property = $this->definitionInstanceRegistry->getByEntityName($entityName)->getField($propertyName);
238
239
        if ($property === null) {
240
            throw RoutingException::invalidRequestParameter($field);
241
        }
242
243
        $flag = $property->getFlag(AllowHtml::class);
244
245
        if ($flag === null) {
246
            return new JsonResponse(
247
                ['preview' => strip_tags($html)]
248
            );
249
        }
250
251
        if ($flag instanceof AllowHtml && !$flag->isSanitized()) {
252
            return new JsonResponse(
253
                ['preview' => $html]
254
            );
255
        }
256
257
        return new JsonResponse(
258
            ['preview' => $this->htmlSanitizer->sanitize($html, [], false, $field)]
259
        );
260
    }
261
262
    private function fetchLanguageIdByName(string $isoCode, Connection $connection): ?string
263
    {
264
        $languageId = $connection->fetchOne(
265
            '
266
            SELECT `language`.id FROM `language`
267
            INNER JOIN locale ON language.translation_code_id = locale.id
268
            WHERE `code` = :code',
269
            ['code' => $isoCode]
270
        );
271
272
        return $languageId === false ? null : Uuid::fromBytesToHex($languageId);
273
    }
274
275
    private function getLatestApiVersion(): ?int
276
    {
277
        $sortedSupportedApiVersions = array_values($this->supportedApiVersions);
278
279
        usort($sortedSupportedApiVersions, fn (int $version1, int $version2) => \version_compare((string) $version1, (string) $version2));
280
281
        return array_pop($sortedSupportedApiVersions);
282
    }
283
284
    private function getCustomerByEmail(string $customerId, string $email, Context $context, ?string $boundSalesChannelId): ?CustomerEntity
285
    {
286
        $criteria = new Criteria();
287
        $criteria->setLimit(1);
288
        if ($boundSalesChannelId) {
289
            $criteria->addAssociation('boundSalesChannel');
290
        }
291
292
        $criteria->addFilter(new EqualsFilter('email', $email));
293
        $criteria->addFilter(new EqualsFilter('guest', false));
294
        $criteria->addFilter(new NotFilter(
295
            NotFilter::CONNECTION_AND,
296
            [new EqualsFilter('id', $customerId)]
297
        ));
298
299
        $criteria->addFilter(new MultiFilter(MultiFilter::CONNECTION_OR, [
300
            new EqualsFilter('boundSalesChannelId', null),
301
            new EqualsFilter('boundSalesChannelId', $boundSalesChannelId),
302
        ]));
303
304
        /** @var ?CustomerEntity $customer */
305
        $customer = $this->customerRepo->search($criteria, $context)->first();
306
307
        return $customer;
308
    }
309
}
310