Passed
Branch feature/throw-422-properly-on-... (3d4d32)
by Pieter
04:18
created

ApieObjectNormalizer::setAttributeValue()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 2
nop 5
dl 0
loc 6
rs 10
1
<?php
2
namespace W2w\Lib\Apie\Normalizers;
3
4
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
5
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
6
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
7
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
8
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
9
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
10
use Throwable;
11
use W2w\Lib\Apie\Exceptions\ApieException;
12
use W2w\Lib\Apie\Exceptions\ValidationException;
13
14
/**
15
 * Class overriding ObjectNormalizer to workaround https://github.com/symfony/symfony/issues/33622
16
 */
17
class ApieObjectNormalizer extends ObjectNormalizer
18
{
19
    /**
20
     * @var PropertyInfoExtractor|null
21
     */
22
    private $propertyInfoExtractor;
23
24
    /**
25
     * @param ClassMetadataFactoryInterface|null $classMetadataFactory
26
     * @param NameConverterInterface|null $nameConverter
27
     * @param PropertyAccessorInterface|null $propertyAccessor
28
     * @param PropertyInfoExtractor|null $propertyInfoExtractor
29
     * @param ClassDiscriminatorResolverInterface|null $classDiscriminatorResolver
30
     * @param callable|null $objectClassResolver
31
     * @param array $defaultContext
32
     */
33
    public function __construct(
34
        ClassMetadataFactoryInterface $classMetadataFactory = null,
35
        NameConverterInterface $nameConverter = null,
36
        PropertyAccessorInterface $propertyAccessor = null,
37
        PropertyInfoExtractor $propertyInfoExtractor = null,
38
        ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null,
39
        callable $objectClassResolver = null,
40
        array $defaultContext = []
41
    ) {
42
        $this->propertyInfoExtractor = $propertyInfoExtractor;
43
        parent::__construct(
44
            $classMetadataFactory,
45
            $nameConverter,
46
            $propertyAccessor,
47
            $propertyInfoExtractor,
48
            $classDiscriminatorResolver,
49
            $objectClassResolver,
50
            $defaultContext
51
        );
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57
    public function normalize($object, $format = null, array $context = [])
58
    {
59
        $context['apie_direction'] = 'read';
60
        return parent::normalize($object, $format, $context);
61
    }
62
63
    /**
64
     * {@inheritdoc}
65
     */
66
    public function denormalize($data, $type, $format = null, array $context = [])
67
    {
68
        $context['apie_direction'] = 'write';
69
        return parent::denormalize($data, $type, $format, $context);
70
    }
71
72
    /**
73
     * {@inheritdoc}
74
     */
75
    public function hasCacheableSupportsMethod(): bool
76
    {
77
        return true;
78
    }
79
80
    /**
81
     * {@inheritDoc}
82
     */
83
    protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = [])
84
    {
85
        try {
86
            parent::setAttributeValue($object, $attribute, $value, $format, $context);
87
        } catch (Throwable $throwable) {
88
            throw new ValidationException(['attribute' => $throwable->getMessage()], $throwable);
89
        }
90
    }
91
92
    /**
93
     * {@inheritdoc}
94
     */
95
    protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = [])
96
    {
97
        $isAllowed = parent::isAllowedAttribute(
98
            $classOrObject,
99
            $attribute,
100
            $format,
101
            $context
102
        );
103
        if ($isAllowed && $this->propertyInfoExtractor && !empty($context['apie_direction'])) {
104
            $className = is_object($classOrObject) ? get_class($classOrObject) : $classOrObject;
105
            switch ($context['apie_direction']) {
106
                case 'read':
107
                    return (bool) $this->propertyInfoExtractor->isReadable($className, $attribute);
108
                case 'write':
109
                    if (empty($context['object_to_populate'])) {
110
                        return $this->propertyInfoExtractor->isWritable($className, $attribute)
111
                            || $this->propertyInfoExtractor->isInitializable($className, $attribute);
112
                    }
113
                    return (bool) $this->propertyInfoExtractor->isWritable($className, $attribute);
114
            }
115
        }
116
        return $isAllowed;
117
    }
118
}
119