PartialCollectionViewNormalizer::normalize()   F
last analyzed

Complexity

Conditions 25
Paths 473

Size

Total Lines 69
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 25
eloc 41
c 1
b 0
f 0
nc 473
nop 3
dl 0
loc 69
rs 0.7319

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[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 ApiPlatform\Core\Hydra\Serializer;
15
16
use ApiPlatform\Core\DataProvider\PaginatorInterface;
17
use ApiPlatform\Core\DataProvider\PartialPaginatorInterface;
18
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
19
use ApiPlatform\Core\Util\IriHelper;
20
use Symfony\Component\PropertyAccess\PropertyAccess;
21
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
22
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
23
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
24
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
25
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
26
27
/**
28
 * Adds a view key to the result of a paginated Hydra collection.
29
 *
30
 * @author Kévin Dunglas <[email protected]>
31
 * @author Samuel ROZE <[email protected]>
32
 */
33
final class PartialCollectionViewNormalizer implements NormalizerInterface, NormalizerAwareInterface, CacheableSupportsMethodInterface
34
{
35
    private $collectionNormalizer;
36
    private $pageParameterName;
37
    private $enabledParameterName;
38
    private $resourceMetadataFactory;
39
    private $propertyAccessor;
40
41
    public function __construct(NormalizerInterface $collectionNormalizer, string $pageParameterName = 'page', string $enabledParameterName = 'pagination', ResourceMetadataFactoryInterface $resourceMetadataFactory = null, PropertyAccessorInterface $propertyAccessor = null)
42
    {
43
        $this->collectionNormalizer = $collectionNormalizer;
44
        $this->pageParameterName = $pageParameterName;
45
        $this->enabledParameterName = $enabledParameterName;
46
        $this->resourceMetadataFactory = $resourceMetadataFactory;
47
        $this->propertyAccessor = $propertyAccessor ?? PropertyAccess::createPropertyAccessor();
48
    }
49
50
    /**
51
     * {@inheritdoc}
52
     */
53
    public function normalize($object, $format = null, array $context = [])
54
    {
55
        $data = $this->collectionNormalizer->normalize($object, $format, $context);
56
        if (!\is_array($data)) {
57
            throw new UnexpectedValueException('Expected data to be an array');
58
        }
59
60
        if (isset($context['api_sub_level'])) {
61
            return $data;
62
        }
63
64
        $currentPage = $lastPage = $itemsPerPage = $pageTotalItems = null;
65
        if ($paginated = $object instanceof PartialPaginatorInterface) {
66
            if ($object instanceof PaginatorInterface) {
67
                $paginated = 1. !== $lastPage = $object->getLastPage();
68
            } else {
69
                $itemsPerPage = $object->getItemsPerPage();
70
                $pageTotalItems = (float) \count($object);
71
            }
72
73
            $currentPage = $object->getCurrentPage();
74
        }
75
76
        $parsed = IriHelper::parseIri($context['request_uri'] ?? '/', $this->pageParameterName);
77
        $appliedFilters = $parsed['parameters'];
78
        unset($appliedFilters[$this->enabledParameterName]);
79
80
        if (!$appliedFilters && !$paginated) {
81
            return $data;
82
        }
83
84
        $metadata = isset($context['resource_class']) && null !== $this->resourceMetadataFactory ? $this->resourceMetadataFactory->create($context['resource_class']) : null;
85
        $isPaginatedWithCursor = $paginated && null !== $metadata && null !== $cursorPaginationAttribute = $metadata->getCollectionOperationAttribute($context['collection_operation_name'] ?? $context['subresource_operation_name'], 'pagination_via_cursor', null, true);
86
87
        $data['hydra:view'] = [
88
            '@id' => IriHelper::createIri($parsed['parts'], $parsed['parameters'], $this->pageParameterName, $paginated && !$isPaginatedWithCursor ? $currentPage : null),
89
            '@type' => 'hydra:PartialCollectionView',
90
        ];
91
92
        if ($isPaginatedWithCursor) {
93
            $objects = iterator_to_array($object);
94
            $firstObject = current($objects);
95
            $lastObject = end($objects);
96
97
            $data['hydra:view']['@id'] = IriHelper::createIri($parsed['parts'], $parsed['parameters']);
98
99
            if (false !== $lastObject && isset($cursorPaginationAttribute)) {
100
                $data['hydra:view']['hydra:next'] = IriHelper::createIri($parsed['parts'], array_merge($parsed['parameters'], $this->cursorPaginationFields($cursorPaginationAttribute, 1, $lastObject)));
101
            }
102
103
            if (false !== $firstObject && isset($cursorPaginationAttribute)) {
104
                $data['hydra:view']['hydra:previous'] = IriHelper::createIri($parsed['parts'], array_merge($parsed['parameters'], $this->cursorPaginationFields($cursorPaginationAttribute, -1, $firstObject)));
105
            }
106
        } elseif ($paginated) {
107
            if (null !== $lastPage) {
108
                $data['hydra:view']['hydra:first'] = IriHelper::createIri($parsed['parts'], $parsed['parameters'], $this->pageParameterName, 1.);
109
                $data['hydra:view']['hydra:last'] = IriHelper::createIri($parsed['parts'], $parsed['parameters'], $this->pageParameterName, $lastPage);
110
            }
111
112
            if (1. !== $currentPage) {
113
                $data['hydra:view']['hydra:previous'] = IriHelper::createIri($parsed['parts'], $parsed['parameters'], $this->pageParameterName, $currentPage - 1.);
114
            }
115
116
            if (null !== $lastPage && $currentPage !== $lastPage || null === $lastPage && $pageTotalItems >= $itemsPerPage) {
0 ignored issues
show
introduced by meyerbaptiste
Consider adding parentheses for clarity. Current Interpretation: (null !== $lastPage && $...lItems >= $itemsPerPage, Probably Intended Meaning: null !== $lastPage && ($...Items >= $itemsPerPage)
Loading history...
117
                $data['hydra:view']['hydra:next'] = IriHelper::createIri($parsed['parts'], $parsed['parameters'], $this->pageParameterName, $currentPage + 1.);
118
            }
119
        }
120
121
        return $data;
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     */
127
    public function supportsNormalization($data, $format = null)
128
    {
129
        return $this->collectionNormalizer->supportsNormalization($data, $format);
130
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135
    public function hasCacheableSupportsMethod(): bool
136
    {
137
        return $this->collectionNormalizer instanceof CacheableSupportsMethodInterface && $this->collectionNormalizer->hasCacheableSupportsMethod();
0 ignored issues
show
Bug introduced by Kévin Dunglas
The method hasCacheableSupportsMethod() does not exist on Symfony\Component\Serial...zer\NormalizerInterface. It seems like you code against a sub-type of said class. However, the method does not exist in ApiPlatform\Core\GraphQl...HttpExceptionNormalizer or Symfony\Component\Serial...er\SerializerNormalizer or ApiPlatform\Core\GraphQl...ception\ErrorNormalizer or ApiPlatform\Core\Tests\F...DocumentationNormalizer or Symfony\Component\Serial...rmalizer\TestNormalizer or Symfony\Component\Serial...sonSerializerNormalizer or ApiPlatform\Core\GraphQl...tionExceptionNormalizer or Symfony\Component\Serial...wareNormalizerInterface or Symfony\Component\Serial...ectSerializerNormalizer or ApiPlatform\Core\GraphQl...timeExceptionNormalizer or Symfony\Component\Serializer\Serializer. Are you sure you never get one of those? ( Ignorable by Annotation )

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

137
        return $this->collectionNormalizer instanceof CacheableSupportsMethodInterface && $this->collectionNormalizer->/** @scrutinizer ignore-call */ hasCacheableSupportsMethod();
Loading history...
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143
    public function setNormalizer(NormalizerInterface $normalizer)
144
    {
145
        if ($this->collectionNormalizer instanceof NormalizerAwareInterface) {
146
            $this->collectionNormalizer->setNormalizer($normalizer);
0 ignored issues
show
Bug introduced by Kévin Dunglas
The method setNormalizer() does not exist on Symfony\Component\Serial...zer\NormalizerInterface. It seems like you code against a sub-type of Symfony\Component\Serial...zer\NormalizerInterface such as ApiPlatform\Core\Hydra\S...ectionFiltersNormalizer or ApiPlatform\Core\Seriali...actCollectionNormalizer or ApiPlatform\Core\Hydra\S...ollectionViewNormalizer or ApiPlatform\Core\Hydra\S...er\CollectionNormalizer. ( Ignorable by Annotation )

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

146
            $this->collectionNormalizer->/** @scrutinizer ignore-call */ 
147
                                         setNormalizer($normalizer);
Loading history...
147
        }
148
    }
149
150
    private function cursorPaginationFields(array $fields, int $direction, $object)
151
    {
152
        $paginationFilters = [];
153
154
        foreach ($fields as $field) {
155
            $forwardRangeOperator = 'desc' === strtolower($field['direction']) ? 'lt' : 'gt';
156
            $backwardRangeOperator = 'gt' === $forwardRangeOperator ? 'lt' : 'gt';
157
158
            $operator = $direction > 0 ? $forwardRangeOperator : $backwardRangeOperator;
159
160
            $paginationFilters[$field['field']] = [
161
                $operator => (string) $this->propertyAccessor->getValue($object, $field['field']),
162
            ];
163
        }
164
165
        return $paginationFilters;
166
    }
167
}
168