model = $model; $this->cache = $cache; $this->query = $model->newQuery(); $this->cacheTag = $this->getCacheTag(); } abstract protected function getCacheTag(): string; protected function getCacheKey(string $method, array $parameters = []): string { $key = $this->cacheTag.':'.$method; if (! empty($parameters)) { $key .= ':'.md5(serialize($parameters)); } $key .= ':'.md5(serialize($this->withRelations)); $key .= ':'.md5(serialize($this->withCountRelations)); return $key; } protected function executeQuery(callable $callback, ?string $cacheKey = null, ?int $ttl = null) { if (! $this->cachingEnabled || $cacheKey === null) { return $callback(); } $ttl = $ttl ?? $this->cacheTtl; return $this->cache->tags([$this->cacheTag])->remember($cacheKey, $ttl, $callback); } protected function resetQuery(): void { $this->query = $this->model->newQuery(); $this->withRelations = []; $this->withCountRelations = []; } public function findById(int $id, array $columns = ['*']): ?Model { $cacheKey = $this->getCacheKey('find_by_id', ['id' => $id, 'columns' => $columns]); return $this->executeQuery(function () use ($id, $columns) { $result = $this->query->find($id, $columns); $this->resetQuery(); return $result; }, $cacheKey); } public function findBy(string $column, mixed $value, array $columns = ['*']): ?Model { $cacheKey = $this->getCacheKey('find_by', [$column => $value, 'columns' => $columns]); return $this->executeQuery(function () use ($column, $value, $columns) { $result = $this->query->where($column, $value)->first($columns); $this->resetQuery(); return $result; }, $cacheKey); } public function all(array $columns = ['*']): Collection { $cacheKey = $this->getCacheKey('all', ['columns' => $columns]); return $this->executeQuery(function () use ($columns) { $result = $this->query->get($columns); $this->resetQuery(); return $result; }, $cacheKey); } public function get(array $columns = ['*']): Collection { $cacheKey = $this->getCacheKey('get', ['columns' => $columns]); return $this->executeQuery(function () use ($columns) { $result = $this->query->get($columns); $this->resetQuery(); return $result; }, $cacheKey); } public function paginate(int $perPage = 15, array $columns = ['*'], string $pageName = 'page'): LengthAwarePaginator { $page = request()->input($pageName, 1); $cacheKey = $this->getCacheKey('paginate', [ 'per_page' => $perPage, 'columns' => $columns, 'page' => $page, ]); return $this->executeQuery(function () use ($perPage, $columns, $pageName) { $result = $this->query->paginate($perPage, $columns, $pageName); $this->resetQuery(); return $result; }, $cacheKey, 1800); // 30 minutes for pagination } public function whereIn(string $column, array $values, array $columns = ['*']): Collection { $cacheKey = $this->getCacheKey('where_in', [$column => $values, 'columns' => $columns]); return $this->executeQuery(function () use ($column, $values, $columns) { $result = $this->query->whereIn($column, $values)->get($columns); $this->resetQuery(); return $result; }, $cacheKey); } public function where(string $column, mixed $operator, mixed $value = null): static { if (func_num_args() === 2) { $this->query->where($column, $operator); } else { $this->query->where($column, $operator, $value); } return $this; } public function whereNull(string $column): static { $this->query->whereNull($column); return $this; } public function whereNotNull(string $column): static { $this->query->whereNotNull($column); return $this; } public function whereBetween(string $column, array $values): static { $this->query->whereBetween($column, $values); return $this; } public function orderBy(string $column, string $direction = 'asc'): static { $this->query->orderBy($column, $direction); return $this; } public function limit(int $limit): static { $this->query->limit($limit); return $this; } public function with(array $relations): static { $this->withRelations = array_merge($this->withRelations, $relations); $this->query->with($relations); return $this; } public function withCount(array $relations): static { $this->withCountRelations = array_merge($this->withCountRelations, $relations); $this->query->withCount($relations); return $this; } public function exists(): bool { $cacheKey = $this->getCacheKey('exists'); return $this->executeQuery(function () { $result = $this->query->exists(); $this->resetQuery(); return $result; }, $cacheKey, 300); // 5 minutes for exists check } public function count(): int { $cacheKey = $this->getCacheKey('count'); return $this->executeQuery(function () { $result = $this->query->count(); $this->resetQuery(); return $result; }, $cacheKey, 600); // 10 minutes for count } public function first(array $columns = ['*']): ?Model { $cacheKey = $this->getCacheKey('first', ['columns' => $columns]); return $this->executeQuery(function () use ($columns) { $result = $this->query->first($columns); $this->resetQuery(); return $result; }, $cacheKey); } public function firstWhere(string $column, mixed $operator, mixed $value = null): ?Model { $cacheKey = $this->getCacheKey('first_where', [$column => $operator, 'value' => $value]); return $this->executeQuery(function () use ($column, $operator, $value) { if (func_num_args() === 2) { $result = $this->query->firstWhere($column, $operator); } else { $result = $this->query->firstWhere($column, $operator, $value); } $this->resetQuery(); return $result; }, $cacheKey); } public function pluck(string $column, ?string $key = null): Collection { $cacheKey = $this->getCacheKey('pluck', ['column' => $column, 'key' => $key]); return $this->executeQuery(function () use ($column, $key) { $result = $this->query->pluck($column, $key); $this->resetQuery(); return $result; }, $cacheKey); } public function clearCache(): void { $this->cache->tags([$this->cacheTag])->flush(); } public function clearCacheForModel(Model $model): void { $this->cache->tags([$this->cacheTag])->flush(); } public function disableCaching(): static { $this->cachingEnabled = false; return $this; } public function enableCaching(): static { $this->cachingEnabled = true; return $this; } public function setCacheTtl(int $ttl): static { $this->cacheTtl = $ttl; return $this; } protected function startTransaction(): void { DB::beginTransaction(); } protected function commitTransaction(): void { DB::commit(); } protected function rollbackTransaction(): void { DB::rollBack(); } public function transaction(callable $callback): mixed { try { $this->startTransaction(); $result = $callback(); $this->commitTransaction(); return $result; } catch (\Throwable $e) { $this->rollbackTransaction(); throw $e; } } }