Appearance
Your own API
The module comes with APIs for entries, members, comments and categories. Missing something? Let us know: maybe it is useful for more sites and it becomes part of the module. You can also build your own API in your own add-on, with the same authentication, permissions, logging, caching and rate limiting as the APIs of the module. The AbstractApi helpers and the route parameters are available from v10.0.0
How it works
The module looks in every add-on in system/user/addons for a webservice_settings.json. The endpoints of that file are added to the API, under /webservice/<name>, and show up in the endpoints overview, the API choice of the members, the testing tool and the OpenAPI specification. The add-on does not need to be installed.
A call is handled by a static method of the class Reinos_webservice_<name>_static in libraries/reinos_webservice_<name>_static.php of your add-on. The method gets the data of the request and returns an array, the module turns it into the JSON response.
Boilerplate
An API news with a list and a single item:
system/user/addons/my_webservice_api/
├── addon.setup.php
├── webservice_settings.json
└── libraries/
└── reinos_webservice_news_static.phpaddon.setup.php
php
<?php
return [
'author' => 'Your name',
'author_url' => 'https://example.com',
'name' => 'My Webservice API',
'description' => 'An own API for the Webservice module',
'version' => '1.0.0',
'namespace' => 'MyCompany\MyWebserviceApi',
];webservice_settings.json
json
{
"name": "news",
"label": "News",
"auth": true,
"public": false,
"enabled": true,
"version": "1.0.0",
"methods": [
{
"route": "/",
"http_method": "GET",
"method_name": "read_news",
"name": "Read the latest news",
"cachable": true,
"data_required": false
},
{
"route": "/{entry_id}",
"http_method": "GET",
"method_name": "read_news_item",
"name": "Read one news item",
"cachable": true
}
],
"test": {
"enabled": true,
"read_news": [
{
"name": "limit",
"type": "form_input",
"value": "10"
}
]
}
}libraries/reinos_webservice_news_static.php
php
<?php
use Reinos\Webservice\Api\AbstractApi;
/**
* The entry points of the News API: the Webservice module calls these static methods with the data of the request
* The name is reinos_webservice_<name>_static, <name> is the name in webservice_settings.json
*/
class Reinos_webservice_news_static
{
public static function read_news($data)
{
return (new My_news_api())->read_news($data);
}
public static function read_news_item($data)
{
return (new My_news_api())->read_news_item($data);
}
}
/**
* The News API, with the helpers of the Webservice module: permissions, paging, sorting and the responses
*/
class My_news_api extends AbstractApi
{
/**
* The latest open entries of the news channel
*
* @param array $data limit, offset, orderby, sort
* @return array
*/
public function read_news($data = array())
{
$query = ee('Model')->get('ChannelEntry')
->with('Channel')
->filter('Channel.channel_name', 'news')
->filter('site_id', $this->siteId())
->filter('status', 'open');
$this->applySort($query, $data, array('entry_date', 'title'), 'entry_date');
$metadata = $this->applyPaging($query, $data);
$news = array();
foreach($query->all() as $entry)
{
$news[] = $this->output($entry);
}
// other add-ons can change the result with the webservice_read_news_end hook
$news = $this->hook('read_news_end', $news);
return $this->success($news, 'Successfully read', $metadata);
}
/**
* One news item
*
* @param array $data entry_id
* @return array
*/
public function read_news_item($data = array())
{
$entry = empty($data['entry_id']) ? null : ee('Model')->get('ChannelEntry', (int) $data['entry_id'])
->with('Channel')
->filter('Channel.channel_name', 'news')
->filter('status', 'open')
->first();
if(!$entry)
{
return $this->error('The news item does not exist', 404);
}
return $this->success(array($this->output($entry)));
}
/**
* The output of a news item
*
* @param object $entry
* @return array
*/
private function output($entry)
{
return array(
'entry_id' => (int) $entry->entry_id,
'title' => $entry->title,
'url_title' => $entry->url_title,
'entry_date' => (int) $entry->entry_date,
);
}
}Then enable the API for a member and call it:
bash
curl -G 'https://your-domain.com/index.php/webservice/news' \
-H 'Authorization: Bearer <shortkey>' \
--data-urlencode 'data[limit]=5'
curl 'https://your-domain.com/index.php/webservice/news/12' \
-H 'Authorization: Bearer <shortkey>'webservice_settings.json
| Key | Description |
|---|---|
name | The name of the API: the first segment of the url (/webservice/news) and the <name> of the class. Letters, numbers and _ |
label | The name in the control panel |
auth | true: the member needs credentials and the API enabled on the member page. false: the API is always public |
enabled | false to switch the API off |
version | The version of your API, shown in the control panel |
methods | The endpoints, see below |
test | The fields of the testing tool per method: form_input, form_textarea, form_password or form_dropdown. {"disabled": "reason"} shows a method without testing it |
Per method:
| Key | Description |
|---|---|
route | The path after the name of the API, / is /webservice/news. A parameter like /{entry_id} is added to the data |
http_method | GET, POST, PUT, PATCH or DELETE, more than one with a pipe: PUT|PATCH |
method_name | The static method that handles the call |
name | The description in the control panel |
cachable | true: the response can be cached (only for endpoints that do not change data) |
clear_cache | true: the call clears the cache, for endpoints that change data |
data_required | false: the endpoint can be called without data. Default: true, a call without data gets a 400 |
The endpoints of your API can be enabled, disabled and made public in the endpoints overview, like the endpoints of the module.
The AbstractApi helpers
Extend Reinos\Webservice\Api\AbstractApi to use the helpers of the module:
| Helper | Description |
|---|---|
$this->success($data, $message, $metadata) | A successful response, $data is an array of items |
$this->error($message, $http_code, $errors) | An error response with an HTTP status and optional errors per field |
$this->validationError($result) | A 422 with the errors per field of a failed model validation ($model->validate()) |
$this->can('edit_channels') | Has the member the ExpressionEngine permission (without can_), a Super Admin always |
$this->isSuperAdmin() | Is the member a Super Admin, in one of its roles |
$this->siteId() | The site of the call (data[site_id], default 1) |
$this->only($data, $keys) | Only the allowed keys of the data |
$this->ids($value) | A list of ids from an array or a comma separated string |
$this->applySort($query, $data, $columns, $default) | Sort a model query with data[orderby] (only the given columns) and data[sort] |
$this->applyPaging($query, $data) | Page a model query with data[limit] (default 25, maximum 100) and data[offset], returns the metadata |
$this->hook($name, $data) | Call the webservice_<name> hook, returns the data of the extensions |
$this->logAction($message) | A line in the control panel log of ExpressionEngine |
You can also return a plain array, the keys success, message, metadata and data are the response, http_code sets the HTTP status.
TIP
Use the models of ExpressionEngine (ee('Model')) and check the permissions of the member yourself: the module only checks that the member may use your API.
