Passed
Push — master ( 53c726...5a2a34 )
by Anton
02:23
created

Serializer::serializeModel()   C

Complexity

Conditions 12
Paths 8

Size

Total Lines 43
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 43
c 0
b 0
f 0
rs 5.1612
cc 12
eloc 26
nc 8
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @author Anton Tuyakhov <[email protected]>
4
 */
5
6
namespace tuyakhov\jsonapi;
7
8
use yii\base\Component;
9
use yii\base\Model;
10
use yii\data\DataProviderInterface;
11
use yii\data\Pagination;
12
use yii\web\Link;
13
use yii\web\Linkable;
14
use yii\web\Request;
15
use yii\web\Response;
16
17
class Serializer extends Component
18
{
19
    /**
20
     * @var string the name of the query parameter containing the information about which fields should be returned
21
     * for a [[Model]] object. If the parameter is not provided or empty, the default set of fields as defined
22
     * by [[Model::fields()]] will be returned.
23
     */
24
    public $fieldsParam = 'fields';
25
    /**
26
     * @var string the name of the query parameter containing the information about which fields should be returned
27
     * in addition to those listed in [[fieldsParam]] for a resource object.
28
     */
29
    public $expandParam = 'include';
30
    /**
31
     * @var string the name of the envelope (e.g. `_links`) for returning the links objects.
32
     * It takes effect only, if `collectionEnvelope` is set.
33
     * @since 2.0.4
34
     */
35
    public $linksEnvelope = 'links';
36
    /**
37
     * @var string the name of the envelope (e.g. `_meta`) for returning the pagination object.
38
     * It takes effect only, if `collectionEnvelope` is set.
39
     * @since 2.0.4
40
     */
41
    public $metaEnvelope = 'meta';
42
    /**
43
     * @var Request the current request. If not set, the `request` application component will be used.
44
     */
45
    public $request;
46
    /**
47
     * @var Response the response to be sent. If not set, the `response` application component will be used.
48
     */
49
    public $response;
50
51
52
    /**
53
     * @inheritdoc
54
     */
55
    public function init()
56
    {
57
        if ($this->request === null) {
58
            $this->request = \Yii::$app->getRequest();
59
        }
60
        if ($this->response === null) {
61
            $this->response = \Yii::$app->getResponse();
62
        }
63
    }
64
65
    /**
66
     * Serializes the given data into a format that can be easily turned into other formats.
67
     * This method mainly converts the objects of recognized types into array representation.
68
     * It will not do conversion for unknown object types or non-object data.
69
     * @param mixed $data the data to be serialized.
70
     * @return mixed the converted data.
71
     */
72
    public function serialize($data)
73
    {
74
        if ($data instanceof Model && $data->hasErrors()) {
75
            return $this->serializeModelErrors($data);
76
        } elseif ($data instanceof ResourceInterface) {
77
            return $this->serializeResource($data);
78
        } elseif ($data instanceof DataProviderInterface) {
79
            return $this->serializeDataProvider($data);
80
        } else {
81
            return $data;
82
        }
83
    }
84
85
    /**
86
     * @param ResourceInterface $model
87
     * @return array
88
     */
89
    protected function serializeModel($model)
90
    {
91
        $fields = $this->getRequestedFields();
92
93
        $attributes = isset($fields[$model->getType()]) ? $fields[$model->getType()] : [];
94
        $data = [
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 7 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...
95
            'id' => $model->getId(),
96
            'type' => $model->getType(),
97
            'attributes' => $model->getResourceAttributes($attributes),
98
        ];
99
100
        $relationships = $model->getResourceRelationships();
101
        if (!empty($relationships)) {
102
            foreach ($relationships as $name => $items) {
103
                $relationship = [];
104
                if (is_array($items)) {
105
                    foreach ($items as $item) {
106
                        if ($item instanceof ResourceIdentifierInterface) {
107
                            $relationship[] = ['id' => $item->getId(), 'type' => $item->getType()];
108
                        }
109
                    }
110
                } elseif ($items instanceof ResourceIdentifierInterface) {
111
                    $relationship = ['id' => $items->getId(), 'type' => $items->getType()];
112
                }
113
114
                if (!empty($relationship)) {
115
                    $data['relationships'][$name]['data'] = $relationship;
116
                    if ($model instanceof LinksInterface) {
117
                        $links = $model->getRelationshipLinks($name);
118
                        if (!empty($links)) {
119
                            $data['relationships'][$name]['links'] = Link::serialize($links);
120
                        }
121
                    }
122
                }
123
            }
124
        }
125
126
        if ($model instanceof Linkable) {
127
            $data['links'] = Link::serialize($model->getLinks());
128
        }
129
130
        return $data;
131
    }
132
133
    /**
134
     * @param ResourceInterface $resource
135
     * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be null|array<string,array>?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
136
     */
137
    protected function serializeResource($resource)
138
    {
139
        if ($this->request->getIsHead()) {
140
            return null;
141
        } else {
142
            $data = ['data' => $this->serializeModel($resource)];
143
144
            $included = $this->serializeIncluded($resource);
145
            if (!empty($included)) {
146
                $data['included'] = $included;
147
            }
148
149
            return $data;
150
        }
151
    }
152
153
    /**
154
     * @param ResourceInterface $resource
155
     * @return array
156
     */
157
    protected function serializeIncluded($resource)
158
    {
159
        $included = $this->getIncluded();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 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...
160
        $relationships = $resource->getResourceRelationships();
161
        $data = [];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 10 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...
162
        foreach ($relationships as $name => $relationship) {
163
            if (!in_array($name, $included)) {
164
                continue;
165
            }
166
            if (!is_array($relationship)) {
167
                $relationship = [$relationship];
168
            }
169
            foreach ($relationship as $model) {
170
                if ($model instanceof ResourceInterface) {
171
                    $data[] = $this->serializeModel($model);
172
                }
173
            }
174
        }
175
        return $data;
176
    }
177
178
    /**
179
     * Serializes a data provider.
180
     * @param DataProviderInterface $dataProvider
181
     * @return array the array representation of the data provider.
0 ignored issues
show
Documentation introduced by
Should the return type not be null|array<string,array>?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
182
     */
183
    protected function serializeDataProvider($dataProvider)
184
    {
185
        if ($this->request->getIsHead()) {
186
            return null;
187
        } else {
188
            $models = [];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 9 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...
189
            $includedModels = [];
190
191
            foreach ($dataProvider->getModels() as $model) {
192
                if ($model instanceof ResourceInterface) {
193
                    $models[] = $this->serializeModel($model);
194
195
                    $included = $this->serializeIncluded($model);
196
                    foreach ($included as $document) {
197
                        $includedModels[] = $document;
198
                    }
199
                }
200
            }
201
202
            $result = ['data' => $models];
203
204
            if (!empty($includedModels)) {
205
                $result['included'] = $includedModels;
206
            }
207
208
            if (($pagination = $dataProvider->getPagination()) !== false) {
209
                return array_merge($result, $this->serializePagination($pagination));
210
            }
211
212
            return $result;
213
        }
214
    }
215
216
    /**
217
     * Serializes a pagination into an array.
218
     * @param Pagination $pagination
219
     * @return array the array representation of the pagination
220
     * @see addPaginationHeaders()
221
     */
222
    protected function serializePagination($pagination)
223
    {
224
        return [
225
            $this->linksEnvelope => Link::serialize($pagination->getLinks(true)),
226
            $this->metaEnvelope => [
227
                'total-count' => $pagination->totalCount,
228
                'page-count' => $pagination->getPageCount(),
229
                'current-page' => $pagination->getPage() + 1,
230
                'per-page' => $pagination->getPageSize(),
231
            ],
232
        ];
233
    }
234
235
    /**
236
     * Serializes the validation errors in a model.
237
     * @param Model $model
238
     * @return array the array representation of the errors
239
     */
240
    protected function serializeModelErrors($model)
241
    {
242
        $this->response->setStatusCode(422, 'Data Validation Failed.');
243
        $result = [];
244
        foreach ($model->getFirstErrors() as $name => $message) {
245
            $result[] = [
246
                'source' => ['pointer' => "/data/attributes/{$name}"],
247
                'detail' => $message,
248
            ];
249
        }
250
251
        return $result;
252
    }
253
254
    /**
255
     * @return array
256
     */
257
    protected function getRequestedFields()
258
    {
259
        $fields = $this->request->get($this->fieldsParam);
260
261
        if (!is_array($fields)) {
262
            $fields = [];
263
        }
264
        foreach ($fields as $key => $field) {
265
            $fields[$key] = preg_split('/\s*,\s*/', $fields, -1, PREG_SPLIT_NO_EMPTY);
266
        }
267
        return $fields;
268
    }
269
270
    protected function getIncluded()
271
    {
272
        $include = $this->request->get($this->expandParam);
273
        return is_string($include) ? preg_split('/\s*,\s*/', $include, -1, PREG_SPLIT_NO_EMPTY) : [];
274
    }
275
}
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...
276