diff --git a/README.md b/README.md index d606f01..cde9c71 100644 --- a/README.md +++ b/README.md @@ -293,6 +293,8 @@ OBS Set `$returnNullOnInvalidColumnAttributeAccess = false;` in model if you want exception instead of null on accessing invalid model attributes or invalid relations (It also needs error_reporting = E_ALL in php ini file). +Set `LIST_UN_HYDRATED_WHEN_POSSIBLE = true` in model if you want to skip eloquent hydration for list db query results; note that setting this to true will not append the primary_key_identifier on response. + Set LIVE_MODE=false in your .env file for non prod environments. Use Request::getFiltered macro to sanitize data retrieved from request diff --git a/src/Eloquent/CustomRelations/Builders/CleverEloquentBuilder.php b/src/Eloquent/CustomRelations/Builders/CleverEloquentBuilder.php index b69ba72..7c644ae 100644 --- a/src/Eloquent/CustomRelations/Builders/CleverEloquentBuilder.php +++ b/src/Eloquent/CustomRelations/Builders/CleverEloquentBuilder.php @@ -2,12 +2,17 @@ namespace MacropaySolutions\LaravelCrudWizard\Eloquent\CustomRelations\Builders; +use Illuminate\Contracts\Pagination\CursorPaginator; +use Illuminate\Contracts\Pagination\Paginator; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\RelationNotFoundException; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\Relations\Relation; +use Illuminate\Pagination\Cursor; +use Illuminate\Pagination\LengthAwarePaginator; +use Illuminate\Support\Collection; use MacropaySolutions\LaravelCrudWizard\Models\BaseModel; /** @@ -15,6 +20,8 @@ */ class CleverEloquentBuilder extends Builder { + protected bool $getUnHydrated = false; + /** * @inheritDoc */ @@ -41,6 +48,73 @@ public function getRelation($name): Relation return $relation; } + /** + * @throws \Throwable + */ + public function getUnHydrated(array|string $columns = ['*']): Collection + { + return $this->toBase()->get($columns); + } + + /** + * @throws \Throwable + */ + public function paginateUnHydrated( + int|null|\Closure $perPage = null, + array|string $columns = ['*'], + string $pageName = 'page', + int|null $page = null, + int|null|\Closure $total = null, + ): LengthAwarePaginator { + $this->getUnHydrated = true; + $result = $total !== null ? + $this->paginate($perPage, $columns, $pageName, $page, $total) : + $this->paginate($perPage, $columns, $pageName, $page); + $this->getUnHydrated = false; + + return $result; + } + + /** + * @throws \Throwable + */ + public function simplePaginateUnHydrated( + int|null $perPage = null, + array $columns = ['*'], + string $pageName = 'page', + int|null $page = null + ): Paginator { + $this->getUnHydrated = true; + $result = $this->simplePaginate($perPage, $columns, $pageName, $page); + $this->getUnHydrated = false; + + return $result; + } + + /** + * @throws \Throwable + */ + public function cursorPaginateUnHydrated( + int|null $perPage = null, + array|string $columns = ['*'], + string $cursorName = 'cursor', + Cursor|string|null $cursor = null + ): CursorPaginator { + $this->getUnHydrated = true; + $result = $this->cursorPaginate($perPage, $columns, $cursorName, $cursor); + $this->getUnHydrated = false; + + return $result; + } + + /** + * @inheritdoc + */ + public function get($columns = ['*']): \Illuminate\Database\Eloquent\Collection|array|Collection + { + return $this->getUnHydrated ? $this->getUnHydrated($columns) : parent::get($columns); + } + /** * @inheritDoc */ @@ -100,4 +174,4 @@ public function with($relations, $callback = null): static return $this; } -} \ No newline at end of file +} diff --git a/src/Http/Controllers/ResourceControllerTrait.php b/src/Http/Controllers/ResourceControllerTrait.php index 8d43d9a..fc5d253 100644 --- a/src/Http/Controllers/ResourceControllerTrait.php +++ b/src/Http/Controllers/ResourceControllerTrait.php @@ -435,8 +435,28 @@ protected function getPaginator( ): LengthAwarePaginator | Paginator | CursorPaginator { $model = $builder instanceof Relation ? $builder->getRelated() : $builder->getModel(); $limit = \max(1, (int)($allRequest['limit'] ?? $model->getPerPage())); + $listUnHydrated = false; + + if ($model::LIST_UN_HYDRATED_WHEN_POSSIBLE) { + /** @var BaseModel $first */ + $first = $model->exists ? $model : ($model::query()->first() ?? $model); + $listUnHydrated = $builder->getEagerLoads() === [] + && \array_diff_key( + $first->attributesToArray(), + ['primary_key_identifier' => null] + ) === $first->getRawOriginal(); + } if (isset($allRequest['cursor'])) { + if ($listUnHydrated) { + return $builder->cursorPaginateUnHydrated( + $limit, + ['*'], + 'cursor', + $allRequest['cursor'] + ); + } + return $builder->cursorPaginate( $limit, ['*'], @@ -445,6 +465,24 @@ protected function getPaginator( ); } + if ($listUnHydrated) { + if ($this->simplePaginate) { + return $builder->simplePaginateUnHydrated( + $limit, + ['*'], + 'page', + \max((int)($allRequest['page'] ?? 1), 1) + ); + } + + return $builder->paginateUnHydrated( + $limit, + ['*'], + 'page', + \max((int)($allRequest['page'] ?? 1), 1) + ); + } + return $builder->{$this->simplePaginate ? 'simplePaginate' : 'paginate'}( $limit, ['*'], diff --git a/src/Models/BaseModel.php b/src/Models/BaseModel.php index 3c31e74..4f9c013 100644 --- a/src/Models/BaseModel.php +++ b/src/Models/BaseModel.php @@ -28,6 +28,10 @@ abstract class BaseModel extends Model public const CREATED_AT_FORMAT = 'Y-m-d H:i:s'; public const UPDATED_AT_FORMAT = 'Y-m-d H:i:s'; public const COMPOSITE_PK_SEPARATOR = '_'; + /** + * Setting this to true will not append the primary_key_identifier on response + */ + public const LIST_UN_HYDRATED_WHEN_POSSIBLE = false; public $timestamps = false; public static $snakeAttributes = false; public BaseModelAttributes $a; @@ -110,8 +114,7 @@ public function getIndexRequiredOnFilteringAttribute(): array */ public function getPrimaryKeyIdentifierAttribute(): string { - return \implode( - $this::COMPOSITE_PK_SEPARATOR, \array_map( + return \implode($this::COMPOSITE_PK_SEPARATOR, \array_map( fn (mixed $value): string => (string)(\is_array($value) ? \last($value) : $value), $this->getPrimaryKeyFilter() )); @@ -242,7 +245,7 @@ function () use ($driver): array { Carbon::now()->addDay(), $callback )); - } catch (\Throwable $e) { + } catch (\Throwable) { return \collect($callback()); } } @@ -364,7 +367,7 @@ public function getAttributeValue($key): mixed if (!$this->hasGetMutator($key) && !$this->hasAttributeGetMutator($key)) { try { return $this->attributes[$key]; - } catch (\Throwable $e) { + } catch (\Throwable) { throw new \Exception('Undefined attribute: ' . $key . ' in model: ' . static::class); } }