Skip to content

Instantly share code, notes, and snippets.

@bayareawebpro
Created April 22, 2022 05:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bayareawebpro/faee40719707f7d781126d1274b770e4 to your computer and use it in GitHub Desktop.
Save bayareawebpro/faee40719707f7d781126d1274b770e4 to your computer and use it in GitHub Desktop.
<?php declare(strict_types=1);
namespace App\Services;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Http\JsonResponse;
use Illuminate\Contracts\View\View;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Http\Resources\Json\JsonResource;
class Resource implements Responsable
{
protected Builder $query;
protected Request $request;
protected Collection $scopeBag;
protected Collection $parameterBag;
protected array|null $view = null;
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) => (
$value instanceof \Closure
? call_user_func($value, $parameters)
: $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 view(string $view, array $data = []): self
{
$this->view = compact('view', 'data');
return $this;
}
public function toResponse($request): View|JsonResponse
{
if ($this->view && !($request->wantsJson() || $request->ajax())) {
return view($this->view['view'], $this->view['data']);
}
$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);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment