Skip to content

Instantly share code, notes, and snippets.

@bayareawebpro
Last active March 12, 2022 00:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bayareawebpro/a6cbc6403c9a90af1b47f2dcefa48930 to your computer and use it in GitHub Desktop.
Save bayareawebpro/a6cbc6403c9a90af1b47f2dcefa48930 to your computer and use it in GitHub Desktop.
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);
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment