Passed
Push — serialization-refactor ( cf9a88...e80911 )
by Alex
06:22
created

ResourceSerializer::transformRecordRelations()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 14
rs 9.4285
cc 2
eloc 7
nc 2
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 ResourceSerializer extends JsonApiSerializer
9
{
10
    /**
11
     * The model instance to transform.
12
     *
13
     * @var \Illuminate\Database\Eloquent\Model
14
     */
15
    protected $record;
16
17
    /**
18
     * The record 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
     * Create a new JSON API resource serializer.
40
     *
41
     * @param Model      $record  The model instance to serialise
42
     * @param array|null $fields  Subset of fields to return
43
     * @param array|null $include Relations to include
44
     */
45
    public function __construct($record, array $fields = [], array $include = [])
46
    {
47
        parent::__construct();
48
49
        $this->record = $record;
50
        $this->relationships = array_merge($record->getRelations(), $include);
51
        $this->fields = array_unique($fields);
52
        $this->include = array_unique($include);
53
    }
54
55
    /**
56
     * Limit which relations can be included.
57
     *
58
     * @param array $include
59
     */
60
    public function scopeIncludes($include)
61
    {
62
        $this->include = array_intersect($this->include, $include);
63
    }
64
65
    /**
66
     * Return a JSON API resource identifier object for the primary record.
67
     *
68
     * @return array
69
     */
70
    public function toResourceIdentifier()
71
    {
72
        return [
73
            'type' => $this->getRecordType(),
74
            'id' => $this->record->id,
75
        ];
76
    }
77
78
    /**
79
     * Return a base JSON API resource object for the primary record containing
80
     * only immediate attributes.
81
     *
82
     * @return array
83
     */
84
    public function toBaseResourceObject()
85
    {
86
        return array_merge($this->toResourceIdentifier(), [
87
            'attributes' => $this->transformRecordAttributes(),
88
        ]);
89
    }
90
91
    /**
92
     * Return a full JSON API resource object for the primary record.
93
     *
94
     * @return array
95
     */
96
    public function toResourceObject()
97
    {
98
        $this->record->load($this->relationships);
99
100
        return array_merge($this->toBaseResourceObject(), [
101
            'relationships' => $this->transformRecordRelations()->toArray(),
102
        ]);
103
    }
104
105
    /**
106
     * Serialise JSON API document to an array.
107
     *
108
     * @return array
109
     */
110
    public function serializeToObject()
111
    {
112
        return array_filter(array_merge(parent::serializeToObject(), [
113
            'included' => $this->transformIncludedRelations()->toArray(),
114
        ]));
115
    }
116
117
    /**
118
     * Return primary data for the JSON API document.
119
     *
120
     * @return array
121
     */
122
    protected function getPrimaryData()
123
    {
124
        return $this->toResourceObject();
125
    }
126
127
    /**
128
     * Return the primary record type name.
129
     *
130
     * @return string
131
     */
132
    protected function getRecordType()
133
    {
134
        $modelName = collect(explode('\\', get_class($this->record)))->last();
135
136
        return snake_case(str_plural($modelName), '-');
137
    }
138
139
    /**
140
     * Return the attribute object data for the primary record.
141
     *
142
     * @return array
143
     */
144
    protected function transformRecordAttributes()
145
    {
146
        $attributes = array_diff_key($this->record->toArray(), $this->record->getRelations());
147
        $attributes = array_except($attributes, ['id']);
148
149
        if (!empty($this->fields)) {
150
            $attributes = array_only($attributes, $this->fields);
151
        }
152
153
        return $attributes;
154
    }
155
156
    /**
157
     * Return a collection of JSON API resource identifier objects by each
158
     * relation on the primary record.
159
     *
160
     * @return \Illuminate\Support\Collection
161
     */
162
    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...
163
    {
164
        $relationships = collect([]);
165
166
        foreach ($this->relationships as $relation) {
167
            $data = $this->mapRelation($relation, function ($record) {
168
                return (new static($record))->toResourceIdentifier();
169
            });
170
171
            $relationships = $relationships->merge([$relation => compact('data')]);
172
        }
173
174
        return $relationships;
175
    }
176
177
    /**
178
     * Return a collection of JSON API resource objects for each included
179
     * relationship.
180
     *
181
     * @return \Illuminate\Support\Collection
182
     */
183
    protected function transformIncludedRelations()
0 ignored issues
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...
184
    {
185
        $included = collect([]);
186
187
        foreach ($this->include as $relation) {
188
            $records = $this->mapRelation($relation, function ($record) {
189
                return (new static($record))->toBaseResourceObject();
190
            });
191
192
            $included = $included->merge(collect($records));
193
        }
194
195
        return $included;
196
    }
197
198
    /**
199
     * Run a map over each item in a loaded relation on the primary record.
200
     *
201
     * @param string   $relation
202
     * @param \Closure $callback
203
     *
204
     * @return Collection|Model|null
205
     */
206
    protected function mapRelation($relation, $callback)
207
    {
208
        $loadedRelation = $this->record->{$relation};
209
210
        if ($loadedRelation instanceof Collection) {
211
            return $loadedRelation->map($callback);
212
        } else if ($loadedRelation instanceof Model) {
213
            return $callback($loadedRelation);
214
        }
215
216
        return null;
217
    }
218
}
219