Passed
Pull Request — master (#64)
by Daniel
10:56 queued 04:18
created

CollectionOutputDataTransformer::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 8
nc 1
nop 8
dl 0
loc 10
ccs 0
cts 9
cp 0
crap 2
rs 10
c 1
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
2
3
/*
4
 * This file is part of the Silverback API Components Bundle Project
5
 *
6
 * (c) Daniel West <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Silverback\ApiComponentsBundle\DataTransformer;
15
16
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
17
use ApiPlatform\Core\DataProvider\ContextAwareCollectionDataProviderInterface;
18
use ApiPlatform\Core\DataTransformer\DataTransformerInterface;
19
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
20
use ApiPlatform\Core\Util\AttributesExtractor;
21
use ApiPlatform\Core\Util\RequestParser;
22
use Silverback\ApiComponentsBundle\Entity\Component\Collection;
23
use Silverback\ApiComponentsBundle\Exception\OutOfBoundsException;
24
use Silverback\ApiComponentsBundle\Helper\Collection\CollectionHelper;
25
use Silverback\ApiComponentsBundle\Serializer\SerializeFormatResolver;
26
use Symfony\Component\HttpFoundation\Request;
27
use Symfony\Component\HttpFoundation\RequestStack;
28
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
29
30
/**
31
 * @author Daniel West <[email protected]>
32
 */
33
class CollectionOutputDataTransformer implements DataTransformerInterface
34
{
35
    private CollectionHelper $iriConverter;
36
    private CollectionDataProviderInterface $collectionDataProvider;
37
    private RequestStack $requestStack;
38
    private SerializerContextBuilderInterface $serializerContextBuilder;
39
    private NormalizerInterface $itemNormalizer;
40
    private SerializeFormatResolver $serializeFormatResolver;
41
    private string $itemsPerPageParameterName;
42
    private string $paginationEnabledParameterName;
43
44
    public function __construct(CollectionHelper $iriConverter, CollectionDataProviderInterface $collectionDataProvider, RequestStack $requestStack, SerializerContextBuilderInterface $serializerContextBuilder, NormalizerInterface $itemNormalizer, SerializeFormatResolver $serializeFormatResolver, string $itemsPerPageParameterName, string $paginationEnabledParameterName)
45
    {
46
        $this->iriConverter = $iriConverter;
47
        $this->collectionDataProvider = $collectionDataProvider;
48
        $this->requestStack = $requestStack;
49
        $this->serializerContextBuilder = $serializerContextBuilder;
50
        $this->itemNormalizer = $itemNormalizer;
51
        $this->serializeFormatResolver = $serializeFormatResolver;
52
        $this->itemsPerPageParameterName = $itemsPerPageParameterName;
53
        $this->paginationEnabledParameterName = $paginationEnabledParameterName;
54
    }
55
56
    public function supportsTransformation($data, string $to, array $context = []): bool
57
    {
58
        return $data instanceof Collection && Collection::class === $to;
59
    }
60
61
    /**
62
     * @param Collection $object
63
     */
64
    public function transform($object, string $to, array $context = [])
65
    {
66
        $parameters = $this->iriConverter->getRouterParametersFromIri($object->getResourceIri());
0 ignored issues
show
Bug introduced by
It seems like $object->getResourceIri() can also be of type null; however, parameter $iri of Silverback\ApiComponents...uterParametersFromIri() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

66
        $parameters = $this->iriConverter->getRouterParametersFromIri(/** @scrutinizer ignore-type */ $object->getResourceIri());
Loading history...
67
        $attributes = AttributesExtractor::extractAttributes($parameters);
68
        $request = $this->requestStack->getMasterRequest();
69
70
        if (!$this->collectionDataProvider instanceof ContextAwareCollectionDataProviderInterface) {
71
            $collectionData = $this->collectionDataProvider->getCollection($attributes['resource_class'], $attributes['collection_operation_name']);
72
        } else {
73
            $filters = [];
74
            if ($perPage = $object->getPerPage()) {
75
                $filters[$this->itemsPerPageParameterName] = $perPage;
76
            }
77
            if ($request) {
78
                if ($requestFilters = $this->getFilters($object, $request)) {
79
                    $filters = array_merge($filters, $requestFilters);
80
                }
81
            }
82
83
            if (isset($filters[$this->itemsPerPageParameterName]) && $filters[$this->itemsPerPageParameterName] <= 0) {
84
                $filters[$this->paginationEnabledParameterName] = false;
85
            }
86
87
            $collectionContext = ['filters' => $filters];
88
            if ($request) {
89
                // Comment copied from ApiPlatform\Core\EventListener\ReadListener
90
                // Builtin data providers are able to use the serialization context to automatically add join clauses
91
                $normalizationContext = $this->serializerContextBuilder->createFromRequest(
92
                    $request,
93
                    true,
94
                    $attributes
95
                );
96
                $collectionContext += $normalizationContext;
97
            }
98
99
            $collectionData = $this->collectionDataProvider->getCollection($attributes['resource_class'], Request::METHOD_GET, $collectionContext);
0 ignored issues
show
Unused Code introduced by
The call to ApiPlatform\Core\DataPro...erface::getCollection() has too many arguments starting with $collectionContext. ( Ignorable by Annotation )

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

99
            /** @scrutinizer ignore-call */ 
100
            $collectionData = $this->collectionDataProvider->getCollection($attributes['resource_class'], Request::METHOD_GET, $collectionContext);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
100
        }
101
102
        // Pagination disabled
103
        if (\is_array($collectionData)) {
104
            $collection = $collectionData;
105
        } else {
106
            if (!$collectionData instanceof \Traversable) {
107
                throw new OutOfBoundsException('$collectionData should be Traversable');
108
            }
109
            $collection = iterator_count($collectionData) ? $collectionData : null;
110
        }
111
112
        $format = $request ? $this->serializeFormatResolver->getFormatFromRequest($request) : null;
113
        $normalizerContext = [
114
            'resource_class' => $attributes['resource_class'],
115
            'request_uri' => $object->getResourceIri(),
116
            'jsonld_has_context' => false,
117
            'api_sub_level' => null,
118
            'subresource_operation_name' => Request::METHOD_GET,
119
        ];
120
        $normalizedCollection = $this->itemNormalizer->normalize($collection, $format, $normalizerContext);
121
122
        $object->setCollection($normalizedCollection);
123
124
        return $object;
125
    }
126
127
    private function getFilters(Collection $object, Request $request)
128
    {
129
        if ($queryParams = $object->getDefaultQueryParameters()) {
130
            $request = clone $request;
131
            foreach ($queryParams as $defaultKey => $defaultValue) {
132
                if ($request->query->has($defaultKey)) {
133
                    continue;
134
                }
135
                $request->query->set($defaultKey, $defaultValue);
136
            }
137
            $request->server->set('QUERY_STRING', http_build_query($request->query->all()));
138
        }
139
140
        $queryString = RequestParser::getQueryString($request);
141
142
        return $queryString ? RequestParser::parseRequestParams($queryString) : null;
143
    }
144
}
145