diff --git a/composer.json b/composer.json index 52bc3d4..39ace06 100644 --- a/composer.json +++ b/composer.json @@ -18,8 +18,8 @@ }, "require-dev": { "laravel/pint": "^1.8", + "nunomaduro/larastan": "^2.7", "orchestra/testbench": "^8.0", - "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.0" }, "autoload": { diff --git a/config/repository.php b/config/repository.php index bb86353..e7a189a 100644 --- a/config/repository.php +++ b/config/repository.php @@ -1,5 +1,5 @@ 20 -]; \ No newline at end of file + 'perPage' => 20, +]; diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 5ebc5a4..bf90da7 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,8 +1,11 @@ includes: - phpstan-baseline.neon + - ./vendor/nunomaduro/larastan/extension.neon parameters: level: 8 paths: - src - - config \ No newline at end of file + - config + checkMissingIterableValueType: false + checkGenericClassInNonGenericObjectType: false diff --git a/src/BaseEloquentRepository.php b/src/BaseEloquentRepository.php index d854dbf..7eb5567 100644 --- a/src/BaseEloquentRepository.php +++ b/src/BaseEloquentRepository.php @@ -1,11 +1,14 @@ applyCriteria(); $this->applyRelations(); - $this->applyOrderBy(); + $this->applyOrder(); $result = $this->getQuery()->first(); $this->resetQuery(); @@ -67,8 +78,8 @@ public function findOneOrFail(int|string $primaryKey = null): Model if ($result === null) { throw (new ModelNotFoundException())->setModel( - get_debug_type($this->model), - $primaryKey + $this->model::class, + $primaryKey ?? '' ); } @@ -78,7 +89,7 @@ public function findOneOrFail(int|string $primaryKey = null): Model /** * {@inheritDoc} */ - public function findAll(array $options = []): Collection + public function findAll(array $options = []): EloquentCollection { $options += [ 'limit' => null, @@ -95,7 +106,7 @@ public function findAll(array $options = []): Collection $this->applyCriteria(); $this->applyRelations(); - $this->applyOrderBy(); + $this->applyOrder(); $results = $this->getQuery()->get(); $this->resetQuery(); @@ -105,11 +116,11 @@ public function findAll(array $options = []): Collection /** * {@inheritDoc} */ - public function findList(string $key = null, string $value = null): GeneralCollection + public function findList(string $key = null, string $value = null): Collection { $this->applyCriteria(); $this->applyRelations(); - $this->applyOrderBy(); + $this->applyOrder(); $key = $key ?? $this->getModel()->getKeyName(); $value = $value ?? $this->getDisplayField(); @@ -129,11 +140,11 @@ public function findList(string $key = null, string $value = null): GeneralColle */ public function paginate(int $perPage = null): Paginator { - $this->perPage = $perPage ?? $this->perPage; + $perPage = $this->preparePageSize($perPage); $this->applyCriteria(); $this->applyRelations(); - $this->applyOrderBy(); + $this->applyOrder(); $results = $this->getQuery()->paginate($perPage); $this->resetQuery(); @@ -141,10 +152,23 @@ public function paginate(int $perPage = null): Paginator } /** - * @param array $conditions Conditions - * @return $this + * Prepare the page size for pagination. + * + * @throws \InvalidArgumentException */ - protected function applyConditions(array $conditions): self + protected function preparePageSize(int $perPage = null): int + { + if ($perPage <= 0) { + throw new InvalidArgumentException('Invalid page size'); + } + + return min($perPage, $this->perPage); + } + + /** + * Apply conditions to the query based on an array of conditions. + */ + protected function applyConditions(array $conditions): static { $conditions = array_combine($this->aliasFields(array_keys($conditions)), $conditions); $this->getQuery()->where($conditions); @@ -155,7 +179,7 @@ protected function applyConditions(array $conditions): self /** * {@inheritDoc} */ - public function addCriteria(CriteriaInterface $criteria): self + public function addCriteria(CriteriaInterface $criteria): static { $this->criteria[] = $criteria; @@ -163,9 +187,9 @@ public function addCriteria(CriteriaInterface $criteria): self } /** - * @return $this + * Apply criteria to the query based on registered criteria objects. */ - protected function applyCriteria(): self + protected function applyCriteria(): static { foreach ($this->criteria as $criteria) { $criteria->apply($this->getModel(), $this->getQuery()); @@ -177,14 +201,17 @@ protected function applyCriteria(): self /** * {@inheritDoc} */ - public function orderBy(string $field, string $direction = 'ASC'): self + public function orderBy(string $field, string $direction = 'ASC'): static { $this->orderByFields[$this->aliasField($field)] = $direction; return $this; } - protected function applyOrderBy(): void + /** + * Apply ordering to the query based on specified fields and directions. + */ + protected function applyOrder(): static { $fields = $this->orderByFields; if (! $fields) { @@ -194,13 +221,14 @@ protected function applyOrderBy(): void foreach ($fields as $field => $direction) { $this->getQuery()->orderBy($field, $direction); } + + return $this; } /** - * @param array $relations Relations - * @return $this + * Add relations to be eager-loaded when querying. */ - protected function with(array $relations): self + protected function with(array $relations): static { $this->relations = array_merge($this->relations, $relations); @@ -210,7 +238,7 @@ protected function with(array $relations): self /** * {@inheritDoc} */ - public function lockForUpdate(): self + public function lockForUpdate(): static { $this->getQuery()->lockForUpdate(); @@ -220,18 +248,23 @@ public function lockForUpdate(): self /** * {@inheritDoc} */ - public function sharedLock(): self + public function sharedLock(): static { $this->getQuery()->sharedLock(); return $this; } - protected function applyRelations(): void + /** + * Apply eager-loading relations to the query. + */ + protected function applyRelations(): static { if ($this->relations) { $this->getQuery()->with($this->relations); } + + return $this; } /** @@ -266,8 +299,7 @@ protected function aliasField(string $field): string protected function getModel(): Model { if (! isset($this->model)) { - $className = $this->getModelClass(); - $this->model = new $className(); + $this->model = app($this->getModelClass()); } return $this->model; @@ -301,6 +333,9 @@ protected function getQuery(): Builder return $this->query; } + /** + * Reset the query builder and related properties. + */ protected function resetQuery(): void { unset($this->query); @@ -308,12 +343,4 @@ protected function resetQuery(): void $this->relations = []; $this->orderByFields = []; } - - /** - * This function returns the name of the field that will be used to display the record in the list view. - */ - protected function getDisplayField(): string - { - return 'id'; - } } diff --git a/src/BaseFilter.php b/src/BaseFilter.php index 6487341..82b038e 100644 --- a/src/BaseFilter.php +++ b/src/BaseFilter.php @@ -1,5 +1,7 @@ '%'.$value, $this::WILD_AFTER => $value.'%', $this::WILD_BOTH => '%'.$value.'%', + default => $value, }; $this->getQuery()->where($field, 'like', $matchText); @@ -84,4 +87,4 @@ protected function getQuery(): QueryBuilder return $this->builder; } -} \ No newline at end of file +} diff --git a/src/Contracts/CriteriaInterface.php b/src/Contracts/CriteriaInterface.php index e3e4e83..e7382f9 100644 --- a/src/Contracts/CriteriaInterface.php +++ b/src/Contracts/CriteriaInterface.php @@ -2,14 +2,13 @@ namespace Salehhashemi\Repository\Contracts; -use Illuminate\Database\Eloquent\Builder; +use Illuminate\Contracts\Database\Query\Builder; use Illuminate\Database\Eloquent\Model; interface CriteriaInterface { /** - * @param \Illuminate\Database\Eloquent\Model $model Model - * @param \Illuminate\Database\Eloquent\Builder $query Query + * Apply the criteria to the given Eloquent query builder. */ public function apply(Model $model, Builder $query): Builder; -} \ No newline at end of file +} diff --git a/src/Contracts/RepositoryInterface.php b/src/Contracts/RepositoryInterface.php index 55f3fc0..84567d9 100644 --- a/src/Contracts/RepositoryInterface.php +++ b/src/Contracts/RepositoryInterface.php @@ -3,39 +3,34 @@ namespace Salehhashemi\Repository\Contracts; use Illuminate\Contracts\Pagination\Paginator; +use Illuminate\Database\Eloquent\Collection as EloquentCollection; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; interface RepositoryInterface { /** - * > It applies the conditions, criteria, relations, and order by to the query, + * It applies the conditions, criteria, relations, and order by to the query, * then gets the first result and resets the query - * - * @param int|string|null $primaryKey The primary key of the model you want to find. - * @return null|\Illuminate\Database\Eloquent\Model The first result of the query. */ public function findOne(int|string $primaryKey = null): ?Model; /** - * > If the result of the `findOne` function is null, throw a `ModelNotFoundException` - * - * @param int|string|null $primaryKey Primary key + * If the result of the `findOne` function is null, throw a `ModelNotFoundException` * - * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model> + * @throws \Illuminate\Database\Eloquent\ModelNotFoundException */ public function findOneOrFail(int|string $primaryKey = null): Model; /** - * It takes an array of options, applies them to the query, executes the query, and returns the results + * It takes an array of options, applies them to the query, executes the query, and returns the results. * - * @param array $options An array of options to pass to the finder. - * @return \Illuminate\Support\Collection A collection of results. + * @param array $options */ - public function findAll(array $options = []): Collection; + public function findAll(array $options = []): EloquentCollection; /** - * > It returns a collection of key-value pairs from the database + * It returns a collection of key-value pairs from the database. * * @param string|null $key The key to use for the list. * @param string|null $value The field to use as the value of the select list. @@ -45,37 +40,27 @@ public function findList(string $key = null, string $value = null): Collection; /** * It applies the criteria, relations, and order by to the query, then paginates the results and resets the query - * - * @param int $perPage The number of items to show per page. - * @return \Illuminate\Contracts\Pagination\Paginator A paginated collection of results. */ - public function paginate(int $perPage): Paginator; + public function paginate(int $perPage = null): Paginator; /** - * @param CriteriaInterface $criteria Criteria - * @return $this + * Add a criteria to the query. */ - public function addCriteria(CriteriaInterface $criteria): self; + public function addCriteria(CriteriaInterface $criteria): static; /** - * @param string $field Field - * @param string $direction Direction - * @return $this + * Add an order by clause to the query. */ - public function orderBy(string $field, string $direction = 'ASC'): self; + public function orderBy(string $field, string $direction = 'ASC'): static; /** - * > Locks the selected rows so that they can't be updated or deleted by other transactions until the next - * commit/rollback - * - * @return $this The query builder instance. + * Locks the selected rows so that they can't be updated or deleted by other transactions + * until the next commit/rollback */ - public function lockForUpdate(): self; + public function lockForUpdate(): static; /** - * > Adds a `LOCK IN SHARE MODE` clause to the query - * - * @return $this The query builder instance. + * Adds a `LOCK IN SHARE MODE` clause to the query */ - public function sharedLock(): self; + public function sharedLock(): static; } diff --git a/src/Contracts/SearchRepositoryInterface.php b/src/Contracts/SearchRepositoryInterface.php index 3d5bd0a..3b7fd09 100644 --- a/src/Contracts/SearchRepositoryInterface.php +++ b/src/Contracts/SearchRepositoryInterface.php @@ -7,9 +7,7 @@ interface SearchRepositoryInterface { /** - * @param array $queryParams Request params - * @param array $options The settings/configuration used for pagination. - * @return \Illuminate\Contracts\Pagination\Paginator; + * Search for records based on the provided query parameters and paginate the results. */ - public function search(array $queryParams, array $options = []): Paginator; -} \ No newline at end of file + public function search(array $queryParams, int $perPage = null): Paginator; +} diff --git a/src/Traits/SearchableTrait.php b/src/Traits/SearchableTrait.php index d8c6b74..b67000b 100644 --- a/src/Traits/SearchableTrait.php +++ b/src/Traits/SearchableTrait.php @@ -2,22 +2,23 @@ namespace Salehhashemi\Repository\Traits; -use App\Filters\BaseFilter; use Illuminate\Contracts\Pagination\Paginator; +use Salehhashemi\Repository\BaseFilter; +/** + * @mixin \Salehhashemi\Repository\BaseEloquentRepository + */ trait SearchableTrait { abstract protected function getFilterManager(): BaseFilter; /** - * @param int $perPage Rows Per Page + * {@inheritDoc} */ - abstract public function paginate(int $perPage = 10): Paginator; - - public function search(array $queryParams, array $options = []): Paginator + public function search(array $queryParams, int $perPage = null): Paginator { $this->setQuery($this->getFilterManager()->applyFilter($queryParams)); - return $this->paginate(); + return $this->paginate($perPage); } -} \ No newline at end of file +}