diff --git a/src/Http/Controllers/BaseRestfulController.php b/src/Http/Controllers/BaseRestfulController.php index a02f492..0bda9ef 100644 --- a/src/Http/Controllers/BaseRestfulController.php +++ b/src/Http/Controllers/BaseRestfulController.php @@ -39,6 +39,11 @@ class BaseRestfulController extends Controller */ public static $transformer = null; + /** + * @var array|string|\Spatie\QueryBuilder\Sorts\Sort + */ + public static $allowedSorts = null; + /** * RestfulController constructor. * diff --git a/src/Http/Controllers/RestfulController.php b/src/Http/Controllers/RestfulController.php index 9a79536..9121c0f 100644 --- a/src/Http/Controllers/RestfulController.php +++ b/src/Http/Controllers/RestfulController.php @@ -4,6 +4,7 @@ use Illuminate\Database\QueryException; use Illuminate\Http\Request; +use Spatie\QueryBuilder\QueryBuilder; use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Cache; @@ -47,9 +48,17 @@ public function getAll() }), $this->getTransformer()); } - $query = $model::with($model::getCollectionWith()); + $query = QueryBuilder::for($model::with($model::getCollectionWith()), Request()); $this->qualifyCollectionQuery($query); + if ($sorts = $model::getAllowedSorts()) { + $query = $query->allowedSorts($sorts); + } + + if ($filters = $model::getAllowedFilters()) { + $query = $query->allowedFilters($filters); + } + // Handle pagination, if applicable $perPage = $model->getPerPage(); if ($perPage) { @@ -58,7 +67,7 @@ public function getAll() $perPage = intval(request()->input('per_page')); } - $paginator = $query->paginate($perPage); + $paginator = $query->paginate($perPage)->appends(request()->only(['filter', 'sort'])); return $this->response->paginator($paginator, $this->getTransformer()); } else { diff --git a/src/Models/RestfulModel.php b/src/Models/RestfulModel.php index a489b60..c762f9a 100644 --- a/src/Models/RestfulModel.php +++ b/src/Models/RestfulModel.php @@ -68,6 +68,10 @@ class RestfulModel extends Model */ public static $transformer = null; + public static $allowedSorts = null; + + public static $allowedFilters = null; + /** * Return the validation rules for this model * @@ -201,6 +205,20 @@ public static function getCollectionWith() } } + public static function getAllowedSorts() + { + if (! is_null(static::$allowedSorts)) { + return static::$allowedSorts; + } + } + + public static function getAllowedFilters() + { + if (! is_null(static::$allowedFilters)) { + return static::$allowedFilters; + } + } + /************************************************************ * Extending Laravel Functions Below ***********************************************************/ diff --git a/test/app/Models/User.php b/test/app/Models/User.php index b52ba00..cc01baa 100644 --- a/test/app/Models/User.php +++ b/test/app/Models/User.php @@ -31,6 +31,16 @@ class User extends BaseModel implements */ public static $localWith = ['primaryRole', 'roles']; + /** + * @var array Sortable attributes + */ + public static $allowedSorts = ['name']; + + /** + * @var array attribute filters + */ + public static $allowedFilters = ['email']; + /** * The attributes that are mass assignable. * diff --git a/test/config/query-builder.php b/test/config/query-builder.php new file mode 100644 index 0000000..b444354 --- /dev/null +++ b/test/config/query-builder.php @@ -0,0 +1,29 @@ + [ + 'include' => 'include', + + 'filter' => 'filter', + + 'sort' => 'sort', + + 'fields' => 'fields', + + 'append' => 'append', + ], + + /* + * Related model counts are included using the relationship name suffixed with this string. + * For example: GET /users?include=postsCount + */ + 'count_suffix' => 'Count', + +]; diff --git a/test/copy-over-deps.php b/test/copy-over-deps.php index 25fd717..5e256c0 100755 --- a/test/copy-over-deps.php +++ b/test/copy-over-deps.php @@ -3,7 +3,7 @@ $sourceDir = '/www/laravel/bp-demo'; $migrationsDir = './database/migrations'; -$configsToCopy = ['api.php', 'auth.php', 'jwt.php']; +$configsToCopy = ['api.php', 'auth.php', 'jwt.php', 'query-builder.php']; // Copy over database dir echo `rm -rf ./database/*`; diff --git a/test/tests/Api/Get/GetTest.php b/test/tests/Api/Get/GetTest.php index 6a29e3c..98a805c 100644 --- a/test/tests/Api/Get/GetTest.php +++ b/test/tests/Api/Get/GetTest.php @@ -22,6 +22,10 @@ public function testGet() $jsonResponse->assertStatus(200); + $jsonResponse = $this->actingAsAdmin() + ->json('GET', '/users?sort=-name&filter[email]=admin.com'); + + $jsonResponse->assertStatus(200); } } diff --git a/test/tests/SetupTestApp.php b/test/tests/SetupTestApp.php index 218ebc5..391c8d6 100644 --- a/test/tests/SetupTestApp.php +++ b/test/tests/SetupTestApp.php @@ -58,6 +58,7 @@ protected function getEnvironmentSetUp($app) $app['config']->set('api', include __DIR__ . '/../config/api.php'); $app['config']->set('auth', include __DIR__ . '/../config/auth.php'); $app['config']->set('jwt', include __DIR__ . '/../config/jwt.php'); + $app['config']->set('query-builder', include __DIR__ . '/../config/query-builder.php'); } /** @@ -74,6 +75,7 @@ protected function getPackageProviders($app) \Specialtactics\L5Api\L5ApiServiceProvider::class, \Specialtactics\L5Api\Test\Mocks\AppServiceProvider::class, \Specialtactics\L5Api\Test\Mocks\RouteServiceProvider::class, + \Spatie\QueryBuilder\QueryBuilderServiceProvider::class, ]; }