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 |
|
|
|
|
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)); |
|
|
|
|
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
|
|
|
|
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.