Passed
Pull Request — master (#44)
by
unknown
08:46
created

JsonApiParser::parse()   D

Complexity

Conditions 9
Paths 7

Size

Total Lines 30
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 30
rs 4.909
c 0
b 0
f 0
cc 9
eloc 20
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);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 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...
37
        $method = \Yii::$app->request->method;
38
        if (!in_array($method, ['GET', 'DELETE']) && ArrayHelper::keyExists('data', $array)) {
39
            if ($this->throwException) {
40
                throw new BadRequestHttpException('The request MUST include a single resource object as primary data.');
41
            }
42
            return [];
43
        }
44
        $data = ArrayHelper::getValue($array, 'data', []);
45
        if (empty($data)) {
46
            return [];
47
        }
48
        if (ArrayHelper::isAssociative($data)) {
49
            $result = $this->parseResource($data);
50
51
            $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...
52
            $result['relationships'] = $this->parseRelationships($relObjects);
53
        } else {
54
            foreach ($data as $object) {
55
                $resource = $this->parseResource($object);
56
                foreach (array_keys($resource) as $key) {
57
                    $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...
58
                }
59
            }
60
        }
61
62
        return isset($result) ? $result : $array;
63
    }
64
65
    /**
66
     * @param $type 'type' member of the document
67
     * @return string form name
68
     */
69
    protected function typeToFormName($type)
70
    {
71
        return call_user_func($this->formNameCallback, $type);
72
    }
73
74
    /**
75
     * @param array $memberNames
76
     * @return array variable names
77
     */
78
    protected function parseMemberNames(array $memberNames = [])
79
    {
80
        return array_map($this->memberNameCallback, $memberNames);
81
    }
82
83
    /**
84
     * @param $item
85
     * @return array
86
     * @throws BadRequestHttpException
87
     */
88
    protected function parseResource($item)
89
    {
90
        if (!$type = ArrayHelper::getValue($item, 'type')) {
91
            if ($this->throwException) {
92
                throw new BadRequestHttpException('The resource object MUST contain at least a type member');
93
            }
94
            return [];
95
        }
96
        $formName = $this->typeToFormName($type);
97
98
        $attributes = ArrayHelper::getValue($item, 'attributes', []);
99
        $attributes = array_combine($this->parseMemberNames(array_keys($attributes)), array_values($attributes));
100
101
        if ($id = ArrayHelper::getValue($item, 'id')) {
102
            $attributes['id'] = $id;
103
        }
104
105
        return [$formName => $attributes];
106
    }
107
108
    /**
109
     * @param array $relObjects
110
     * @return array
111
     */
112
    protected function parseRelationships(array $relObjects = [])
113
    {
114
        $relationships = [];
115
        foreach ($relObjects as $name => $relationship) {
116
            if (!ArrayHelper::keyExists('data', $relationship)) {
117
                continue;
118
            }
119
            $relData = ArrayHelper::getValue($relationship, 'data', []);
120
            if (!ArrayHelper::isIndexed($relData)) {
121
                $relData = [$relData];
122
            }
123
            $relationships[$name] = [];
124
            foreach ($relData as $identifier) {
125
                if (isset($identifier['type']) && isset($identifier['id'])) {
126
                    $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...
127
                    $relationships[$name][$formName][] = ['id' => $identifier['id']];
128
                }
129
            }
130
        }
131
        return $relationships;
132
    }
133
}
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...
134