return Resource::fromQuery(Page::query())
->additional($this->compileActions('pages'))
->resource(PagesResource::class)
->sortable([
'id',
'slug',
'title',
'parent_id',
'created_at',
'updated_at'
])
->params([
'order_by' => 'id',
'sort' => 'desc',
'per_page' => 1,
])
->rules([
'search' => 'sometimes|nullable|string',
'status' => 'sometimes|nullable|string',
'parent' => 'sometimes|nullable|numeric',
])
->scopes([
'search' => null,
'parent' => null,
'status' => 'draft',
])
->options([
'order_by' => [
['label' => 'ID', 'value' => 'id'],
['label' => 'Slug', 'value' => 'slug'],
['label' => 'Title', 'value' => 'title'],
],
'sort' => [
['label' => 'Asc', 'value' => 'asc'],
['label' => 'Desc', 'value' => 'desc'],
],
'parent' => Page::parentOptions(),
'status' => Page::statusOptions(),
]);
<?php declare(strict_types=1);
namespace App\Services;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Support\Collection;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Support\Facades\App;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Resources\Json\JsonResource;
class Resource implements Responsable
{
protected Builder $query;
protected Request $request;
protected Collection $scopeBag;
protected Collection $parameterBag;
protected string $resource = JsonResource::class;
protected array $rules = [];
protected array $options = [];
protected array $additional = [];
protected array $parameters = [
'per_page' => 12,
'order_by' => 'id',
'sort' => 'desc',
];
public function __construct(Request $request, Builder $query)
{
$this->query = $query;
$this->request = $request;
$this->scopeBag = Collection::make();
$this->parameterBag = Collection::make($this->parameters);
$this->rules = array_merge([
'sort' => 'sometimes|string|in:asc,desc',
'per_page' => 'sometimes|numeric|min:1,max:50',
], $this->rules);
}
public static function fromQuery(Builder $query): self
{
return App::make(static::class, compact('query'));
}
public function rules(array $rules): self
{
$this->rules = array_merge($this->rules, $rules);
return $this;
}
public function sortable(array $columns): self
{
$this->rules([
'order_by' => ['sometimes', 'string', Rule::in($columns)],
]);
return $this;
}
public function scopes(array $scopes): self
{
$this->scopeBag = $this->scopeBag->merge($scopes);
return $this;
}
public function params(array $params): self
{
$this->parameterBag = $this->parameterBag->merge($params);
return $this;
}
public function options(array $options): self
{
$this->options = array_merge($this->options, $options);
return $this;
}
public function additional(array $additional): self
{
$this->additional = array_merge($this->additional, $additional);
return $this;
}
public function resource(string $resourceClassString): self
{
$this->resource = $resourceClassString;
return $this;
}
protected function validateParameters(): Collection
{
return $this->parameterBag
->merge($this->request->validate($this->rules))
->reject(fn($value) => empty($value));
}
protected function buildScopes(Collection $parameters): Collection
{
return $this->scopeBag
->map(fn($value, $key) => $parameters->get($key, $value))
->reject(fn($value) => empty($value));
}
protected function executePaginator(Collection $parameters, Collection $scopes): Paginator
{
return $this->query
->scopes($scopes->toArray())
->orderBy($parameters->get('order_by'), $parameters->get('sort'))
->paginate($parameters->get('per_page'));
}
protected function makeResourceCollection(Paginator $paginator): JsonResource
{
return call_user_func([$this->resource, "collection"], $paginator);
}
public function toResponse($request): JsonResponse
{
$parameters = $this->validateParameters();
$scopes = $this->buildScopes($parameters);
$combined = $parameters->merge($scopes)->toArray();
return $this
->makeResourceCollection(
$this->executePaginator($parameters, $scopes)->appends($combined)
)
->additional(array_merge($this->additional, [
'query' => $combined,
'options' => $this->options
]))
->toResponse($request ?? $this->request);
}
}