-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for nested wrapped resources
- Loading branch information
1 parent
bbd037a
commit 4fecfb9
Showing
9 changed files
with
376 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
|
||
namespace AgilePixels\ResourceAbilities; | ||
|
||
class AnonymousResourceCollection extends ResourceCollection | ||
{ | ||
/** | ||
* Create a new anonymous resource collection. | ||
* | ||
* @param mixed $resource | ||
* @param string $collects | ||
* @return void | ||
*/ | ||
public function __construct($resource, public $collects) | ||
{ | ||
parent::__construct($resource); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
<?php | ||
|
||
namespace AgilePixels\ResourceAbilities; | ||
|
||
use Illuminate\Http\JsonResponse; | ||
use Illuminate\Http\Request; | ||
use Illuminate\Support\Arr; | ||
|
||
class PaginatedResourceResponse extends ResourceResponse | ||
{ | ||
/** | ||
* Create an HTTP response that represents the object. We make sure the returned array from the resolve() method | ||
* isn't wrapped again since we moved the wrapping logic to the resource instead of this response class. | ||
* | ||
* @param Request $request | ||
* @return JsonResponse | ||
*/ | ||
public function toResponse($request): JsonResponse | ||
{ | ||
return tap(response()->json( | ||
array_merge_recursive( | ||
$this->resource->resolve($request), | ||
$this->paginationInformation($request), | ||
), | ||
$this->calculateStatus() | ||
), function ($response) use ($request) { | ||
$response->original = $this->resource->resource->map(function ($item) { | ||
return is_array($item) ? Arr::get($item, 'resource') : $item->resource; | ||
}); | ||
|
||
$this->resource->withResponse($request, $response); | ||
}); | ||
} | ||
|
||
/** | ||
* Add the pagination information to the response. | ||
* | ||
* @param Request $request | ||
* @return array | ||
*/ | ||
protected function paginationInformation(Request $request): array | ||
{ | ||
$paginated = $this->resource->resource->toArray(); | ||
|
||
return [ | ||
'links' => $this->paginationLinks($paginated), | ||
'meta' => $this->meta($paginated), | ||
]; | ||
} | ||
|
||
/** | ||
* Get the pagination links for the response. | ||
* | ||
* @param array $paginated | ||
* @return array | ||
*/ | ||
protected function paginationLinks(array $paginated): array | ||
{ | ||
return [ | ||
'first' => $paginated['first_page_url'] ?? null, | ||
'last' => $paginated['last_page_url'] ?? null, | ||
'prev' => $paginated['prev_page_url'] ?? null, | ||
'next' => $paginated['next_page_url'] ?? null, | ||
]; | ||
} | ||
|
||
/** | ||
* Gather the meta data for the response. | ||
* | ||
* @param array $paginated | ||
* @return array | ||
*/ | ||
protected function meta(array $paginated): array | ||
{ | ||
return Arr::except($paginated, [ | ||
'data', | ||
'first_page_url', | ||
'last_page_url', | ||
'prev_page_url', | ||
'next_page_url', | ||
]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
<?php | ||
|
||
namespace AgilePixels\ResourceAbilities; | ||
|
||
use Illuminate\Container\Container; | ||
use Illuminate\Contracts\Support\Arrayable; | ||
use Illuminate\Http\JsonResponse; | ||
use Illuminate\Http\Request; | ||
use Illuminate\Pagination\AbstractPaginator; | ||
use Illuminate\Support\Collection; | ||
use Illuminate\Http\Resources\Json\ResourceCollection as BaseResourceCollection; | ||
use JsonSerializable; | ||
|
||
class ResourceCollection extends BaseResourceCollection | ||
{ | ||
/** | ||
* Wrap the given data if necessary. This is the same wrapping that Laravel normally does in the | ||
* ResourceResponse class. However, we make sure the wrapping is done in here so that nested resource | ||
* collections are also wrapped instead of only the first JsonResource using the toResponse method. | ||
* | ||
* @param array $data | ||
* @param array $with | ||
* @param array $additional | ||
* @return array | ||
*/ | ||
protected function wrapData(array $data, array $with = [], array $additional = []): array | ||
{ | ||
if ($data instanceof Collection) { | ||
$data = $data->all(); | ||
} | ||
|
||
if ($this->haveDefaultWrapperAndDataIsUnwrapped($data)) { | ||
$data = [$this->wrapper() => $data]; | ||
} elseif ($this->haveAdditionalInformationAndDataIsUnwrapped($data, $with, $additional)) { | ||
$data = [($this->wrapper() ?? 'data') => $data]; | ||
} | ||
|
||
return array_merge_recursive($data, $with, $additional); | ||
} | ||
|
||
/** | ||
* Determine if we have a default wrapper and the given data is unwrapped. | ||
* | ||
* @param array $data | ||
* @return bool | ||
*/ | ||
protected function haveDefaultWrapperAndDataIsUnwrapped(array $data): bool | ||
{ | ||
return $this->wrapper() && ! array_key_exists($this->wrapper(), $data); | ||
} | ||
|
||
/** | ||
* Determine if "with" data has been added and our data is unwrapped. | ||
* | ||
* @param array $data | ||
* @param array $with | ||
* @param array $additional | ||
* @return bool | ||
*/ | ||
protected function haveAdditionalInformationAndDataIsUnwrapped(array $data, array $with, array $additional): bool | ||
{ | ||
return (! empty($with) || ! empty($additional)) && | ||
(! $this->wrapper() || | ||
! array_key_exists($this->wrapper(), $data)); | ||
} | ||
|
||
/** | ||
* Get the default data wrapper for the resource. | ||
* | ||
* @return string | ||
*/ | ||
protected function wrapper(): string | ||
{ | ||
return static::$wrap; | ||
} | ||
|
||
/** | ||
* Resolve the resource to an array. | ||
* | ||
* @param Request|null $request | ||
* @return array | ||
*/ | ||
public function resolve($request = null) | ||
{ | ||
$data = static::toArray( | ||
$request = $request ?: Container::getInstance()->make('request') | ||
); | ||
|
||
if ($data instanceof Arrayable) { | ||
$data = $data->toArray(); | ||
} elseif ($data instanceof JsonSerializable) { | ||
$data = $data->jsonSerialize(); | ||
} | ||
|
||
$data = $this->filter((array) $data); | ||
|
||
return $this->wrapData( | ||
$data, | ||
$this->with($request), | ||
$this->additional | ||
); | ||
} | ||
|
||
/** | ||
* Create an HTTP response that represents the object. | ||
* | ||
* @param Request $request | ||
* @return JsonResponse | ||
*/ | ||
public function toResponse($request): JsonResponse | ||
{ | ||
if ($this->resource instanceof AbstractPaginator) { | ||
return $this->preparePaginatedResponse($request); | ||
} | ||
|
||
return (new ResourceResponse($this))->toResponse($request); | ||
} | ||
|
||
/** | ||
* Create a paginate-aware HTTP response. | ||
* | ||
* @param Request $request | ||
* @return JsonResponse | ||
*/ | ||
protected function preparePaginatedResponse($request): JsonResponse | ||
{ | ||
if ($this->preserveAllQueryParameters) { | ||
$this->resource->appends($request->query()); | ||
} elseif (! is_null($this->queryParameters)) { | ||
$this->resource->appends($this->queryParameters); | ||
} | ||
|
||
return (new PaginatedResourceResponse($this))->toResponse($request); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<?php | ||
|
||
namespace AgilePixels\ResourceAbilities; | ||
|
||
use Illuminate\Http\JsonResponse; | ||
use Illuminate\Http\Request; | ||
use Illuminate\Http\Resources\Json\ResourceResponse as Response; | ||
|
||
class ResourceResponse extends Response | ||
{ | ||
/** | ||
* Create an HTTP response that represents the object. We make sure the returned array from the resolve() method | ||
* isn't wrapped again since we moved the wrapping logic to the resource instead of this response class. | ||
* | ||
* @param Request $request | ||
* @return JsonResponse | ||
*/ | ||
public function toResponse($request): JsonResponse | ||
{ | ||
return tap(response()->json( | ||
$this->resource->resolve($request), | ||
$this->calculateStatus() | ||
), function ($response) use ($request) { | ||
$response->original = $this->resource->resource; | ||
|
||
$this->resource->withResponse($request, $response); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.