Introduction¶
zcwilt/rest-api is written for Laravel and provides a query parser and api controllers for CRUD type actions.
The query parser allows for complex filtering and sorting, converting the URI query into eloquent queries.
The API controller supports resource creation, reading, updating and deletion.
Reading, updating and deletion can all access the query parser.
e.g.
delete ?whereIn[]=id:(1,2)
update ?whereBetween=age:30:60 @todo
although note that the filtering can be more complex than just a simple where
Some examples of filtering/Sorting etc
{api-uri}?where[]=id:eq:2
{api-uri}?whereIn[]=id:(1,2)
{api-uri}?sort[]=id,-name
Query Parser¶
The query parser allows for complex filtering, sorting, the use of child relations and more.
Currently the filter parser supports
- where
- orWhere
- whereIn
- orWhereIn
- whereNotIn
- orWhereNotIn
- whereBetween
- whereNotBetween
- orWhereBetween
- orWhereNotBetween
- withTrashed
- onlyTrashed
- scope
Sorting allows for multiple sort targets for ascending and descending sorts.
Includes allow for loading child models.
Joins are also supported
Query results by default return all columns for the query, however you can use the columns filter to restrict which columns are returned.
Query Parser Filtering¶
Simple Where Clauses¶
Simple where clauses take the format of
{api-uri}?where[]=fieldname:operator:value
for example
{api-uri}?where[]=id:eq:1
would equate to an eloquent query of
model::where('id', '=', 1)
The operators allowed are
- eq equates to =
- noteq equates to !=
- lte equates to <=
- gte equates to >=
- gt equates to >
- lt equates to <
- lk equates to LIKE
- nlk equates to NOT LIKE
Can also use orWhere
{api-uri}?orWhere[]=id:eq:1
Where In clauses¶
Where In clauses take the form of
{api-uri}?whereIn[]=fieldname:(comma separate list)
For example
{api-uri}?whereIn[]=id:(1,2,3)
Can also use
- orWhereIn
- whereNotIn
- orWhereNotIn
Where Between Clauses¶
Where Between clauses take the form of
{api-uri}?whereBetween[]=fieldname:start:end
For example
{api-uri}?whereBetween[]=age:18:45
Can also use
- orWhereBetween
- whereNotBetween
- orWhereNotBetween
Query Parser Soft Deletes¶
For models that support soft deletes, the query parser also provides 2 further filters.
Only Trashed¶
To show only entries for the model that have been soft deleted use
{api-uri}?onlyTrashed
Note
Using withTrashed
or onlyTrashed
on models that do not support soft deletes
will result in an exception, with the error message being returned in the Json response.
e.g.
{"error":{"message":"Model does not support soft deletes","status_code":400}}
Warning
There is currently no way to force delete soft deleted items. This is on the todo list.
Query Parser Sorting¶
example
{api-uri}?sort[]=id,-name
would sort ascending on id then sort descending on name
Query Parser Columns¶
example
{api-uri}?columns[]=id,name
will restrict the returned columns to just id and name.
Query Parser With¶
Warning
This feature is a bit experimental at the moment. In terms of testing i’ve only tried with a simple one to many relationship. e.g. user->posts
.
example
{api-uri}?with[]=posts
The above assumes the query is being done on a model that has a relationship defined,
and uses Laravel Querbuilder with
method.
Query Parser Joins¶
Example
{api-uri}?join[]=joinType:tableName:leftKey:rightKey
The join clause takes 4 parameters
- joinType - can be 1 of
inner
,left
orcross
- tableName - the table to join on
- leftkey - the table field used on the left side of the Join on clause
- rightKey - the table field used on the right side of the Join on clause
More examples
{api-uri}?join[]=inner:posts:posts.user_id:users.id
same as
$model->join('posts', 'posts.user_id', '=', 'users.id', 'inner');
Query Parser Scopes¶
Warning
This feature is a bit experimental at the moment.
example
{api-uri}?scope[]=myscope
Allows the use of Laravel scopes
the scope parameter should be the name of your scope less the preceding scope
e.g. If your scope is called scopeActive then you would just use active
URL Parameter Format¶
Most examples you will see in the documentation show URL parameters in a simple format
e.g {api-uri}?columns=id,name&where=id:eq:1
However this format will break if you need to include multiple copies of a parser
consider this
{api-uri}?columns=id,name&where=id:eq:1&orWhereBetween=age:(10,15)&orWhereBetween=age:(50,60)
The above query will not work as the PHP/Laravel request will only be able to choose one of the orWhereBetween clauses.
In these cases you will need to use the standard bracket notation for GET URLS
e.g.
{api-uri}?columns=id,name&where=id:eq:1&orWhereBetween[]=age:(10,15)&orWhereBetween[]=age:(50,60)
Note
You only need to use bracket notation e.g. &orWhereBetween[]=age:(10,15)
if you have multiple parsers of a certain type.
However for simplicity and consistency, we would suggest using bracket notation all the time.
API Controllers¶
Routes¶
zcwilt/rest-api uses resource controllers. To define your routes for each controller you want, you will need to add the following to your api routes file.
Route::resource('modelName', 'controller');
Route::delete('modelName', 'controller@destroyByQuery');
Route::put('modelName', 'controller@updateByQuery');
modelName
is the model name you want to have api access.
Controller
is the name of your controller class.
As an example. To use the dummy simple controller supplied by the project, your routing entries would be.
Route::resource('dummySimple', 'Api\DummySimpleController');
Route::delete('dummySimple', 'Api\DummySimpleController@destroyByQuery');
Route::put('dummySimple', 'Api\DummySimpleController@updateByQuery');
or to use the default User model that comes with Laravel
Route::resource('user', 'Api\UserController');
Route::delete('user', 'Api\UserController@destroyByQuery');
Route::put('user', 'Api\UserController@updateByQuery');
For each Laravel Model that you want to use in the API you will need to create a Controller
Controller Definition¶
As mentioned above, for each Laravel model that you want to provide API access to you will need to create a Controller.
This should be placed in the standard laravel location e.g. App/Http/Controllers or a sub directory. Our suggestion is to use App/Http/Controllers/Api
The controller definition is fairly simple
<?php
namespace App\Http\Controllers\Api;
use Zcwilt\Api\Controllers\ApiController;
class DummySimpleController extends ApiController
{
protected $modelName = '\\Zcwilt\Api\\Models\\DummySimple';
}
Note
The protected $modelName
defines the Eloquent Model that will be used by the controller. The factory class used will try and resolve the model
from either your projects App folder or from the App/Models folder, If the Model is in one of these folders there is no need to namespace the model name. e.g. you could just do
protected $modelName = 'ModelName';
Validation¶
The base ApiController class that your controller extends contains calls to Laravels Validation system.
To use validation on your api request you must create a public rules
method on
the model your controller accesses.
e.g.
public function rules($id = 0)
{
return [
'email' => 'required|unique:zcwilt_users'.($id ? ",email,$id" : ''),
'name' => 'required'
];
}
Note
For update methods, the primary key value is passed as the $id
parameter.
Warning
The controller uses $request->all()
to pass request fields
to the model update/create methods. This could allow malicious users to update
database fields that you did not want. You must therefore be very explicit
in your models as to which fields can be used in this way. e.g. using the $fillable
property.
Api Endpoints¶
The api endpoints provided by the resource controller and extra controller methods provide the following route/actions
GET api/modelname -> controller@index : allows for query filtering
GET api/modelname/{id} -> controller@show
POST api/modelname -> controller@store
The request body should be an array of field/values
e.g ['name' => 'foo', 'email' => 'bar@test.com']
PUT api/modelname/{id} -> controller@update
The request body should be an array of field/values
e.g ['name' => 'foo', 'email' => 'bar@test.com']
PUT api/modelname -> controller@updateByQuery : allows for query filtering
The request body should be an array of parser clauses however field name/values
should be set in a fields array
e.g ['where' => 'status:eq:1', 'fields' => [name' => 'foo', 'email' => 'bar@test.com']]
DELETE api/modelname/{id} - controller@destroy
DELETE api/modelname - controller@destroyByQuery : allows for query filtering
The request body should be an array of parser clauses
e.g ['where' => ''status:eq:1]
Pagination¶
All results from the index
route are paginated using the standard Laravel paginator
Therefore you can add a page
and per_page
parameter to those queries.
You can also return all results by adding paginate=no
to the query string.
Exception Handling¶
@todo
Authorization¶
This project/code is agnostic as to how you provide authorization for your API.
It’s expected that you may need to extend the Api Controller to provide authorization and/or role/scope based access.
There will be some more documentation here regarding Authorizartion etc
Testing¶
Tests can be run within the package
vendor/bin/phpunit
There is also a phpunit xml file for running tests with code coverage
vendor/bin/phpunit -c phpunit-cc.xml
Note
To run the code coverage tests, you will need to install xdebug
Contributing¶
See the - Contributing File
Acknowledgements¶
This project would not exist without the work of others.
Thanks go to
From our composer.json
The following projects also provided inspiration.
Thanks also to the guys from Reddit who provided feedback
Todo¶
- Allow for force deleting entries
- Document Exception Handling
- Update demo site with auth examples