Passed
Pull Request — feature/publishable (#43)
by Daniel
06:23
created

PublishableNormalizer::denormalize()   C

Complexity

Conditions 13
Paths 35

Size

Total Lines 81
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 37
c 1
b 0
f 0
dl 0
loc 81
ccs 0
cts 36
cp 0
rs 6.6166
cc 13
nc 35
nop 4
crap 182

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 Silverback API Component 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\ApiComponentBundle\Serializer;
15
16
use Doctrine\Persistence\ManagerRegistry;
17
use Silverback\ApiComponentBundle\Exception\InvalidArgumentException;
18
use Silverback\ApiComponentBundle\Publishable\PublishableHelper;
19
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
20
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
21
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
22
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
23
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
24
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
25
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
26
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
27
28
/**
29
 * Adds `published` property on response, if not set.
30
 *
31
 * @author Vincent Chalamon <[email protected]>
32
 */
33
final class PublishableNormalizer implements ContextAwareNormalizerInterface, CacheableSupportsMethodInterface, NormalizerAwareInterface, ContextAwareDenormalizerInterface, DenormalizerAwareInterface
34
{
35
    use NormalizerAwareTrait;
36
    use DenormalizerAwareTrait;
37
38
    private const ALREADY_CALLED = 'PUBLISHABLE_NORMALIZER_ALREADY_CALLED';
39
40
    private PublishableHelper $publishableHelper;
41
    private ManagerRegistry $registry;
42
43
    public function __construct(PublishableHelper $publishableHelper, ManagerRegistry $registry)
44
    {
45
        $this->publishableHelper = $publishableHelper;
46
        $this->registry = $registry;
47
    }
48
49
    public function normalize($object, $format = null, array $context = [])
50
    {
51
        $context[self::ALREADY_CALLED] = true;
52
        $data = $this->normalizer->normalize($object, $format, $context);
53
54
        if (!\array_key_exists('published', $data)) {
55
            $data['published'] = $this->publishableHelper->isPublished($object);
56
        }
57
58
        return $data;
59
    }
60
61
    public function supportsNormalization($data, $format = null, $context = []): bool
62
    {
63
        return !isset($context[self::ALREADY_CALLED]) &&
64
            \is_object($data) &&
65
            !$data instanceof \Traversable &&
66
            $this->publishableHelper->isPublishable($data);
67
    }
68
69
    /**
70
     * {@inheritdoc}
71
     */
72
    public function denormalize($data, $type, $format = null, array $context = [])
73
    {
74
        $context[self::ALREADY_CALLED] = true;
75
        $configuration = $this->publishableHelper->getConfiguration($type);
76
        if (!$configuration) {
77
            throw new InvalidArgumentException(sprintf('Could not get configuration for %s', $type));
78
        }
79
80
        // It's not possible to change the publishedResource and draftResource properties
81
        unset($data[$configuration->associationName], $data[$configuration->reverseAssociationName]);
82
83
        // User doesn't have draft access: cannot set or change the publication date
84
        if (!$this->publishableHelper->isGranted()) {
85
            unset($data[$configuration->fieldName]);
86
        }
87
88
        // It's a new object
89
        if (!isset($context[AbstractNormalizer::OBJECT_TO_POPULATE])) {
90
            // User doesn't have draft access: force publication date
91
            if (!$this->publishableHelper->isGranted()) {
92
                $data[$configuration->fieldName] = date('Y-m-d H:i:s');
93
            }
94
95
            return $this->denormalizer->denormalize($data, $type, $format, $context);
96
        }
97
98
        $object = $context[AbstractNormalizer::OBJECT_TO_POPULATE];
99
        if (isset($data[$configuration->fieldName])) {
100
            $publicationDate = new \DateTimeImmutable($data[$configuration->fieldName]);
101
102
            // User changed the publication date with an earlier one on a published resource: ignore it
103
            if (
104
                $this->publishableHelper->isPublished($object) &&
105
                new \DateTimeImmutable() >= $publicationDate
106
            ) {
107
                unset($data[$configuration->fieldName]);
108
            }
109
        }
110
111
        // No field has been updated: nothing to do here anymore
112
        if (empty($data)) {
113
            return $this->denormalizer->denormalize($data, $type, $format, $context);
114
        }
115
116
        $em = $this->registry->getManagerForClass($type);
117
        if (!$em) {
118
            throw new InvalidArgumentException(sprintf('Could not find entity manager for class %s', $type));
119
        }
120
        $classMetadata = $em->getClassMetadata($type);
121
122
        // Resource is a draft: nothing to do here anymore
123
        if (
124
            null !== $classMetadata->getFieldValue($object, $configuration->associationName) ||
0 ignored issues
show
Bug introduced by
The method getFieldValue() does not exist on Doctrine\Persistence\Mapping\ClassMetadata. Did you maybe mean getFieldNames()? ( Ignorable by Annotation )

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

124
            null !== $classMetadata->/** @scrutinizer ignore-call */ getFieldValue($object, $configuration->associationName) ||

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
125
            !$this->publishableHelper->isPublished($object)
126
        ) {
127
            return $this->denormalizer->denormalize($data, $type, $format, $context);
128
        }
129
130
        // User doesn't have draft access: update the original object
131
        if (!$this->publishableHelper->isGranted()) {
132
            return $this->denormalizer->denormalize($data, $type, $format, $context);
133
        }
134
135
        // Any field has been modified: create a draft
136
        $draft = clone $object; // Identifier(s) should be reset from AbstractComponent::__clone method
137
138
        // Add draft object to UnitOfWork
139
        $em->persist($draft);
140
141
        // Empty publishedDate on draft
142
        $classMetadata->setFieldValue($draft, $configuration->fieldName, null);
0 ignored issues
show
Bug introduced by
The method setFieldValue() does not exist on Doctrine\Persistence\Mapping\ClassMetadata. It seems like you code against a sub-type of Doctrine\Persistence\Mapping\ClassMetadata such as Doctrine\ORM\Mapping\ClassMetadataInfo. ( Ignorable by Annotation )

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

142
        $classMetadata->/** @scrutinizer ignore-call */ 
143
                        setFieldValue($draft, $configuration->fieldName, null);
Loading history...
143
144
        // Set publishedResource on draft
145
        $classMetadata->setFieldValue($draft, $configuration->associationName, $object);
146
147
        // Set draftResource on data
148
        $classMetadata->setFieldValue($object, $configuration->reverseAssociationName, $draft);
149
150
        $context[AbstractNormalizer::OBJECT_TO_POPULATE] = $draft;
151
152
        return $this->denormalizer->denormalize($data, $type, $format, $context);
153
    }
154
155
    /**
156
     * {@inheritdoc}
157
     */
158
    public function supportsDenormalization($data, $type, $format = null, array $context = []): bool
159
    {
160
        return !isset($context[self::ALREADY_CALLED]) && $this->publishableHelper->isPublishable($type);
161
    }
162
163
    public function hasCacheableSupportsMethod(): bool
164
    {
165
        return false;
166
    }
167
}
168