Completed
Push — master ( 65e0b3...862681 )
by Michael
01:56
created

resolveRequestBody()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 8.6737
c 0
b 0
f 0
cc 5
eloc 12
nc 4
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
        $content = $this->resolveRequestBody($request, $configuration);
44
45
        if ($content === null) {
46
            return false;
47
        }
48
49
        $decoded  = $this->decodeContent($content);
50
        $document = $this->hydrator->hydrate($decoded);
51
        $expected = $configuration->getClass();
52
53
        if ($expected === AbstractDocument::class || $document instanceof $expected) {
54
            $request->attributes->set($configuration->getName(), $document);
55
            return true;
56
        }
57
58
        throw new BadRequestHttpException(sprintf(
59
            'Provided document of type "%s" does not meet expected document of type "%s"',
60
            get_class($document),
61
            $expected
62
        ));
63
    }
64
65
    /**
66
     * Resolve request body
67
     *
68
     * @param  Request        $request
69
     * @param  ParamConverter $configuration
70
     * @throws BadRequestHttpException
71
     */
72
    protected function resolveRequestBody(Request $request, ParamConverter $configuration)
73
    {
74
        $isOptional = $configuration->isOptional();
75
        $isJsonApi  = $request->headers->contains('Content-Type', 'application/vnd.api+json');
76
77
        if (! $isOptional && ! $isJsonApi) {
78
            throw new BadRequestHttpException(sprintf(
79
                'Invalid media-type of request, "application/vnd.api+json" expected, "%s" given.',
80
                implode(', ', (array) $request->headers->get('Content-Type'))
81
            ));
82
        }
83
84
        $content = $request->getContent();
85
86
        if (! empty($content)) {
87
            return $content;
88
        }
89
90
        if (! $isOptional) {
91
            throw new BadRequestHttpException('Request body is empty');
92
        }
93
    }
94
95
    /**
96
     * Decode JSON
97
     *
98
     * @param  string $content
99
     * @return mixed
100
     * @throws BadRequestHttpException
101
     */
102
    protected function decodeContent(string $content): \stdClass
103
    {
104
        $decoded = json_decode($content);
105
106
        if (json_last_error() !== JSON_ERROR_NONE) {
107
            throw new BadRequestHttpException('Decoding error: ' . json_last_error_msg());
108
        }
109
110
        return $decoded;
111
    }
112
113
    /**
114
     * {@inheritdoc}
115
     */
116
    public function supports(ParamConverter $configuration)
117
    {
118
        return in_array(
119
            $configuration->getClass(),
120
            [
121
                NoDataDocument::class,
122
                AbstractDocument::class,
123
                SingleResourceDocument::class,
124
                ResourceCollectionDocument::class
125
            ],
126
            true
127
        );
128
    }
129
}