Completed
Push — 1.0.0-alpha ( c04b09...2436c2 )
by Alex
11s
created

src/Serializers/ResourceSerializer.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
    public function __construct($record, array $fields = [], array $include = [], array $relationships = [])
46
    {
47
        parent::__construct();
48
49
        $this->record = $record;
50
        $this->fields = array_unique($fields);
51
        $this->include = array_unique($include);
52
53
        $this->relationships = array_unique(
54
            array_merge(
55
                $relationships,
56
                array_keys($record->getRelations()),
57
                array_map(function ($path) {
58
                    return explode('.', $path, 2)[0];
59
                }, $include)
60
            )
61
        );
62
    }
63
64
    /**
65
     * Limit which relations can be included.
66
     *
67
     * @param array $include
68
     */
69
    public function scopeIncludes($include)
70
    {
71
        $this->include = array_intersect($this->include, $include);
72
    }
73
74
    /**
75
     * Return a JSON API resource identifier object for the primary record.
76
     *
77
     * @return array
78
     */
79
    public function toResourceIdentifier()
80
    {
81
        return [
82
            'type' => $this->getResourceType(),
83
            '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
    public function toBaseResourceObject()
94
    {
95
        return array_merge($this->toResourceIdentifier(), [
96
            '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
    public function toResourceObject()
106
    {
107
        return array_filter(array_merge($this->toBaseResourceObject(), [
108
            'relationships' => $this->transformRecordRelations()->toArray(),
109
        ]));
110
    }
111
112
    /**
113
     * Return primary data for the JSON API document.
114
     *
115
     * @return mixed
116
     */
117
    protected function getPrimaryData()
118
    {
119
        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
    public function getIncluded()
130
    {
131
        $included = collect();
132
133
        foreach ($this->getIncludePaths() as $path) {
134
            $resolved = $this->record;
135
136
            if (!($resolved instanceof Collection)) {
137
                $resolved = collect([$resolved])->filter();
138
            }
139
140
            while (!empty($path)) {
141
                list($relation, $path) = array_pad(explode('.', $path, 2), 2, null);
142
                $nextRelation = preg_replace('/\..*/', '', $path);
143
144
                foreach ($resolved as $record) {
145
                    $records = (new RelationshipSerializer($record, $relation, $this->fields, array_filter([$nextRelation])))
0 ignored issues
show
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
                        ->toResourceCollection();
147
148
                    if ($records instanceof Collection) {
149
                        $included = $included->merge($records);
150
                    } else if (!empty($records)) {
151
                        $included->push($records);
152
                    }
153
                }
154
155
                $resolved = $resolved
156
                    ->map(function ($record) use ($relation) {
157
                        return $record->{$relation};
158
                    })
159
                    ->flatten()
160
                    ->filter();
161
            }
162
        }
163
164
        return $this->filterUnique($included);
165
    }
166
167
    /**
168
     * Return the primary resource type name.
169
     */
170
    protected function getResourceType(): string
171
    {
172
        $modelName = collect(explode('\\', get_class($this->record)))->last();
173
174
        if (config('jsonapi.singular_type_names') !== true) {
175
            $modelName = str_plural($modelName);
176
        }
177
178
        return snake_case($modelName, '-');
179
    }
180
181
    /**
182
     * Return the primary key value for the resource.
183
     *
184
     * @return int|string
185
     */
186
    protected function getPrimaryKey()
187
    {
188
        $value = $this->record->getKey();
189
190
        return is_int($value) ? $value : (string) $value;
191
    }
192
193
    /**
194
     * Return the attribute subset requested for the primary resource type.
195
     */
196
    protected function getRequestedFields(): array
197
    {
198
        $fields = array_get($this->fields, $this->getResourceType());
199
200
        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
    protected function getIncludePaths(): array
208
    {
209
        return array_diff(
210
            $this->include,
211
            array_filter(array_map(function ($path) {
212
                preg_match('/(.*)\..+$/', $path, $matches);
213
214
                return $matches[1] ?? null;
215
            }, $this->include))
216
        );
217
    }
218
219
    /**
220
     * Return the attribute object data for the primary record.
221
     */
222
    protected function transformRecordAttributes(): array
223
    {
224
        $attributes = array_except($this->record->attributesToArray(), ['id']);
225
        $fields = $this->getRequestedFields();
226
227
        if (!empty($fields)) {
228
            $attributes = array_only($attributes, $fields);
229
        }
230
231
        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
        return collect($this->relationships)->combine(array_map(function ($relation) {
245
            return [
246
                'data' => (new RelationshipSerializer($this->record, $relation))->toResourceLinkage(),
247
            ];
248
        }, $this->relationships));
249
    }
250
}
251