Skip to content

Commit

Permalink
Support of type hinted scope parameters (#415)
Browse files Browse the repository at this point in the history
* Add capability of resolving type hinted scope parameters

* Add test

* Update documentation

* Improve naming

* Add additional test

* Add custom exception

* Add support for non-numeric primary key models

* Update
  • Loading branch information
50bhan authored Feb 11, 2020
1 parent 100a31c commit a5694bb
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 3 deletions.
11 changes: 11 additions & 0 deletions docs/features/filtering.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,17 @@ You can even pass multiple parameters to the scope by passing a comma separated
GET /events?filter[starts_between]=2018-01-01,2018-12-31
```

When using scopes, for more convenient you can also use type hint parameters to automatically inject the model instances into your scope:

```php
public function scopeEvent(Builder $query, Event $event): Builder
{
return $query->where('id', '=', $event->id);
}

// GET /events?filter[event]=1
```

Scopes are usually not named with query filters in mind. Use [filter aliases](#filter-aliases) to alias them to something more appropriate:

```php
Expand Down
13 changes: 13 additions & 0 deletions src/Exceptions/InvalidFilterValue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Spatie\QueryBuilder\Exceptions;

use Exception;

class InvalidFilterValue extends Exception
{
public static function make($value)
{
return new static("Filter value `{$value}` is invalid.");
}
}
37 changes: 34 additions & 3 deletions src/Filters/FiltersScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,48 @@
namespace Spatie\QueryBuilder\Filters;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use ReflectionObject;
use Spatie\QueryBuilder\Exceptions\InvalidFilterValue;

class FiltersScope implements Filter
{
public function __invoke(Builder $query, $values, string $property)
public function __invoke(Builder $query, $values, string $property): Builder
{
$scope = Str::camel($property);

$values = Arr::wrap($values);
$values = array_values(Arr::wrap($values));
$values = $this->resolveParameters($query, $values, $scope);

$query->$scope(...array_values($values));
return $query->$scope(...$values);
}

protected function resolveParameters(Builder $query, $values, string $scope): array
{
$parameters = (new ReflectionObject($query->getModel()))
->getMethod('scope'.ucfirst($scope))
->getParameters();

foreach ($parameters as $parameter) {
if (! optional($parameter->getClass())->isSubclassOf(Model::class)) {
continue;
}

$model = $parameter->getClass()->newInstance();
$index = $parameter->getPosition() - 1;
$value = $values[$index];

$result = $model->resolveRouteBinding($value);

if ($result === null) {
throw InvalidFilterValue::make($value);
}

$values[$index] = $result;
}

return $values;
}
}
26 changes: 26 additions & 0 deletions tests/FilterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,32 @@ public function it_can_filter_results_by_scope()
$this->assertCount(1, $modelsResult);
}

/** @test */
public function it_can_filter_results_by_type_hinted_scope()
{
TestModel::create(['name' => 'John Testing Doe']);

$modelsResult = $this
->createQueryFromFilterRequest(['user' => 1])
->allowedFilters(AllowedFilter::scope('user'))
->get();

$this->assertCount(1, $modelsResult);
}

/** @test */
public function it_can_filter_results_by_regular_and_type_hinted_scope()
{
TestModel::create(['id'=> 1000, 'name' => 'John Testing Doe']);

$modelsResult = $this
->createQueryFromFilterRequest(['user_info' => ['id' => '1000', 'name' => 'John Testing Doe']])
->allowedFilters(AllowedFilter::scope('user_info'))
->get();

$this->assertCount(1, $modelsResult);
}

/** @test */
public function it_can_filter_results_by_scope_with_multiple_parameters()
{
Expand Down
12 changes: 12 additions & 0 deletions tests/TestClasses/Models/TestModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ public function scopeNamed(Builder $query, string $name): Builder
return $query->where('name', $name);
}

public function scopeUser(Builder $query, self $user): Builder
{
return $query->where('id', $user->id);
}

public function scopeUserInfo(Builder $query, self $user, string $name): Builder
{
return $query
->where('id', $user->id)
->where('name', $name);
}

public function scopeCreatedBetween(Builder $query, $from, $to): Builder
{
return $query->whereBetween('created_at', [
Expand Down

0 comments on commit a5694bb

Please sign in to comment.