Passed
Push — serialization-refactor ( cf9a88 )
by Alex
03:31
created

ModelSerializer::toBaseResourceObject()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 1
eloc 3
nc 1
nop 0
1
<?php
2
3
namespace Huntie\JsonApi\Serializers;
4
5
use Illuminate\Database\Eloquent\Model;
6
use Illuminate\Support\Collection;
7
8
class ModelSerializer
9
{
10
    /**
11
     * The model instance to transform.
12
     *
13
     * @var \Illuminate\Database\Eloquent\Model
14
     */
15
    protected $record;
16
17
    /**
18
     * The relationships to return.
19
     *
20
     * @var array
21
     */
22
    protected $relationships;
23
24
    /**
25
     * The subset of record attributes to return.
26
     *
27
     * @var array
28
     */
29
    protected $fields;
30
31
    /**
32
     * The relationships to load and include.
33
     *
34
     * @var array
35
     */
36
    protected $include;
37
38
    /**
39
     * Meta information to include.
40
     *
41
     * @var \Illuminate\Support\Collection
42
     */
43
    protected $meta;
44
45
    /**
46
     * Resource links to include.
47
     *
48
     * @var \Illuminate\Support\Collection
49
     */
50
    protected $links;
51
52
    /**
53
     * Create a new JSON API resource serializer.
54
     *
55
     * @param Model      $record  The model instance to serialise
56
     * @param array|null $fields  Subset of fields to return
57
     * @param array|null $include Relations to include
58
     */
59
    public function __construct($record, array $fields = [], array $include = [])
60
    {
61
        $this->record = $record;
62
        $this->relationships = array_merge($record->getRelations(), $include);
63
        $this->fields = array_unique($fields);
64
        $this->include = array_unique($include);
65
        $this->meta = collect([]);
66
        $this->links = collect([]);
67
    }
68
69
    /**
70
     * Limit which relations can be included.
71
     *
72
     * @param array $include
73
     */
74
    public function scopeIncludes($include)
75
    {
76
        $this->include = array_intersect($this->include, $include);
77
    }
78
79
    /**
80
     * Add meta information to the returned object.
81
     *
82
     * @param string|array    $key
83
     * @param string|int|null $value
84
     */
85
    public function addMeta($key, $value = null)
86
    {
87
        $this->meta = $this->meta->merge(is_array($key) ? $key : [$key => $value]);
88
    }
89
90
    /**
91
     * Add one or more links to the returned object.
92
     *
93
     * @param string|array    $key
94
     * @param string|int|null $value
95
     */
96
    public function addLinks($key, $value = null)
97
    {
98
        $this->links = $this->links->merge(is_array($key) ? $key : [$key => $value]);
99
    }
100
101
    /**
102
     * Return a JSON API resource identifier object for the primary record.
103
     *
104
     * @return array
105
     */
106
    public function toResourceIdentifier()
107
    {
108
        return [
109
            'type' => $this->getRecordType(),
110
            'id' => $this->record->id,
111
        ];
112
    }
113
114
    /**
115
     * Return a base JSON API resource object for the primary record containing
116
     * only immediate attributes.
117
     *
118
     * @return array
119
     */
120
    public function toBaseResourceObject()
121
    {
122
        return array_merge($this->toResourceIdentifier(), [
123
            'attributes' => $this->transformRecordAttributes(),
124
        ]);
125
    }
126
127
    /**
128
     * Return a full JSON API resource object for the primary record.
129
     *
130
     * @return array
131
     */
132
    public function toResourceObject()
133
    {
134
        $this->record->load($this->relationships);
135
136
        return array_merge($this->toBaseResourceObject(), [
137
            'relationships' => $this->transformRecordRelations()->toArray(),
138
        ]);
139
    }
140
141
    /**
142
     * Serialise complete JSON API document to an array.
143
     *
144
     * @return array
145
     */
146
    public function serialiseToObject()
147
    {
148
        return array_filter([
149
            'data' => $this->toResourceObject(),
150
            'included' => $this->transformIncludedRelations()->toArray(),
151
            'links' => $this->links->toArray(),
152
            'meta' => $this->meta->toArray(),
153
        ]);
154
    }
155
156
    /**
157
     * Serialise complete JSON API document to a JSON string.
158
     *
159
     * @return array
160
     */
161
    public function serializeToJson()
162
    {
163
        return json_encode($this->serialiseToObject());
164
    }
165
166
    /**
167
     * Return the primary record type name.
168
     *
169
     * @return string
170
     */
171
    protected function getRecordType()
172
    {
173
        $modelName = collect(explode('\\', get_class($this->record)))->last();
174
175
        return snake_case(str_plural($modelName), '-');
176
    }
177
178
    /**
179
     * Return the attribute object data for the primary record.
180
     *
181
     * @return array
182
     */
183
    protected function transformRecordAttributes()
184
    {
185
        $attributes = array_diff_key($this->record->toArray(), $this->record->getRelations());
186
        $attributes = array_except($attributes, ['id']);
187
188
        if (!empty($this->fields)) {
189
            $attributes = array_only($attributes, $this->fields);
190
        }
191
192
        return $attributes;
193
    }
194
195
    /**
196
     * Return a collection of JSON API resource identifier objects by each
197
     * relation on the primary record.
198
     *
199
     * @return \Illuminate\Support\Collection
200
     */
201 View Code Duplication
    protected function transformRecordRelations()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
202
    {
203
        $relationships = collect([]);
204
205
        foreach ($this->relationships as $relation) {
206
            $data = $this->mapRelation($relation, function ($record) {
207
                return (new static($record))->toResourceIdentifier();
208
            });
209
210
            $relationships = $relationships->merge([$relation => compact('data')]);
211
        }
212
213
        return $relationships;
214
    }
215
216
    /**
217
     * Return a collection of JSON API resource objects for each included
218
     * relationship.
219
     *
220
     * @return \Illuminate\Support\Collection
221
     */
222 View Code Duplication
    protected function transformIncludedRelations()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
223
    {
224
        $included = collect([]);
225
226
        foreach ($this->include as $relation) {
227
            $records = $this->mapRelation($relation, function ($record) {
228
                return (new static($record))->toBaseResourceObject();
229
            });
230
231
            $included = $included->merge(collect($records));
232
        }
233
234
        return $included;
235
    }
236
237
    /**
238
     * Run a map over each item in a loaded relation on the primary record.
239
     *
240
     * @param string   $relation
241
     * @param \Closure $callback
242
     *
243
     * @return Collection|Model|null
244
     */
245
    protected function mapRelation($relation, $callback)
246
    {
247
        $loadedRelation = $this->record->{$relation};
248
249
        if ($loadedRelation instanceof Collection) {
250
            return $loadedRelation->map($callback);
251
        } else if ($loadedRelation instanceof Model) {
252
            return $callback($loadedRelation);
253
        }
254
255
        return null;
256
    }
257
}
258