Skip to content

Instantly share code, notes, and snippets.

@Alexisgt01
Created January 23, 2024 10:52
Show Gist options
  • Save Alexisgt01/8485b1063dc5b300c6e877913ff160c8 to your computer and use it in GitHub Desktop.
Save Alexisgt01/8485b1063dc5b300c6e877913ff160c8 to your computer and use it in GitHub Desktop.
Search and fill pattern
Route::controller(SupportController::class)->prefix('support')->group(function () {
Route::get('search', 'simple');
Route::get('fill', 'fill');
});
<?php
namespace App\Models\Contracts;
interface IsSearchEntity
{
public function getSearchIdentifier(): string|int;
public function getSearchLabel(): string|int;
public function getSearchDetails(): string|int|array;
public function getSearchType(): string;
}
<?php
namespace App\Http\Resources\Support;
use Illuminate\Http\Resources\Json\ResourceCollection;
class SearchCollection extends ResourceCollection
{
/**
* @param $resource
*/
public function __construct($resource)
{
parent::__construct($resource);
}
}
<?php
namespace App\Support;
final readonly class SearchEntity
{
public function __construct(
private \Closure $collectionCallback,
private \Closure $builderCallback,
private \Closure $authorizationCallback,
private \Closure $fillCallback,
){}
public function getCollectionCallback(): \Closure
{
return $this->collectionCallback;
}
public function getBuilderCallback(): \Closure
{
return $this->builderCallback;
}
public function getAuthorizationCallback(): \Closure
{
return $this->authorizationCallback;
}
public function getFillCallback(): \Closure
{
return $this->fillCallback;
}
}
<?php
namespace App\Http\Resources\Support;
use App\Models\Contracts\IsSearchEntity;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/** @mixin IsSearchEntity */
class SearchResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'id' => $this->getSearchIdentifier(),
'label' => $this->getSearchLabel(),
'details' => $this->getSearchDetails(),
'type' => $this->getSearchType(),
];
}
}
// with pinia, works with vuex
import {defineStore} from 'pinia'
import Support from "@/api/Support";
const api = new Support();
export const useSupport = defineStore('domain', {
state: () => {
return {
loading: false as boolean,
}
},
actions: {
fill(type: string) {
this.loading = true;
return api.fill(type)
.then((r) => {
this[type] = r.data.data;
return r.data;
})
.catch((e) => {
alertStore.error(e);
throw e;
})
.finally(() => {
this.loading = false;
});
}
}
});
<?php
namespace App\Http\Controllers;
use App\Http\Resources\Support\SearchCollection;
use App\Models\User;
use App\Support\SearchEntity;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;
use Illuminate\Support\Arr;
class SupportController extends Controller
{
protected array $searchEntities;
public function __construct()
{
$this->searchEntities = [
'user' => new SearchEntity(
fn(Collection $collection): ResourceCollection => new SearchCollection($collection),
fn(?string $label): Builder => User::where('name', 'like', "%$label%"),
fn() => $this->authorize('viewAny', User::class), // Based on UserPolicy, update as your wish
fn() => User::select(['id', 'name'])->scope()->get(),
),
];
}
public function simple(Request $request)
{
$request->validate([
'search' => ['string', 'nullable'],
'type' => ['in:' . Arr::join(array_keys($this->searchEntities), ',')]
]);
$entity = $this->searchEntities[$request->type];
$entity->getAuthorizationCallback()();
$builder = $entity->getBuilderCallback()($request->search);
$data = $builder instanceof Builder ? $builder->limit(20)->get() : $builder;
return $entity->getCollectionCallback()($data);
}
public function fill(Request $request)
{
$request->validate([
'type' => ['in:' . Arr::join(array_keys($this->searchEntities), ',')]
]);
$entity = $this->searchEntities[$request->type];
$entity->getAuthorizationCallback()();
$data = $entity->getFillCallback()();
return $entity->getCollectionCallback()($data);
}
}
<?php
namespace App\Models;
use App\Models\Contracts\IsSearchEntity;
use App\Models\Scope\TenantByUserScope;
use App\State\Tenant\From\From;
use App\State\Tenant\From\Undefined;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Auth;
class User extends Model implements IsSearchEntity
{
public function getSearchIdentifier(): string|int
{
return $this->id;
}
public function getSearchLabel(): string|int
{
return $this->name;
}
public function getSearchDetails(): array|int|string
{
return [];
}
public function getSearchType(): string
{
return 'user';
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment