Skip to content

Commit f2c6772

Browse files
authored
Merge pull request #84 from HennerM/master
Support to-many relations in included resources.
2 parents 89204a8 + 2f97f49 commit f2c6772

File tree

6 files changed

+125
-41
lines changed

6 files changed

+125
-41
lines changed

src/Helpers/DataIncludedHelper.php

+53-12
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ protected static function addToRelationshipsArray(
9494
foreach ($value as $propertyName => $attribute) {
9595
if (PropertyHelper::isAttributeProperty($mappings, $propertyName, $type)) {
9696
$propertyName = DataAttributesHelper::transformToValidMemberName($propertyName);
97+
if (\array_key_exists(Serializer::MAP_TYPE, $attribute)
98+
&& count(array_values($attribute[Serializer::SCALAR_VALUE])) > 0
99+
&& \array_key_exists(Serializer::CLASS_IDENTIFIER_KEY, array_values($attribute[Serializer::SCALAR_VALUE])[0])) {
100+
self::setResponseDataIncluded($mappings, $value, $data);
101+
continue;
102+
}
97103

98104
if (\array_key_exists(Serializer::CLASS_IDENTIFIER_KEY, $attribute)) {
99105
self::setResponseDataIncluded($mappings, $value, $data);
@@ -155,17 +161,43 @@ protected static function addToIncludedArray(array &$mappings, array &$data, arr
155161
$arrayData[JsonApiTransformer::RELATIONSHIPS_KEY] = $relationships;
156162
}
157163

158-
$data[JsonApiTransformer::INCLUDED_KEY][] = \array_filter($arrayData);
164+
$existingIndex = false;
165+
if (array_key_exists(JsonApiTransformer::INCLUDED_KEY, $data)) {
166+
$existingIndex = self::findIncludedIndex($data[JsonApiTransformer::INCLUDED_KEY], $arrayData[JsonApiTransformer::ID_KEY], $arrayData[JsonApiTransformer::TYPE_KEY]);
167+
}
168+
if ($existingIndex !== false) {
169+
$data[JsonApiTransformer::INCLUDED_KEY][$existingIndex] = \array_filter(\array_merge($data[JsonApiTransformer::INCLUDED_KEY][$existingIndex],
170+
\array_filter($arrayData, self::filterEmptyArray())), self::filterEmptyArray());
171+
} else {
172+
$data[JsonApiTransformer::INCLUDED_KEY][] = \array_filter($arrayData, self::filterEmptyArray());
173+
}
159174
}
160175
}
161-
162176
if (!empty($data[JsonApiTransformer::INCLUDED_KEY])) {
163177
$data[JsonApiTransformer::INCLUDED_KEY] = \array_values(
164178
\array_unique($data[JsonApiTransformer::INCLUDED_KEY], SORT_REGULAR)
165179
);
166180
}
167181
}
168182

183+
protected static function filterEmptyArray()
184+
{
185+
return function($value) {
186+
return $value !== null && (!is_array($value) || count($value) > 0);
187+
};
188+
}
189+
190+
protected static function findIncludedIndex($includedData, $idNeedle, $typeNeedle)
191+
{
192+
foreach ($includedData as $key => $value) {
193+
if ($value[JsonApiTransformer::ID_KEY] === $idNeedle && $value[JsonApiTransformer::TYPE_KEY] === $typeNeedle) {
194+
return $key;
195+
}
196+
}
197+
198+
return false;
199+
}
200+
169201
/**
170202
* @param array $includedData
171203
*
@@ -201,34 +233,43 @@ protected static function addRelationshipsToIncludedResources(
201233

202234
continue;
203235
}
236+
237+
if (\is_array($attribute) && \array_key_exists(Serializer::MAP_TYPE, $attribute)) {
238+
$relations = [];
239+
$elements = $attribute[Serializer::SCALAR_VALUE];
240+
foreach ($elements as $arrayValue) {
241+
if (\array_key_exists(Serializer::CLASS_IDENTIFIER_KEY, $arrayValue)) {
242+
$relations[] = PropertyHelper::setResponseDataTypeAndId($mappings, $arrayValue);
243+
}
244+
}
245+
if (count($relations) > 0) {
246+
$data[DataLinksHelper::camelCaseToUnderscore($propertyName)][JsonApiTransformer::DATA_KEY] = $relations;
247+
}
248+
continue;
249+
}
204250
}
205251
}
206252
}
207253

208254
/**
209255
* Enforce with this check that each property leads to a data element.
210256
*
211-
* @param array $value
212257
* @param array $arrayData
213258
*
214259
* @return array
215260
*/
216261
protected static function normalizeRelationshipData(array &$value, array $arrayData)
217262
{
218263
$relationships = [];
219-
foreach ($arrayData[JsonApiTransformer::RELATIONSHIPS_KEY] as $attribute => $value) {
264+
foreach ($arrayData[JsonApiTransformer::RELATIONSHIPS_KEY] as $attribute => $attributeValue) {
220265
//if $value[data] is not found, get next level where [data] should exist.
221-
if (!array_key_exists(JsonApiTransformer::DATA_KEY, $value)) {
222-
array_shift($value);
266+
if (!array_key_exists(JsonApiTransformer::DATA_KEY, $attributeValue)) {
267+
array_shift($attributeValue);
223268
}
224269

225-
//If one value in $value[data], remove the array and make it object only.
226-
if (1 === count($value[JsonApiTransformer::DATA_KEY])) {
227-
$value = reset($value[JsonApiTransformer::DATA_KEY]);
228-
}
229270

230-
if (count($value[JsonApiTransformer::DATA_KEY]) > 0) {
231-
$relationships[$attribute] = $value;
271+
if (count($attributeValue[JsonApiTransformer::DATA_KEY]) > 0) {
272+
$relationships[$attribute] = $attributeValue;
232273
}
233274
}
234275

src/Helpers/DataLinksHelper.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ protected static function toUnderScore($original, $idValues, $url)
413413
*
414414
* @return string
415415
*/
416-
protected static function camelCaseToUnderscore($camel, $splitter = '_')
416+
public static function camelCaseToUnderscore($camel, $splitter = '_')
417417
{
418418
$camel = \preg_replace(
419419
'/(?!^)[[:upper:]][[:lower:]]/',

tests/Behaviour/Dummy/ComplexObject/Comment.php

+6-1
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,24 @@ class Comment
3434
*/
3535
private $oneDate;
3636

37+
/**
38+
* @var User[]
39+
*/
40+
private $likes;
3741
/**
3842
* @param CommentId $id
3943
* @param $comment
4044
* @param User $user
4145
* @param array $dates
4246
*/
43-
public function __construct(CommentId $id, $comment, User $user, array $dates, \DateTime $d = null)
47+
public function __construct(CommentId $id, $comment, User $user, array $dates, \DateTime $d = null, $likes = [])
4448
{
4549
$this->commentId = $id;
4650
$this->comment = $comment;
4751
$this->user = $user;
4852
$this->dates = $dates;
4953
$this->oneDate = $d;
54+
$this->likes = $likes;
5055
}
5156

5257
/**

tests/Behaviour/HelperFactory.php

+11-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,17 @@ public static function complexPost()
4646
'created_at' => (new DateTime('2015-07-18T12:13:00+00:00'))->format('c'),
4747
'accepted_at' => (new DateTime('2015-07-19T00:00:00+00:00'))->format('c'),
4848
],
49-
new DateTime('2015-07-18T12:13:00+00:00')
49+
new DateTime('2015-07-18T12:13:00+00:00'),
50+
[
51+
new User(
52+
new UserId(3),
53+
'First Liker'
54+
),
55+
new User(
56+
new UserId(4),
57+
'Second Liker'
58+
)
59+
]
5060
),
5161
]
5262
);

tests/Behaviour/JsonApiTransformerTest.php

+48
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,42 @@ public function testItWillSerializeToJsonApiAComplexObject()
135135
}
136136
}
137137
},
138+
{
139+
"type":"user",
140+
"id":"3",
141+
"attributes":{
142+
"name":"First Liker"
143+
},
144+
"links":{
145+
"self":{
146+
"href":"http://example.com/users/3"
147+
},
148+
"friends":{
149+
"href":"http://example.com/users/3/friends"
150+
},
151+
"comments":{
152+
"href":"http://example.com/users/3/comments"
153+
}
154+
}
155+
},
156+
{
157+
"type":"user",
158+
"id":"4",
159+
"attributes":{
160+
"name":"Second Liker"
161+
},
162+
"links":{
163+
"self":{
164+
"href":"http://example.com/users/4"
165+
},
166+
"friends":{
167+
"href":"http://example.com/users/4/friends"
168+
},
169+
"comments":{
170+
"href":"http://example.com/users/4/comments"
171+
}
172+
}
173+
},
138174
{
139175
"type":"user",
140176
"id":"2",
@@ -174,6 +210,18 @@ public function testItWillSerializeToJsonApiAComplexObject()
174210
"type":"user",
175211
"id":"2"
176212
}
213+
},
214+
"likes": {
215+
"data": [
216+
{
217+
"type": "user",
218+
"id":"3"
219+
},
220+
{
221+
"type": "user",
222+
"id":"4"
223+
}
224+
]
177225
}
178226
},
179227
"links":{

tests/Integrations/Doctrine/DoctrineTest.php

+6-26
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,12 @@ public function testSecondLevelNestingEntitySerialize()
191191
"type":"post",
192192
"id":"1",
193193
"attributes":{
194-
"description":"Description test"
194+
"description":"Description test",
195+
"date":{
196+
"date":"2016-07-12 16:30:12.000000",
197+
"timezone_type":3,
198+
"timezone":"Europe/Madrid"
199+
}
195200
},
196201
"relationships":{
197202
"customer":{
@@ -228,31 +233,6 @@ public function testSecondLevelNestingEntitySerialize()
228233
"href":"http://example.com/comment/1"
229234
}
230235
}
231-
},
232-
{
233-
"type":"post",
234-
"id":"1",
235-
"attributes":{
236-
"date":{
237-
"date":"2016-07-12 16:30:12.000000",
238-
"timezone_type":3,
239-
"timezone":"Europe/Madrid"
240-
},
241-
"description":"Description test"
242-
},
243-
"relationships":{
244-
"customer":{
245-
"data":{
246-
"type":"customer",
247-
"id":"1"
248-
}
249-
}
250-
},
251-
"links":{
252-
"self":{
253-
"href":"http://example.com/post/1"
254-
}
255-
}
256236
}
257237
],
258238
"links":{

0 commit comments

Comments
 (0)