Passed
Push — master ( e59562...d63ab9 )
by Anton
33s
created

JsonApiParser::parse()   D

Complexity

Conditions 9
Paths 7

Size

Total Lines 29
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
rs 4.909
c 0
b 0
f 0
cc 9
eloc 19
nc 7
nop 2
1
<?php
2
/**
3
 * @author Anton Tuyakhov <[email protected]>
4
 */
5
6
namespace tuyakhov\jsonapi;
7
8
use yii\helpers\ArrayHelper;
9
use yii\web\BadRequestHttpException;
10
use \yii\web\JsonParser;
11
12
class JsonApiParser extends JsonParser
13
{
14
    /**
15
     * Converts 'type' member to form name
16
     * If not set, type will be converted to singular form.
17
     * For example, 'articles' will be converted to 'Article'
18
     * @var callable
19
     */
20
    public $formNameCallback = ['tuyakhov\jsonapi\Inflector', 'type2form'];
21
22
    /**
23
     * Converts member names to variable names
24
     * If not set, all special characters will be replaced by underscore
25
     * For example, 'first-name' will be converted to 'first_name'
26
     * @var callable
27
     */
28
    public $memberNameCallback = ['tuyakhov\jsonapi\Inflector', 'member2var'];
29
30
    /**
31
     * Parse resource object into the input data to populates the model
32
     * @inheritdoc
33
     */
34
    public function parse($rawBody, $contentType)
35
    {
36
        $array = parent::parse($rawBody, $contentType);
37
        if (!empty($array) && !ArrayHelper::keyExists('data', $array)) {
38
            if ($this->throwException) {
39
                throw new BadRequestHttpException('The request MUST include a single resource object as primary data.');
40
            }
41
            return [];
42
        }
43
        $data =  ArrayHelper::getValue($array, 'data', []);
44
        if (empty($data)) {
45
            return [];
46
        }
47
        if (ArrayHelper::isAssociative($data)) {
48
            $result = $this->parseResource($data);
49
50
            $relObjects = ArrayHelper::getValue($data, 'relationships', []);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 14 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
51
            $result['relationships'] = $this->parseRelationships($relObjects);
52
        } else {
53
            foreach ($data as $object) {
54
                $resource = $this->parseResource($object);
55
                foreach (array_keys($resource) as $key) {
56
                    $result[$key][] = $resource[$key];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$result was never initialized. Although not strictly required by PHP, it is generally a good practice to add $result = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
57
                }
58
            }
59
        }
60
61
        return isset($result) ? $result : $array;
62
    }
63
64
    /**
65
     * @param $type 'type' member of the document
66
     * @return string form name
67
     */
68
    protected function typeToFormName($type)
69
    {
70
        return call_user_func($this->formNameCallback, $type);
71
    }
72
73
    /**
74
     * @param array $memberNames
75
     * @return array variable names
76
     */
77
    protected function parseMemberNames(array $memberNames = [])
78
    {
79
        return array_map($this->memberNameCallback, $memberNames);
80
    }
81
82
    /**
83
     * @param $item
84
     * @return array
85
     * @throws BadRequestHttpException
86
     */
87
    protected function parseResource($item)
88
    {
89
        if (!$type = ArrayHelper::getValue($item, 'type')) {
90
            if ($this->throwException) {
91
                throw new BadRequestHttpException('The resource object MUST contain at least a type member');
92
            }
93
            return [];
94
        }
95
        $formName = $this->typeToFormName($type);
96
97
        $attributes = ArrayHelper::getValue($item, 'attributes', []);
98
        $attributes = array_combine($this->parseMemberNames(array_keys($attributes)), array_values($attributes));
99
100
        if ($id = ArrayHelper::getValue($item, 'id')) {
101
            $attributes['id'] = $id;
102
        }
103
104
        return [$formName => $attributes];
105
    }
106
107
    /**
108
     * @param array $relObjects
109
     * @return array
110
     */
111
    protected function parseRelationships(array $relObjects = [])
112
    {
113
        $relationships = [];
114
        foreach ($relObjects as $name => $relationship) {
115
            if (!ArrayHelper::keyExists('data', $relationship)) {
116
                continue;
117
            }
118
            $relData = ArrayHelper::getValue($relationship, 'data', []);
119
            if (!ArrayHelper::isIndexed($relData)) {
120
                $relData = [$relData];
121
            }
122
            $relationships[$name] = [];
123
            foreach ($relData as $identifier) {
124
                if (isset($identifier['type']) && isset($identifier['id'])) {
125
                    $formName = $this->typeToFormName($identifier['type']);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 26 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
126
                    $relationships[$name][$formName][] = ['id' => $identifier['id']];
127
                }
128
            }
129
        }
130
        return $relationships;
131
    }
132
}
0 ignored issues
show
Coding Style introduced by
As per coding style, files should not end with a newline character.

This check marks files that end in a newline character, i.e. an empy line.

Loading history...
133