Skip to content

Commit

Permalink
[1.x] Adds Model Refiner
Browse files Browse the repository at this point in the history
  • Loading branch information
DarkGhostHunter committed Apr 10, 2024
1 parent 9dfe217 commit 5db112d
Show file tree
Hide file tree
Showing 8 changed files with 416 additions and 156 deletions.
71 changes: 48 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ class PostRefiner extends Refiner

### Only some keys

On rare occasions, you may have a method you don't want to be executed as part of the refinement procedure. In that case, you may instruct which URL parameters keys should be used to match their respective methods with the `getKeys()` method.
On rare occasions, you may have a method you don't want to be executed as part of the refinement procedure, especially if your Refiner is extending another Refiner. In that case, you may instruct which URL parameters keys should be used to match their respective methods with the `getKeys()` method.

```php
use Illuminate\Http\Request;
Expand Down Expand Up @@ -212,12 +212,12 @@ public function getObligatoryKeys(): array
}
```

Then, our method should be able to receive `null` for when the URL parameter doesn't exist.
Then, the method should be able to receive a `null` value when the URL parameter is not set.

```php
public function orderBy($query, ?string $value, Request $request)
{
// If the value was not set, use the publishig timestamp as the column to sort.
// If the value was not set, use the publishing timestamp as the column to sort.
$value ??= 'published_at'

$query->orderBy($value, $request->query('order') ?? 'asc');
Expand Down Expand Up @@ -317,21 +317,34 @@ public function all(Request $request)
// ...
])

Post::query()->refineBy(PostFilter::class, ['author_id', 'order', 'order_by'])->paginate();
return Post::query()->refineBy(PostFilter::class, ['author_id', 'order_by'])->paginate();
}
```

## Model Refiner

You may use the included `ModelRefiner` to quickly create a refiner for a model query, and automatically manage columns, relations, count, relation sum, trashed, and order.
You may use the included `ModelRefiner` to quickly create a refiner for a database query over a model. The Model Refiner simplifies automatically the following URL parameters:

- `query` to search by both primary key _or_ text contained in predetermined columns.
- `only[]` to only retrieve certain of columns.
- `has[]` to retrieve items that have at least one related model.
- `has_not[]` to retrieve items that doesn't have a related model.
- `with[]` to retrieve items including a relation or nested relation.
- `with_count[]` to include the count of the given relations.
- `with_sum[]` to include the count of the given relation column.
- `trashed` to include trashed items in the query.
- `order_by|order_by_desc` to determine which column to use for ordering.
- `limit|per_page` to limit the number of items retrieved.

### Creating a Model Refiner

Simply call the `make:refiner` with the `--model` option.

```shell
php artisan make:refiner ArticleRefiner --model
```

You will receive a refiner extending the base `ModelRefiner`. Here you should set the relations, columns, sums, and order the refiner should use to validate the URL query.
You will receive a refiner extending the base `ModelRefiner`. Here you should set the relations, columns, sums, and order the refiner should use to validate the URL parameters values. This way you can have control on which columns or relations are permitted to be set in the query.

```php
namespace App\Http\Refiners;
Expand All @@ -350,16 +363,6 @@ class ArticleRefiner extends ModelRefiner
return [];
}

/**
* Return the columns that should be removed from the query.
*
* @return string[]
*/
protected function getExceptColumns(): array
{
return [];
}

/**
* Return the relations that should exist for the query.
*
Expand All @@ -375,7 +378,7 @@ class ArticleRefiner extends ModelRefiner
*
* @return string[]
*/
protected function getMissingRelations(): array
protected function getHasNotRelations(): array
{
return [];
}
Expand Down Expand Up @@ -405,7 +408,7 @@ class ArticleRefiner extends ModelRefiner
*
* @return string[]
*/
protected function getSumRelations(): array
protected function getWithSumRelations(): array
{
// Separate the relation name using hyphen (`-`). For example, `published_posts-votes`.
return [];
Expand Down Expand Up @@ -435,12 +438,12 @@ class ArticleRefiner extends ModelRefiner
{
public function validationRules(): array
{
return Arr::only(parent::validationRules(), ['with', 'with.*', 'order', 'order_by']);
return Arr::only(parent::validationRules(), ['with', 'with.*', 'order_by']);
}

public function getKeys(Request $request): array
{
return Arr::only(parent::getKeys(), ['with', 'order', 'order_by']);
return Arr::only(parent::getKeys(), ['with', 'order_by']);
}

public function query(Builder $query, string $search): void
Expand All @@ -454,7 +457,27 @@ class ArticleRefiner extends ModelRefiner

> [!TIP]
>
> Even if you validate relations using `snake_case`, when building the query for relations, these will be automatically transformed into `camelCase`, even if these are separated by `dot.notation`.
> Even if you validate relations using `snake_case`, when building the query for relations, these will be automatically transformed into `camelCase`, even if these are separated by `dot.notation`. No need to change case.
### Full text search

By default, when receiving a string to search as "query", the Model Refiner will use an `ILIKE` operator to search inside one or many columns. This approach will work on all SQL engines.

Alternatively, you may use [PostgreSQL or MySQL full-text search capabilities](https://laravel.com/docs/11.x/queries#full-text-where-clauses) by setting `$fullTextSearch` as `true` in your Model Refiner.

```php
namespace App\Http\Refiners;

use Illuminate\Support\Arr;
use Laragear\Refine\ModelRefiner;

class ArticleRefiner extends ModelRefiner
{
protected bool $fullTextSearch = true;

// ...
}
```

### Sum relations

Expand All @@ -471,6 +494,8 @@ protected function getSumRelations(): array
}
```

The above will make calls to the `userComments()` relation of the queried model.

## Laravel Octane compatibility

- There are no singletons using a stale application instance.
Expand All @@ -479,9 +504,9 @@ protected function getSumRelations(): array
- A static property being written is the cache of Refiner methods which grows by every unique Refiner that runs.
- A static property being written is the cache of Abstract Refiner methods which is only written once.

There should be no problems using this package with Laravel Octane.
The cached Refiner methods shouldn't grow uncontrollably, unless you have dozens of Refiner classes being called multiple times. In any case, you can always flush the cached refiner methods using the `RefineQuery::flushCachedRefinerMethods()`.

If you can always flush the cached refiner methods using the `RefineQuery::flushCachedRefinerMethods()`.
There should be no problems using this package with Laravel Octane.

## Security

Expand Down
12 changes: 2 additions & 10 deletions src/Console/MakeRefinerCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,6 @@
#[AsCommand('make:refiner', 'Create a new custom Refiner class')]
class MakeRefinerCommand extends GeneratorCommand
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'make:refiner {--model: Creates a refiner for an Eloquent Model}';

/**
* The console command description.
*
Expand All @@ -40,9 +33,7 @@ class MakeRefinerCommand extends GeneratorCommand
*/
protected function getStub()
{
return $this->hasOption('model')
? $this->resolveStubPath('/stubs/model-refiner.stub')
: $this->resolveStubPath('/stubs/refiner.stub');
return $this->resolveStubPath($this->option('model') ? '/stubs/model-refiner.stub' : '/stubs/refiner.stub');
}

/**
Expand Down Expand Up @@ -72,6 +63,7 @@ protected function getDefaultNamespace($rootNamespace)
protected function getOptions()
{
return [
['model', 'm', InputOption::VALUE_NONE, 'Creates a refiner for an Eloquent Model'],
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the cast already exists'],
];
}
Expand Down
14 changes: 3 additions & 11 deletions src/Console/stubs/model-refiner.stub
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,6 @@ class {{ class }} extends ModelRefiner
return [];
}

/**
* Return the columns that should be removed from the query.
*
* @return string[]
*/
protected function getExceptColumns(): array
{
return [];
}

/**
* Return the relations that should exist for the query.
*
Expand Down Expand Up @@ -81,7 +71,9 @@ class {{ class }} extends ModelRefiner
*/
protected function getSumRelations(): array
{
// Separate the relation name using hyphen (`-`). For example, `published_posts-votes`.
// Separate the relation name using hyphen (`-`). For example, the string
// `published_posts-approved_votes` will be transformed into the code:
// `$query->withSum('PublishedPosts', 'approved_votes')`
return [];
}

Expand Down
Loading

0 comments on commit 5db112d

Please sign in to comment.