diff --git a/src/Serializer/JsonApiSerializer.php b/src/Serializer/JsonApiSerializer.php index 43f76cb8..fc841161 100644 --- a/src/Serializer/JsonApiSerializer.php +++ b/src/Serializer/JsonApiSerializer.php @@ -20,6 +20,11 @@ class JsonApiSerializer extends ArraySerializer protected $baseUrl; protected $rootObjects; + /** + * JsonApiSerializer constructor. + * + * @param string $baseUrl + */ public function __construct($baseUrl = null) { $this->baseUrl = $baseUrl; @@ -30,7 +35,7 @@ public function __construct($baseUrl = null) * Serialize a collection. * * @param string $resourceKey - * @param array $data + * @param array $data * * @return array */ @@ -49,7 +54,7 @@ public function collection($resourceKey, array $data) * Serialize an item. * * @param string $resourceKey - * @param array $data + * @param array $data * * @return array */ @@ -85,13 +90,13 @@ public function item($resourceKey, array $data) */ public function paginator(PaginatorInterface $paginator) { - $currentPage = (int) $paginator->getCurrentPage(); - $lastPage = (int) $paginator->getLastPage(); + $currentPage = (int)$paginator->getCurrentPage(); + $lastPage = (int)$paginator->getLastPage(); $pagination = [ - 'total' => (int) $paginator->getTotal(), - 'count' => (int) $paginator->getCount(), - 'per_page' => (int) $paginator->getPerPage(), + 'total' => (int)$paginator->getTotal(), + 'count' => (int)$paginator->getCount(), + 'per_page' => (int)$paginator->getPerPage(), 'current_page' => $currentPage, 'total_pages' => $lastPage, ]; @@ -151,7 +156,7 @@ public function null() * Serialize the included data. * * @param ResourceInterface $resource - * @param array $data + * @param array $data * * @return array */ @@ -164,12 +169,8 @@ public function includedData(ResourceInterface $resource, array $data) if ($this->isNull($includeObject) || $this->isEmpty($includeObject)) { continue; } - if ($this->isCollection($includeObject)) { - $includeObjects = $includeObject['data']; - } - else { - $includeObjects = [$includeObject['data']]; - } + + $includeObjects = $this->createIncludeObjects($includeObject); foreach ($includeObjects as $object) { $includeType = $object['type']; @@ -215,14 +216,13 @@ public function injectData($data, $includedData) /** * Hook to manipulate the final sideloaded includes. - * * The JSON API specification does not allow the root object to be included * into the sideloaded `included`-array. We have to make sure it is * filtered out, in case some object links to the root object in a * relationship. * - * @param array $includedData - * @param array $data + * @param array $includedData + * @param array $data * * @return array */ @@ -232,12 +232,8 @@ public function filterIncludes($includedData, $data) return $includedData; } - if ($this->isCollection($data)) { - $this->setRootObjects($data['data']); - } - else { - $this->setRootObjects([$data['data']]); - } + // Create the RootObjects + $this->createRootObjects($data); // Filter out the root objects $filteredIncludes = array_filter($includedData['included'], [$this, 'filterRootObject']); @@ -293,7 +289,7 @@ protected function isRootObject($object) protected function isCollection($data) { return array_key_exists('data', $data) && - array_key_exists(0, $data['data']); + array_key_exists(0, $data['data']); } /** @@ -311,7 +307,8 @@ protected function isNull($data) * * @return bool */ - protected function isEmpty($data) { + protected function isEmpty($data) + { return array_key_exists('data', $data) && $data['data'] === []; } @@ -319,29 +316,17 @@ protected function isEmpty($data) { * @param array $data * @param array $relationships * - * @return mixed + * @return array */ protected function fillRelationships($data, $relationships) { if ($this->isCollection($data)) { foreach ($relationships as $key => $relationship) { - foreach ($relationship as $index => $relationshipData) { - $data['data'][$index]['relationships'][$key] = $relationshipData; - } + $data = $this->fillRelationshipAsCollection($data, $relationship, $key); } - } - else { // Single resource + } else { // Single resource foreach ($relationships as $key => $relationship) { - $data['data']['relationships'][$key] = $relationship[0]; - - if ($this->shouldIncludeLinks()) { - $data['data']['relationships'][$key] = array_merge([ - 'links' => [ - 'self' => "{$this->baseUrl}/{$data['data']['type']}/{$data['data']['id']}/relationships/$key", - 'related' => "{$this->baseUrl}/{$data['data']['type']}/{$data['data']['id']}/$key", - ], - ], $data['data']['relationships'][$key]); - } + $data = $this->FillRelationshipAsSingleResource($data, $relationship, $key); } } @@ -358,40 +343,8 @@ protected function parseRelationships($includedData) $relationships = []; foreach ($includedData as $key => $inclusion) { - foreach ($inclusion as $includeKey => $includeObject) - { - if (!array_key_exists($includeKey, $relationships)) { - $relationships[$includeKey] = []; - } - - if ($this->isNull($includeObject)) { - $relationship = $this->null(); - } - elseif ($this->isEmpty($includeObject)) { - $relationship = [ - 'data' => [], - ]; - } - elseif ($this->isCollection($includeObject)) { - $relationship = ['data' => []]; - - foreach ($includeObject['data'] as $object) { - $relationship['data'][] = [ - 'type' => $object['type'], - 'id' => $object['id'], - ]; - } - } - else { - $relationship = [ - 'data' => [ - 'type' => $includeObject['data']['type'], - 'id' => $includeObject['data']['id'], - ], - ]; - } - - $relationships[$includeKey][$key] = $relationship; + foreach ($inclusion as $includeKey => $includeObject) { + $relationships = $this->buildRelationships($includeKey, $relationships, $includeObject, $key); } } @@ -401,7 +354,7 @@ protected function parseRelationships($includedData) /** * @param array $data * - * @return mixed + * @return integer */ protected function getIdFromData(array $data) { @@ -454,4 +407,152 @@ protected function shouldIncludeLinks() { return $this->baseUrl !== null; } + + /** + * Check if the objects are part of a collection or not + * + * @param $includeObject + * + * @return array + */ + private function createIncludeObjects($includeObject) + { + if ($this->isCollection($includeObject)) { + $includeObjects = $includeObject['data']; + + return $includeObjects; + } else { + $includeObjects = [$includeObject['data']]; + + return $includeObjects; + } + } + + /** + * Sets the RootObjects, either as collection or not. + * + * @param $data + */ + private function createRootObjects($data) + { + if ($this->isCollection($data)) { + $this->setRootObjects($data['data']); + } else { + $this->setRootObjects([$data['data']]); + } + } + + + /** + * Loops over the relationships of the provided data and formats it + * + * @param $data + * @param $relationship + * @param $nestedDepth + * + * @return array + */ + private function fillRelationshipAsCollection($data, $relationship, $nestedDepth) + { + foreach ($relationship as $index => $relationshipData) { + $data['data'][$index]['relationships'][$nestedDepth] = $relationshipData; + } + + return $data; + } + + + /** + * @param $data + * @param $relationship + * @param $key + * + * @return array + */ + private function FillRelationshipAsSingleResource($data, $relationship, $key) + { + $data['data']['relationships'][$key] = $relationship[0]; + + if ($this->shouldIncludeLinks()) { + $data['data']['relationships'][$key] = array_merge([ + 'links' => [ + 'self' => "{$this->baseUrl}/{$data['data']['type']}/{$data['data']['id']}/relationships/$key", + 'related' => "{$this->baseUrl}/{$data['data']['type']}/{$data['data']['id']}/$key", + ], + ], $data['data']['relationships'][$key]); + + return $data; + } + return $data; + } + + /** + * @param $includeKey + * @param $relationships + * @param $includeObject + * @param $key + * + * @return array + */ + private function buildRelationships($includeKey, $relationships, $includeObject, $key) + { + $relationships = $this->addIncludekeyToRelationsIfNotSet($includeKey, $relationships); + + if ($this->isNull($includeObject)) { + $relationship = $this->null(); + } elseif ($this->isEmpty($includeObject)) { + $relationship = [ + 'data' => [], + ]; + } elseif ($this->isCollection($includeObject)) { + $relationship = ['data' => []]; + + $relationship = $this->addIncludedDataToRelationship($includeObject, $relationship); + } else { + $relationship = [ + 'data' => [ + 'type' => $includeObject['data']['type'], + 'id' => $includeObject['data']['id'], + ], + ]; + } + + $relationships[$includeKey][$key] = $relationship; + + return $relationships; + } + + /** + * @param $includeKey + * @param $relationships + * + * @return array + */ + private function addIncludekeyToRelationsIfNotSet($includeKey, $relationships) + { + if (!array_key_exists($includeKey, $relationships)) { + $relationships[$includeKey] = []; + return $relationships; + } + + return $relationships; + } + + /** + * @param $includeObject + * @param $relationship + * + * @return array + */ + private function addIncludedDataToRelationship($includeObject, $relationship) + { + foreach ($includeObject['data'] as $object) { + $relationship['data'][] = [ + 'type' => $object['type'], + 'id' => $object['id'], + ]; + } + + return $relationship; + } } diff --git a/test/Serializer/JsonApiSerializerTest.php b/test/Serializer/JsonApiSerializerTest.php index 8050d60e..b759b957 100644 --- a/test/Serializer/JsonApiSerializerTest.php +++ b/test/Serializer/JsonApiSerializerTest.php @@ -5,8 +5,8 @@ use League\Fractal\Resource\Item; use League\Fractal\Scope; use League\Fractal\Serializer\JsonApiSerializer; -use League\Fractal\Test\Stub\Transformer\JsonApiBookTransformer; use League\Fractal\Test\Stub\Transformer\JsonApiAuthorTransformer; +use League\Fractal\Test\Stub\Transformer\JsonApiBookTransformer; class JsonApiSerializerTest extends PHPUnit_Framework_TestCase {