Test Failed
Push — main ( 388b7c...eafc82 )
by Daniel
17:58
created

hasCacheableSupportsMethod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 1
b 0
f 0
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\Serializer\Normalizer;
15
16
use ApiPlatform\Api\IriConverterInterface;
17
use Doctrine\ORM\Mapping\ClassMetadataInfo;
18
use Doctrine\Persistence\ManagerRegistry;
19
use Silverback\ApiComponentsBundle\DataProvider\PageDataProvider;
20
use Silverback\ApiComponentsBundle\Entity\Core\AbstractComponent;
21
use Silverback\ApiComponentsBundle\Entity\Core\ComponentPosition;
22
use Silverback\ApiComponentsBundle\Exception\InvalidArgumentException;
23
use Silverback\ApiComponentsBundle\Helper\ComponentPosition\ComponentPositionSortValueHelper;
24
use Silverback\ApiComponentsBundle\Helper\Publishable\PublishableStatusChecker;
25
use Symfony\Component\HttpFoundation\RequestStack;
26
use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
27
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
28
use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
29
use Symfony\Component\PropertyAccess\PropertyAccess;
30
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
31
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
32
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
33
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
34
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
35
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
36
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
37
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
38
39
/**
40
 * When creating a new component position the sort value should be set if not already explicitly set in the request.
41
 *
42
 * @author Daniel West <[email protected]>
43
 */
44
class ComponentPositionNormalizer implements CacheableSupportsMethodInterface, ContextAwareDenormalizerInterface, DenormalizerAwareInterface, ContextAwareNormalizerInterface, NormalizerAwareInterface
0 ignored issues
show
Deprecated Code introduced by
The interface Symfony\Component\Serial...reDenormalizerInterface has been deprecated: since symfony/serializer 6.1, use DenormalizerInterface instead ( Ignorable by Annotation )

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

44
class ComponentPositionNormalizer implements CacheableSupportsMethodInterface, /** @scrutinizer ignore-deprecated */ ContextAwareDenormalizerInterface, DenormalizerAwareInterface, ContextAwareNormalizerInterface, NormalizerAwareInterface

This interface has been deprecated. The supplier of the interface has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the interface will be removed and what other interface to use instead.

Loading history...
Deprecated Code introduced by
The interface Symfony\Component\Serial...wareNormalizerInterface has been deprecated: since symfony/serializer 6.1, use NormalizerInterface instead ( Ignorable by Annotation )

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

44
class ComponentPositionNormalizer implements CacheableSupportsMethodInterface, ContextAwareDenormalizerInterface, DenormalizerAwareInterface, /** @scrutinizer ignore-deprecated */ ContextAwareNormalizerInterface, NormalizerAwareInterface

This interface has been deprecated. The supplier of the interface has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the interface will be removed and what other interface to use instead.

Loading history...
45
{
46
    use DenormalizerAwareTrait;
47
    use NormalizerAwareTrait;
48
49
    private const ALREADY_CALLED = 'COMPONENT_POSITION_NORMALIZER_ALREADY_CALLED';
50
51
    private PageDataProvider $pageDataProvider;
52
    private ComponentPositionSortValueHelper $componentPositionSortValueHelper;
53
    private RequestStack $requestStack;
54
    private PublishableStatusChecker $publishableStatusChecker;
55
    private ManagerRegistry $registry;
56
    private IriConverterInterface $iriConverter;
57
58
    public function __construct(
59
        PageDataProvider $pageDataProvider,
60
        ComponentPositionSortValueHelper $componentPositionSortValueHelper,
61
        RequestStack $requestStack,
62
        PublishableStatusChecker $publishableStatusChecker,
63
        ManagerRegistry $registry,
64
        IriConverterInterface $iriConverter
65
    ) {
66
        $this->pageDataProvider = $pageDataProvider;
67
        $this->componentPositionSortValueHelper = $componentPositionSortValueHelper;
68
        $this->requestStack = $requestStack;
69
        $this->publishableStatusChecker = $publishableStatusChecker;
70
        $this->registry = $registry;
71
        $this->iriConverter = $iriConverter;
72
    }
73
74
    public function hasCacheableSupportsMethod(): bool
75
    {
76
        return false;
77
    }
78
79
    public function supportsDenormalization($data, $type, $format = null, array $context = []): bool
80
    {
81
        return !isset($context[self::ALREADY_CALLED]) && ComponentPosition::class === $type;
82
    }
83
84
    public function denormalize($data, $type, $format = null, array $context = []): ComponentPosition
85
    {
86
        $context[self::ALREADY_CALLED] = true;
87
88
        $originalObject = $context[AbstractNormalizer::OBJECT_TO_POPULATE] ?? null;
89
        $originalSortValue = $originalObject ? $originalObject->sortValue : null;
90
91
        /** @var ComponentPosition $object */
92
        $object = $this->denormalizer->denormalize($data, $type, $format, $context);
93
94
        $this->componentPositionSortValueHelper->calculateSortValue($object, $originalSortValue);
95
96
        return $object;
97
    }
98
99
    public function supportsNormalization($data, $format = null, array $context = []): bool
100
    {
101
        return $data instanceof ComponentPosition && !isset($context[self::ALREADY_CALLED]);
102
    }
103
104
    public function normalize($object, $format = null, array $context = []): float|array|\ArrayObject|bool|int|string|null
105
    {
106
        /* @var ComponentPosition $object */
107
        /* @var mixed|null        $format */
108
109
        $context[self::ALREADY_CALLED] = true;
110
111
        $staticComponent = $object->component ? $this->getPublishableComponent($object->component) : null;
112
        $context[MetadataNormalizer::METADATA_CONTEXT]['static_component'] = $staticComponent ? $this->iriConverter->getIriFromResource($staticComponent) : null;
113
114
        $object = $this->normalizeForPageData($object);
115
        if ($object->component !== $staticComponent) {
116
            $component = $object->component;
117
            $object->setComponent($this->getPublishableComponent($component));
0 ignored issues
show
Bug introduced by
It seems like $this->getPublishableComponent($component) can also be of type null; however, parameter $component of Silverback\ApiComponents...osition::setComponent() does only seem to accept Silverback\ApiComponents...\Core\AbstractComponent, 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

117
            $object->setComponent(/** @scrutinizer ignore-type */ $this->getPublishableComponent($component));
Loading history...
118
        }
119
120
        return $this->normalizer->normalize($object, $format, $context);
121
    }
122
123
    private function getPublishableComponent($component)
124
    {
125
        if (
126
            $component &&
127
            $this->publishableStatusChecker->getAnnotationReader()->isConfigured($component) &&
128
            $this->publishableStatusChecker->isGranted($component)
129
        ) {
130
            return $this->normalizePublishableComponent($component);
131
        }
132
        return $component;
133
    }
134
135
    private function normalizePublishableComponent(AbstractComponent $component)
136
    {
137
        $configuration = $this->publishableStatusChecker->getAnnotationReader()->getConfiguration($type = \get_class($component));
138
        $em = $this->registry->getManagerForClass(\get_class($component));
139
        if (!$em) {
140
            throw new InvalidArgumentException(sprintf('Could not find entity manager for class %s', $type));
141
        }
142
        /** @var ClassMetadataInfo $classMetadata */
143
        $classMetadata = $em->getClassMetadata($type);
144
        $draft = $classMetadata->getFieldValue($component, $configuration->reverseAssociationName);
145
146
        return $draft ?? $component;
147
    }
148
149
    private function normalizeForPageData(ComponentPosition $object): ComponentPosition
150
    {
151
        if (!$object->pageDataProperty || !$this->requestStack->getCurrentRequest()) {
152
            return $object;
153
        }
154
        $pageData = $this->pageDataProvider->getPageData();
155
        if (!$pageData) {
156
            return $object;
157
        }
158
159
        $propertyAccessor = PropertyAccess::createPropertyAccessor();
160
        try {
161
            $component = $propertyAccessor->getValue($pageData, $object->pageDataProperty);
162
        } catch (UnexpectedTypeException|NoSuchIndexException|NoSuchPropertyException $e) {
163
            return $object;
164
        }
165
166
        // optional to have the page data component found
167
        if (!$component) {
168
            return $object;
169
        }
170
171
        // it must be a component if it is found though
172
        if (!$component instanceof AbstractComponent) {
173
            throw new InvalidArgumentException(sprintf('The page data property %s is not a component', $object->pageDataProperty));
174
        }
175
176
        // populate the position
177
        $object->setComponent($component);
178
179
        return $object;
180
    }
181
}
182