Completed
Push — master ( 4b782d...65e0b3 )
by Michael
01:54
created

JsonApiDocumentParameterConverter::apply()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 37
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 37
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 22
nc 5
nop 2
1
<?php
2
declare(strict_types = 1);
3
4
namespace Mikemirten\Bundle\JsonApiBundle\Request;
5
6
use Mikemirten\Component\JsonApi\Document\AbstractDocument;
7
use Mikemirten\Component\JsonApi\Document\NoDataDocument;
8
use Mikemirten\Component\JsonApi\Document\ResourceCollectionDocument;
9
use Mikemirten\Component\JsonApi\Document\SingleResourceDocument;
10
use Mikemirten\Component\JsonApi\Hydrator\DocumentHydrator;
11
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
12
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface;
13
use Symfony\Component\HttpFoundation\Request;
14
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
15
16
/**
17
 * Parameter converter of request's body into a JsonAPI document
18
 *
19
 * @package Mikemirten\Bundle\JsonApiBundle\Request
20
 */
21
class JsonApiDocumentParameterConverter implements ParamConverterInterface
22
{
23
    /**
24
     * @var DocumentHydrator
25
     */
26
    protected $hydrator;
27
28
    /**
29
     * JsonApiDocumentParameterConverter constructor.
30
     *
31
     * @param DocumentHydrator $hydrator
32
     */
33
    public function __construct(DocumentHydrator $hydrator)
34
    {
35
        $this->hydrator = $hydrator;
36
    }
37
38
    /**
39
     * {@inheritdoc}
40
     */
41
    public function apply(Request $request, ParamConverter $configuration)
42
    {
43
        $isOptional = $configuration->isOptional();
44
        $isJsonApi  = $request->headers->contains('Content-Type', 'application/vnd.api+json');
45
46
        if (! $isOptional && ! $isJsonApi) {
47
            throw new BadRequestHttpException(sprintf(
48
                'Invalid media-type of request, "application/vnd.api+json" expected, "%s" given.',
49
                implode(', ', (array) $request->headers->get('Content-Type'))
50
            ));
51
        }
52
53
        $content = $request->getContent();
54
55
        if (empty($content)) {
56
            if ($isOptional) {
57
                return false;
58
            }
59
60
            throw new BadRequestHttpException('Request body is empty');
61
        }
62
63
        $decoded  = $this->decodeContent($content);
64
        $document = $this->hydrator->hydrate($decoded);
65
        $expected = $configuration->getClass();
66
67
        if ($expected === AbstractDocument::class || $document instanceof $expected) {
68
            $request->attributes->set($configuration->getName(), $document);
69
            return true;
70
        }
71
72
        throw new BadRequestHttpException(sprintf(
73
            'Provided document of type "%s" does not meet expected document of type "%s"',
74
            get_class($document),
75
            $expected
76
        ));
77
    }
78
79
    /**
80
     * Decode JSON
81
     *
82
     * @param  string $content
83
     * @return mixed
84
     */
85
    protected function decodeContent(string $content): \stdClass
86
    {
87
        $decoded = json_decode($content);
88
89
        if (json_last_error() !== JSON_ERROR_NONE) {
90
            throw new BadRequestHttpException('Decoding error: ' . json_last_error_msg());
91
        }
92
93
        return $decoded;
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99
    public function supports(ParamConverter $configuration)
100
    {
101
        return in_array(
102
            $configuration->getClass(),
103
            [
104
                NoDataDocument::class,
105
                AbstractDocument::class,
106
                SingleResourceDocument::class,
107
                ResourceCollectionDocument::class
108
            ],
109
            true
110
        );
111
    }
112
}