Creating a ReST API¶
This section covers how to make a ReST API from scratch and consume it in any App (Web or Mobile).
If you're not familiar with ReST concepts, see this great explanation.
Prerequisites¶
First, you'll need a working setup of equal-framework on your localhost. See Getting started > Quick start (skip point 3. Defining classes)
If you look at the Directory structure, you'll get a good idea of what comes with every project. In this one, we'll use actions, classes, and data.
Each of those folders correspond to a specific role :
/actions= method DO (post, put/patch, delete, ...)/data= method GET (fetching data from server)/classes= class definition & DB architecture
Go ahead and create them in your package directory (ex: /packages/todolist/)
Defining classes¶
Inside folder /classes create a new PHP file for each class you want to declare.
To continue with our previous todolist-app example, we'll create two classes: Task and User.
/classes/Task.class.php¶
<?php
namespace todolist;
use equal\orm\Model;
class Task extends Model {
public static function getColumns() {
return [
'title' => [
'type' => 'string'
],
'content' => [
'type' => 'string'
],
'deadline' => [
'type' => 'datetime'
],
'user_id' => [
'type' => 'many2one',
'foreign_object' => 'todolist\User'
]
];
}
}
/classes/User.class.php¶
<?php
namespace todolist;
class User extends \core\User {
public static function getColumns() {
return [
'name' => [
'type' => 'string'
],
'tasks_ids' => [
'type' => 'one2many',
'foreign_object' => 'todolist\Task',
'foreign_field' => 'user_id'
]
];
}
}
We're done! A few things to consider:
- In order to work, your parameters have to match with your front-end.
- Objects' IDs aren't needed here; they're automatically generated by eQual.
-
As you can see, we're using a DBMS relationship, linking
user_idwithtasks_ids. This is also handled by the framework; all you have to know is the definition syntax). -
In order to work, your parameters have to match with your front-end
-
Objects'IDs aren't needed here, they're automatically generated by eQual
-
As you can see we're using a DBMS relationship, linking
user_idwithtasks_ids. This is also handled by the framework, all you have to know is the definition syntax.
Defining GET¶
Open /data and create a new file for each GET method you want to use.
In the following example, we're asking our database to retrieve all tasks :
/data/tasks.php¶
<?php
use todolist\Task;
list($params, $providers) = announce([
'description' => 'Retrieve the list of existing tasks',
'params' => [],
'response' => [
'content-type' => 'application/json',
'charset' => 'utf-8',
'accept-origin' => ['*']
],
'providers' => ['context']
]);
list($context) = [ $providers['context'] ];
$list = Task::search([])
->read(['id', 'title', 'content', 'deadline', 'user_id' => ['id', 'name']])
->adapt('txt')
->get(true);
$context->httpResponse()
->body($list)
->send();
What it does :¶
announce() will handle the values of our query :
descriptionparamsgives additional requirements and conditionsresponsedefines the format of the server responseprovidershelps us to access useful services such ascontext,orm,auth
We use list($context) = [$providers['context']] to implement the services we want to use
Then $list is where we receive the data from our query :
Task::searchsearches for data associated with Task
Function defined inside the Collection class;
-
read([])** tells which parameters we want to retrieve from Task -
->adapt('txt)turns the data into strings -
->get(true)
Finally, $context is used to accomplish REST's purpose, displaying the data on our browser as JSON
<?php
$context
->httpResponse()// get the HTTP response being built
->body($list) // populate the body with resulting list
->send(); // output the response (i.e. some plain
// text @see https://www.w3.org/Protocols/rfc2616)
At this point, if you go to http://equal.local/index.php?get=todolist_tasks, you should see a JSON-formatted answer showing an empty array of tasks. To populate it, you can use the DO methods we will define in the next section.
Using GET in your browser¶
We'll assume we already have an existing database containing a data sample of tasks, and have already done the following:
$> php run.php --do=test_package-consistency --package=todolist
$> php run.php --do=init_package --package=todolist
Open your browser, and in the localhost page you defined for eQual.
To the address bar, add this:
?get=todolist_tasks
Tasks refers to our tasks.php located in /data.
It will display a JSON-formatted answer showing an array of tasks.
As easy as that. You now have a REST response that you can use in any frontend project.
Defining DO¶
In the /actions folder, create a sub-folder for each previously defined class, as follows:
/public
/packages
/todolist
/actions
/task
/user
...
Now let's define the CREATE function of our API:
/actions/task/create.php¶
<?php
use todolist\Task;
use todolist\User;
list($params, $providers) = announce([
'params' => [
'title' => [
'type' => 'string',
'required' => true
],
'content' => [
'type' => 'string',
'required' => true
],
'deadline' => [
'type' => 'datetime',
'default' => date("Y-m-d H:i:s")
],
'user_id' => [
'type' => 'integer',
'required' => true
]
],
'response' => [
'content-type' => 'application/json',
'charset' => 'utf-8',
'accept-origin' => ['http://localhost:4200', '*']
],
'providers' => ['context']
]);
list($context) = [$providers['context']];
$user = User::id($params['user_id'])->read(['id'])->first();
if(is_null($user)) {
throw new Exception('user_not_found', EQ_ERROR_UNKNOWN_OBJECT);
}
$task = Task::create($params)
->read(['id', 'title', 'content', 'deadline', 'user_id' => ['id', 'name']])
->first(true);
$context->httpResponse()
->status(201)
->body($task)
->send();
What it does:¶
params defines the properties of our class Task (title, content, deadline, user_id). Some of them are declared with a required field, which will make them mandatory (or not if set to false, which is the default behavior).
useris used to retrieve the ID of a single user, based on the fielduser_id.taskis then used with the::create()method to initiate a POST request.
Finally, we use $context to send it and get a REST response.
Using DO in your browser¶
DO: CREATE
http://equal.local/index.php?do=todolist_task_create&title=my+task&content=lorem+ipsum&user_id=1
eQual resolves the operation ?do=todolist_task_create to the script /todolist/actions/task/create.php.
After CREATE, let's implement PUT and DELETE.
We'll do that with no further explanation, as you should now be familiar with how it works.
/actions/task/update.php¶
<?php
use todolist\Task;
use todolist\User;
list($params, $providers) = announce([
'params' => [
'id' => [
'type' => 'integer',
'required' => true
],
'title' => [
'type' => 'string',
'required' => true
],
'content' => [
'type' => 'string',
'required' => true
],
'deadline' => [
'type' => 'datetime',
'default' => date("Y-m-d H:i:s")
],
'user_id' => [
'type' => 'integer',
'required' => true
]
],
'response' => [
'content-type' => 'application/json',
'charset' => 'utf-8',
'accept-origin' => ['http://localhost:4200', '*']
],
'providers' => ['context']
]);
list($context) = [$providers['context']];
$user = User::id($params['user_id'])->read(['id'])->first();
if(is_null($user)) {
throw new Exception('user_not_found', EQ_ERROR_UNKNOWN_OBJECT);
}
$task = Task::ids($params['id'])
->update($params)
->read(['id', 'title', 'content', 'deadline', 'user_id' => ['id', 'name']])
->first(true);
$context->httpResponse()
->status(201)
->body($task)
->send();
In practice:
DO: PUT/PATCH
http://equal.local/index.php?do=todolist_task_update&id=1
idrefers to the task we update.- Additional parameters can be used, like title, content, deadline, and user_id.
/actions/task/delete.php¶
<?php
use todolist\Task;
use todolist\User;
list($params, $providers) = announce([
'params' => [
'id' => [
'type' => 'integer',
'required' => true
]
],
'response' => [
'content-type' => 'application/json',
'charset' => 'utf-8',
'accept-origin' => ['http://localhost:4200', '*']
],
'providers' => ['context']
]);
list($context) = [$providers['context']];
Task::ids($params['id'])->delete(true);
$context->httpResponse()
->status(204)
->send();
In practice:
DO: DELETE
http://equal.local/index.php?do=todolist_task_delete&id=1
id=1refers to the task we delete.
Mapping the ReST API routes¶
Back to the root folder of the equal installation, create a file: `/config/routing/20-api_todolist.json.
Replace everything with this:
{
"/tasks": {
"GET": {
"description": "get all tasks",
"operation": "?get=todolist_tasks"
}
},
"/task": {
"POST": {
"description": "get all tasks",
"operation": "?do=todolist_task_create"
}
},
"/task/:id" : {
"PUT": {
"description": "update a task",
"operation": "?do=todolist_task_update"
},
"DELETE": {
"description": "delete a task",
"operation": "?do=todolist_task_delete"
}
}
}
What it does is pretty self-explanatory. The /:id is a way for us to target and retrieve a single task when needed.
From now on, the route http://equal.local/tasks is equivalent to calling http://equal.local/?get=todolist_tasks.