Passed
Push — testing ( a33802...fee880 )
by Alex
09:56
created

ResourceSerializer   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 244
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 23
c 0
b 0
f 0
lcom 1
cbo 4
dl 0
loc 244
ccs 76
cts 76
cp 1
rs 10

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 1
A scopeIncludes() 0 4 1
A toResourceIdentifier() 0 7 1
A toBaseResourceObject() 0 6 1
A toResourceObject() 0 6 1
A getPrimaryData() 0 4 1
C getIncluded() 0 37 7
A getResourceType() 0 10 2
A getPrimaryKey() 0 6 2
A getRequestedFields() 0 6 2
A getIncludePaths() 0 11 1
A transformRecordAttributes() 0 11 2
A transformRecordRelations() 0 8 1
1
<?php
2
3
namespace Huntie\JsonApi\Serializers;
4
5
use Illuminate\Support\Collection;
6
7
class ResourceSerializer extends JsonApiSerializer
8
{
9
    /**
10
     * The model instance to transform.
11
     *
12
     * @var \Illuminate\Database\Eloquent\Model
13
     */
14
    protected $record;
15
16
    /**
17
     * The subset of attributes to return on each resource type.
18
     *
19
     * @var array
20
     */
21
    protected $fields;
22
23
    /**
24
     * The relationship paths to match for included resources.
25
     *
26
     * @var array
27
     */
28
    protected $include;
29
30
    /**
31
     * The named relationships to list against this resource.
32
     *
33
     * @var array
34
     */
35
    protected $relationships;
36
37
    /**
38
     * Create a new JSON API resource serializer.
39
     *
40
     * @param \Illuminate\Database\Eloquent\Model $record        The model instance to serialise
41
     * @param array|null                          $fields        The subset of fields to return on each resource type
42
     * @param array|null                          $include       The paths of relationships to include
43
     * @param array|null                          $relationships Additional named relationships to list
44
     */
45 22
    public function __construct($record, array $fields = [], array $include = [], array $relationships = [])
46
    {
47 22
        parent::__construct();
48
49 22
        $this->record = $record;
50 22
        $this->fields = array_unique($fields);
51 22
        $this->include = array_unique($include);
52
53 22
        $this->relationships = array_unique(
54 22
            array_merge(
55 22
                $relationships,
56 22
                array_keys($record->getRelations()),
57 22
                array_map(function ($path) {
58 4
                    return explode('.', $path, 2)[0];
59 22
                }, $include)
60
            )
61
        );
62 22
    }
63
64
    /**
65
     * Limit which relations can be included.
66
     *
67
     * @param array $include
68
     */
69 1
    public function scopeIncludes($include)
70
    {
71 1
        $this->include = array_intersect($this->include, $include);
72 1
    }
73
74
    /**
75
     * Return a JSON API resource identifier object for the primary record.
76
     *
77
     * @return array
78
     */
79 22
    public function toResourceIdentifier()
80
    {
81
        return [
82 22
            'type' => $this->getResourceType(),
83 22
            'id' => $this->getPrimaryKey(),
84
        ];
85
    }
86
87
    /**
88
     * Return a base JSON API resource object for the primary record containing
89
     * only immediate attributes.
90
     *
91
     * @return array
92
     */
93 18
    public function toBaseResourceObject()
94
    {
95 18
        return array_merge($this->toResourceIdentifier(), [
96 18
            'attributes' => $this->transformRecordAttributes(),
97
        ]);
98
    }
99
100
    /**
101
     * Return a full JSON API resource object for the primary record.
102
     *
103
     * @return array
104
     */
105 16
    public function toResourceObject()
106
    {
107 16
        return array_filter(array_merge($this->toBaseResourceObject(), [
108 16
            'relationships' => $this->transformRecordRelations()->toArray(),
109
        ]));
110
    }
111
112
    /**
113
     * Return primary data for the JSON API document.
114
     *
115
     * @return mixed
116
     */
117 6
    protected function getPrimaryData()
118
    {
119 6
        return $this->toResourceObject();
120
    }
121
122
    /**
123
     * Return any secondary included resource objects.
124
     *
125
     * @throws \Huntie\JsonApi\Exceptions\InvalidRelationPathException
126
     *
127
     * @return \Illuminate\Support\Collection
128
     */
129 12
    public function getIncluded()
130
    {
131 12
        $included = collect();
132
133 12
        foreach ($this->getIncludePaths() as $path) {
134 3
            $resolved = $this->record;
135
136 3
            if (!($resolved instanceof Collection)) {
137 3
                $resolved = collect([$resolved])->filter();
138
            }
139
140 3
            while (!empty($path)) {
141 3
                list($relation, $path) = array_pad(explode('.', $path, 2), 2, null);
142 3
                $nextRelation = preg_replace('/\..*/', '', $path);
143
144 3
                foreach ($resolved as $record) {
145 3
                    $records = (new RelationshipSerializer($record, $relation, $this->fields, array_filter([$nextRelation])))
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 125 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
146 3
                        ->toResourceCollection();
147
148 3
                    if ($records instanceof Collection) {
149 3
                        $included = $included->merge($records);
150 1
                    } else if (!empty($records)) {
151 3
                        $included->push($records);
152
                    }
153
                }
154
155
                $resolved = $resolved
156 3
                    ->map(function ($record) use ($relation) {
157 3
                        return $record->{$relation};
158 3
                    })
159 3
                    ->flatten()
160 3
                    ->filter();
161
            }
162
        }
163
164 12
        return $this->filterUnique($included);
165
    }
166
167
    /**
168
     * Return the primary resource type name.
169
     */
170 22
    protected function getResourceType(): string
171
    {
172 22
        $modelName = collect(explode('\\', get_class($this->record)))->last();
173
174 22
        if (config('jsonapi.singular_type_names') !== true) {
175 22
            $modelName = str_plural($modelName);
176
        }
177
178 22
        return snake_case($modelName, '-');
179
    }
180
181
    /**
182
     * Return the primary key value for the resource.
183
     *
184
     * @return int|string
185
     */
186 22
    protected function getPrimaryKey()
187
    {
188 22
        $value = $this->record->getKey();
189
190 22
        return is_int($value) ? $value : (string) $value;
191
    }
192
193
    /**
194
     * Return the attribute subset requested for the primary resource type.
195
     */
196 18
    protected function getRequestedFields(): array
197
    {
198 18
        $fields = array_get($this->fields, $this->getResourceType());
199
200 18
        return is_array($fields) ? $fields : preg_split('/,/', $fields, null, PREG_SPLIT_NO_EMPTY);
201
    }
202
203
    /**
204
     * Return the deepest relationship paths that should be used to resolve
205
     * all included relations.
206
     */
207 12
    protected function getIncludePaths(): array
208
    {
209 12
        return array_diff(
210 12
            $this->include,
211 12
            array_filter(array_map(function ($path) {
212 3
                preg_match('/(.*)\..+$/', $path, $matches);
213
214 3
                return $matches[1] ?? null;
215 12
            }, $this->include))
216
        );
217
    }
218
219
    /**
220
     * Return the attribute object data for the primary record.
221
     */
222 18
    protected function transformRecordAttributes(): array
223
    {
224 18
        $attributes = array_except($this->record->attributesToArray(), ['id']);
225 18
        $fields = $this->getRequestedFields();
226
227 18
        if (!empty($fields)) {
228 2
            $attributes = array_only($attributes, $fields);
229
        }
230
231 18
        return $attributes;
232
    }
233
234
    /**
235
     * Return a collection of JSON API resource identifier objects by each
236
     * relation on the primary record.
237
     *
238
     * @throws \Huntie\JsonApi\Exceptions\InvalidRelationPathException
239
     *
240
     * @return \Illuminate\Support\Collection
241
     */
242
    protected function transformRecordRelations()
243
    {
244 16
        return collect($this->relationships)->combine(array_map(function ($relation) {
245
            return [
246 2
                'data' => (new RelationshipSerializer($this->record, $relation))->toResourceLinkage(),
247
            ];
248 16
        }, $this->relationships));
249
    }
250
}
251