Skip to content

Commit

Permalink
feat: scoped filters
Browse files Browse the repository at this point in the history
  • Loading branch information
alexzarbn authored Dec 30, 2021
1 parent 50acdad commit 3e51f57
Show file tree
Hide file tree
Showing 13 changed files with 302 additions and 31 deletions.
45 changes: 39 additions & 6 deletions src/Concerns/BuildsResponses.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,24 @@
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Resources\Json\ResourceCollection;
use Illuminate\Support\Collection;
use Orion\Http\Resources\Resource;

trait BuildsResponses
{
protected $meta = [];

/**
* @param Model $entity
* @return JsonResource
*/
public function entityResponse(Model $entity): JsonResource
{
$resource = $this->getResource();
$resourceClass = $this->getResource();

/** @var Resource $resource */
$resource = new $resourceClass($entity);

return new $resource($entity);
return $this->addMetaToResource($resource);
}

/**
Expand All @@ -27,12 +33,39 @@ public function entityResponse(Model $entity): JsonResource
*/
public function collectionResponse($entities): ResourceCollection
{
if ($collectionResource = $this->getCollectionResource()) {
return new $collectionResource($entities);
if ($collectionResourceClass = $this->getCollectionResource()) {
$collectionResource = new $collectionResourceClass($entities);
} else {
$resource = $this->getResource();

$collectionResource = $resource::collection($entities);
}

$resource = $this->getResource();

return $resource::collection($entities);
return $this->addMetaToResource($collectionResource);
}

public function withMeta(string $key, $value): self
{
$this->meta[$key] = $value;

return $this;
}

/**
* @param JsonResource|ResourceCollection $resource
* @return JsonResource|ResourceCollection
*/
protected function addMetaToResource($resource)
{
if (count($this->meta)) {
$resource->additional(
[
'meta' => $this->meta,
]
);
}

return $resource;
}
}
42 changes: 30 additions & 12 deletions src/Concerns/HandlesRelationStandardOperations.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@
namespace Orion\Concerns;

use Exception;
use Illuminate\Support\Arr;
use InvalidArgumentException;
use Orion\Http\Requests\Request;
use Orion\Http\Resources\Resource;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Orion\Http\Resources\CollectionResource;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Arr;
use InvalidArgumentException;
use Orion\Http\Requests\Request;
use Orion\Http\Resources\CollectionResource;
use Orion\Http\Resources\Resource;

trait HandlesRelationStandardOperations
{
Expand Down Expand Up @@ -151,6 +151,13 @@ protected function runParentFetchQuery(Request $request, Builder $query, $parent
*/
protected function buildIndexFetchQuery(Request $request, Model $parentEntity, array $requestedRelations): Relation
{
$filters = collect($request->get('filters', []))
->map(function (array $filterDescriptor) use ($request, $parentEntity) {
return $this->beforeFilterApplied($request, $parentEntity, $filterDescriptor);
})->toArray();

$request->merge(['filters' => $filters]);

return $this->buildRelationFetchQuery($request, $parentEntity, $requestedRelations);
}

Expand Down Expand Up @@ -1136,4 +1143,15 @@ protected function performFill(
Arr::except($attributes, array_keys($entity->getDirty()))
);
}

/**
* @param Request $request
* @param Model $parentEntity
* @param array $filterDescriptor
* @return array
*/
protected function beforeFilterApplied(Request $request, Model $parentEntity, array $filterDescriptor): array
{
return $filterDescriptor;
}
}
98 changes: 98 additions & 0 deletions src/Concerns/HandlesScopedFilters.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

declare(strict_types=1);

namespace Orion\Concerns;

use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Orion\Http\Controllers\RelationController;
use Orion\Http\Requests\Request;

/**
* @mixin RelationController
*/
trait HandlesScopedFilters
{
/**
* @param $query
* @param Request $request
* @param array $mappingCallbacks
* @return array
*/
public function resolveScopedFilters($query, Request $request, array $mappingCallbacks): array
{
$requestedFilters = $request->get('filters', []);

$filters = $this->getScopedFilterDescriptors(collect($requestedFilters));

$scopedFilters = [];
$appliedFilters = [];

foreach ($filters as $filterDescriptor) {
$qualifiedField = $this->qualifyScopedFilterField($filterDescriptor['field']);

$scopedFilters[$filterDescriptor['field']] = [
'values' => collect(
$query
->select([$qualifiedField])
->groupBy($qualifiedField)
->reorder()
->getModels()
)->map(
(function ($model) use ($filterDescriptor, $mappingCallbacks) {
$value = Arr::first($model->getAttributes());

if ($mappingCallback = Arr::get($mappingCallbacks, $filterDescriptor['field'])) {
$value = $mappingCallback($value, $filterDescriptor);
}

return ['value' => $value];
})
)->unique('value')->values()->toArray(),
];

if (Arr::has($filterDescriptor, 'value')) {
$appliedFilters[] = $filterDescriptor;

$this->getResourceQueryBuilder()->applyFiltersToQuery($query, $request, $appliedFilters);
}
}

return $scopedFilters;
}

protected function qualifyScopedFilterField(string $field): string
{
if (!str_contains($field, '.')) {
return $this->resolveQualifiedFieldName($field);
}

$relation = $this->relationsResolver->relationFromParamConstraint($field);
$relationField = $this->relationsResolver->relationFieldFromParamConstraint($field);

if ($relation === 'pivot') {
return $this->resolveQualifiedPivotFieldName($relationField);
}

return $this->resolveQualifiedRelationFieldName($relation, $relationField);
}

protected function getScopedFilterDescriptors(Collection $requestedFilters): Collection
{
$filters = collect($this->scopedFilters())
->filter(function (string $field) use ($requestedFilters) {
return !$requestedFilters->contains('field', $field);
})->map(
function (string $field) {
return ['field' => $field];
}
)->values();

return $requestedFilters->merge($filters)->filter(
function (array $filterDescriptor) {
return in_array($filterDescriptor['field'], $this->filterableBy(), true);
}
);
}
}
40 changes: 35 additions & 5 deletions src/Concerns/HandlesStandardOperations.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace Orion\Concerns;

use Exception;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
Expand All @@ -20,6 +22,8 @@ trait HandlesStandardOperations
*
* @param Request $request
* @return CollectionResource
* @throws AuthorizationException
* @throws BindingResolutionException
*/
public function index(Request $request)
{
Expand Down Expand Up @@ -58,6 +62,13 @@ public function index(Request $request)
*/
protected function buildIndexFetchQuery(Request $request, array $requestedRelations): Builder
{
$filters = collect($request->get('filters', []))
->map(function(array $filterDescriptor) use ($request) {
return $this->beforeFilterApplied($request, $filterDescriptor);
})->toArray();

$request->merge(['filters' => $filters]);

return $this->buildFetchQuery($request, $requestedRelations);
}

Expand Down Expand Up @@ -92,14 +103,15 @@ protected function beforeIndex(Request $request)
* @param Builder $query
* @param int $paginationLimit
* @return Paginator|Collection
* @throws BindingResolutionException
*/
protected function runIndexFetchQuery(Request $request, Builder $query, int $paginationLimit)
{
return $this->shouldPaginate($request, $paginationLimit) ? $query->paginate($paginationLimit) : $query->get();
}

/**
* The hooks is executed after fetching the list of resources.
* The hook is executed after fetching the list of resources.
*
* @param Request $request
* @param Paginator|Collection $entities
Expand All @@ -115,6 +127,8 @@ protected function afterIndex(Request $request, $entities)
*
* @param Request $request
* @return CollectionResource
* @throws AuthorizationException
* @throws BindingResolutionException
*/
public function search(Request $request)
{
Expand All @@ -134,7 +148,7 @@ public function store(Request $request)
$result = $this->storeWithTransaction($request);
$this->commitTransaction();
return $result;
} catch (\Exception $exception) {
} catch (Exception $exception) {
$this->rollbackTransactionAndRaise($exception);
}
}
Expand All @@ -144,6 +158,8 @@ public function store(Request $request)
*
* @param Request $request
* @return Resource
* @throws AuthorizationException
* @throws BindingResolutionException
*/
protected function storeWithTransaction(Request $request)
{
Expand Down Expand Up @@ -276,6 +292,8 @@ protected function afterStore(Request $request, Model $entity)
* @param Request $request
* @param int|string $key
* @return Resource
* @throws AuthorizationException
* @throws BindingResolutionException
*/
public function show(Request $request, $key)
{
Expand Down Expand Up @@ -378,7 +396,7 @@ public function update(Request $request, $key)
$result = $this->updateWithTransaction($request, $key);
$this->commitTransaction();
return $result;
} catch (\Exception $exception) {
} catch (Exception $exception) {
$this->rollbackTransactionAndRaise($exception);
}
}
Expand All @@ -389,6 +407,8 @@ public function update(Request $request, $key)
* @param Request $request
* @param int|string $key
* @return Resource
* @throws AuthorizationException
* @throws BindingResolutionException
*/
protected function updateWithTransaction(Request $request, $key)
{
Expand Down Expand Up @@ -526,7 +546,7 @@ public function destroy(Request $request, $key)
$result = $this->destroyWithTransaction($request, $key);
$this->commitTransaction();
return $result;
} catch (\Exception $exception) {
} catch (Exception $exception) {
$this->rollbackTransactionAndRaise($exception);
}
}
Expand Down Expand Up @@ -689,7 +709,7 @@ public function restore(Request $request, $key)
$result = $this->restoreWithTransaction($request, $key);
$this->commitTransaction();
return $result;
} catch (\Exception $exception) {
} catch (Exception $exception) {
$this->rollbackTransactionAndRaise($exception);
}
}
Expand Down Expand Up @@ -821,4 +841,14 @@ protected function performFill(Request $request, Model $entity, array $attribute
Arr::except($attributes, array_keys($entity->getDirty()))
);
}

/**
* @param Request $request
* @param array $filterDescriptor
* @return array
*/
protected function beforeFilterApplied(Request $request, array $filterDescriptor): array
{
return $filterDescriptor;
}
}
5 changes: 3 additions & 2 deletions src/Concerns/HandlesTransactions.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Orion\Concerns;

use Exception;
use Illuminate\Support\Facades\DB;

trait HandlesTransactions
Expand Down Expand Up @@ -54,12 +55,12 @@ protected function rollbackTransaction(): void
* Rollback changes made to database and finish
* database transaction and finally raise an exception
*
* @param \Exception $exception
* @param Exception $exception
* @return void
*
* @throws Exception
*/
protected function rollbackTransactionAndRaise(\Exception $exception): void
protected function rollbackTransactionAndRaise(Exception $exception): void
{
$this->rollbackTransaction();

Expand Down
Loading

0 comments on commit 3e51f57

Please sign in to comment.