Skip to content

Commit ca2f85f

Browse files
committed
improve inclusion of related resources
1 parent f2bee3d commit ca2f85f

File tree

6 files changed

+199
-58
lines changed

6 files changed

+199
-58
lines changed

src/Inflector.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public static function var2member($var)
3030
*/
3131
public static function member2var($member)
3232
{
33-
return str_replace(' ', '_', preg_replace('/[^A-Za-z0-9]+/', ' ', $member));
33+
return str_replace(' ', '_', preg_replace('/[^A-Za-z0-9\.]+/', ' ', $member));
3434
}
3535

3636
/**

src/ResourceInterface.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ public function getResourceAttributes(array $fields = []);
1919

2020
/**
2121
* The "relationships" member of the resource object describing relationships between the resource and other JSON API resources.
22+
* @param null|array $linked specific resource linkage that a client has requested.
2223
* @return ResourceIdentifierInterface[] represent references from the resource object in which it’s defined to other resource objects.
2324
*/
24-
public function getResourceRelationships();
25+
public function getResourceRelationships(array $linked = null);
2526

2627
public function setResourceRelationship($name, $relationship);
2728

28-
}
29+
}

src/ResourceTrait.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,19 +51,28 @@ public function getResourceAttributes(array $fields = [])
5151
}
5252

5353
/**
54+
* @param null|array $linked
5455
* @return array
5556
*/
56-
public function getResourceRelationships()
57+
public function getResourceRelationships(array $linked = null)
5758
{
58-
$relationships = [];
5959
$fields = [];
6060
if ($this instanceof Arrayable) {
6161
$fields = $this->extraFields();
6262
}
63+
$resolvedFields = $this->resolveFields($fields);
64+
$keys = array_keys($resolvedFields);
65+
if ($linked === null) {
66+
$linked = $keys;
67+
}
68+
$relationships = array_fill_keys($keys, null);
69+
$linkedFields = array_intersect($keys, $linked);
6370

64-
foreach ($this->resolveFields($fields) as $name => $definition) {
71+
foreach ($linkedFields as $name) {
72+
$definition = $resolvedFields[$name];
6573
$relationships[$name] = is_string($definition) ? $this->$definition : call_user_func($definition, $this, $name);
6674
}
75+
6776
return $relationships;
6877
}
6978

src/Serializer.php

Lines changed: 65 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public function serialize($data)
105105
* @param ResourceInterface $model
106106
* @return array
107107
*/
108-
protected function serializeModel(ResourceInterface $model)
108+
protected function serializeModel(ResourceInterface $model, array $included = null)
109109
{
110110
$fields = $this->getRequestedFields();
111111
$type = $this->pluralize ? Inflector::pluralize($model->getType()) : $model->getType();
@@ -118,8 +118,7 @@ protected function serializeModel(ResourceInterface $model)
118118
'attributes' => $attributes,
119119
]);
120120

121-
$included = $this->getIncluded();
122-
$relationships = $model->getResourceRelationships();
121+
$relationships = $model->getResourceRelationships($included);
123122
if (!empty($relationships)) {
124123
foreach ($relationships as $name => $items) {
125124
$relationship = [];
@@ -132,17 +131,15 @@ protected function serializeModel(ResourceInterface $model)
132131
} elseif ($items instanceof ResourceIdentifierInterface) {
133132
$relationship = $this->serializeIdentifier($items);
134133
}
134+
$memberName = $this->prepareMemberNames([$name]);
135+
$memberName = reset($memberName);
135136
if (!empty($relationship)) {
136-
$memberName = $this->prepareMemberNames([$name]);
137-
$memberName = reset($memberName);
138-
if (in_array($name, $included)) {
139-
$data['relationships'][$memberName]['data'] = $relationship;
140-
}
141-
if ($model instanceof LinksInterface) {
142-
$links = $model->getRelationshipLinks($memberName);
143-
if (!empty($links)) {
144-
$data['relationships'][$memberName]['links'] = Link::serialize($links);
145-
}
137+
$data['relationships'][$memberName]['data'] = $relationship;
138+
}
139+
if ($model instanceof LinksInterface) {
140+
$links = $model->getRelationshipLinks($memberName);
141+
if (!empty($links)) {
142+
$data['relationships'][$memberName]['links'] = Link::serialize($links);
146143
}
147144
}
148145
}
@@ -164,11 +161,24 @@ protected function serializeResource(ResourceInterface $resource)
164161
if ($this->request->getIsHead()) {
165162
return null;
166163
} else {
167-
$data = ['data' => $this->serializeModel($resource)];
164+
$included = $topLevel = $this->getIncluded();
165+
166+
if ($included !== null) {
167+
$topLevel = array_map(function($item) {
168+
if (($pos = strrpos($item, '.')) !== false) {
169+
return substr($item, 0, $pos);
170+
}
171+
return $item;
172+
}, $included);
173+
}
174+
175+
$data = [
176+
'data' => $this->serializeModel($resource, $topLevel)
177+
];
168178

169-
$included = $this->serializeIncluded($resource);
170-
if (!empty($included)) {
171-
$data['included'] = $included;
179+
$relatedResources = $this->serializeIncluded($resource, $included);
180+
if (!empty($relatedResources)) {
181+
$data['included'] = $relatedResources;
172182
}
173183

174184
return $data;
@@ -200,21 +210,39 @@ protected function serializeIdentifier(ResourceIdentifierInterface $identifier)
200210

201211
/**
202212
* @param ResourceInterface|array $resources
213+
* @param null|array $included
203214
* @return array
204215
*/
205-
protected function serializeIncluded($resources)
216+
protected function serializeIncluded($resources, array $included = null)
206217
{
207-
$included = $this->getIncluded();
208218
$resources = is_array($resources) ? $resources : [$resources];
209219
$data = [];
210220

221+
if ($included === null) {
222+
return [];
223+
}
224+
225+
$inclusion = [];
226+
$linked = [];
227+
foreach ($included as $path) {
228+
if (($pos = strrpos($path, '.')) === false) {
229+
$linked[] = $path;
230+
$inclusion[$path] = [];
231+
continue;
232+
}
233+
$name = substr($path, $pos + 1);
234+
$key = substr($path, 0, $pos);
235+
$inclusion[$key][] = $name;
236+
$linked[] = $key;
237+
}
238+
211239
foreach ($resources as $resource) {
212240
if (!$resource instanceof ResourceInterface) {
213241
continue;
214242
}
215-
$relationships = $resource->getResourceRelationships();
243+
$relationships = $resource->getResourceRelationships($linked);
216244
foreach ($relationships as $name => $relationship) {
217-
if (!in_array($name, $included)) {
245+
if ($relationship === null) {
218246
continue;
219247
}
220248
if (!is_array($relationship)) {
@@ -223,7 +251,10 @@ protected function serializeIncluded($resources)
223251
foreach ($relationship as $model) {
224252
if ($model instanceof ResourceInterface) {
225253
$uniqueKey = $model->getType() . '/' . $model->getId();
226-
$data[$uniqueKey] = $this->serializeModel($model);
254+
$data[$uniqueKey] = $this->serializeModel($model, $inclusion[$name]);
255+
if (!empty($inclusion[$name])) {
256+
$data = array_merge($data, $this->serializeIncluded($model, $inclusion[$name]));
257+
}
227258
}
228259
}
229260
}
@@ -235,7 +266,7 @@ protected function serializeIncluded($resources)
235266
/**
236267
* Serializes a data provider.
237268
* @param DataProviderInterface $dataProvider
238-
* @return array the array representation of the data provider.
269+
* @return null|array the array representation of the data provider.
239270
*/
240271
protected function serializeDataProvider($dataProvider)
241272
{
@@ -245,17 +276,18 @@ protected function serializeDataProvider($dataProvider)
245276
$models = $dataProvider->getModels();
246277
$data = [];
247278

279+
$included = $this->getIncluded();
248280
foreach ($models as $model) {
249281
if ($model instanceof ResourceInterface) {
250-
$data[] = $this->serializeModel($model);
282+
$data[] = $this->serializeModel($model, $included);
251283
}
252284
}
253285

254286
$result = ['data' => $data];
255287

256-
$included = $this->serializeIncluded($models);
257-
if (!empty($included)) {
258-
$result['included'] = $included;
288+
$relatedResources = $this->serializeIncluded($models, $included);
289+
if (!empty($relatedResources)) {
290+
$result['included'] = $relatedResources;
259291
}
260292

261293
if (($pagination = $dataProvider->getPagination()) !== false) {
@@ -321,9 +353,15 @@ protected function getRequestedFields()
321353
return $fields;
322354
}
323355

356+
/**
357+
* @return array|null
358+
*/
324359
protected function getIncluded()
325360
{
326361
$include = $this->request->get($this->expandParam);
362+
if (!$this->request->isGet) {
363+
return null;
364+
}
327365
return is_string($include) ? array_map($this->formatMemberName, preg_split('/\s*,\s*/', $include, -1, PREG_SPLIT_NO_EMPTY)) : [];
328366
}
329367

0 commit comments

Comments
 (0)