For a recent project, i was asked to build a service that would consume the client's internal data from his API. I got lucky with this because it was a very well structured and documented API. The only thing that i had not tried before was to use the new Drupal::httpClient() functionality in combination with a raw body post of (form-params) data (it was really hard - for me - to find a well written tutorial for this). The API could be used as a resource for getting POIs (Point of Interest) but the admin could also "create - post" pois in demand from his back office.
This is the working custom module awesome_api_client with all the necessary files in order to work right.
So first we need a .info.yml file for the module
name: 'Awesome Api'
type: module
description: 'A REST API custom module for retrieving and updating data through the Awesome API'
core: 8.x
package: 'Pixelthis'
Then we need a .services.yml file, for registering the service.
services:
awesome_client:
class: Drupal\awesome_api\AwesomeClient
arguments:
- '@http_client_factory'
in awesome.module file i use hook_cron for registering the API - GET resources call in recursive 15' cron tasks
awesome.module
in web/custom/modules/awesome_api/src/AwesomeClient.php i added the actual PHP Client Class Service
client = $http_client_factory->fromOptions([
'base_uri' => 'http://awesome_api_ip:15333/',
// 'headers' => [
// 'Content-Type' => 'application/json',
// ],
]);
}
catch (RequestException $e) {
watchdog_exception('awesome_module', $e);
}
}
/**
* Get all available pois
*
* @param int $amount
*
* @return array
*/
public function pois() {
try {
$response = $this->client->get('pois', []);
$data = Json::decode($response->getBody());
return $data;
}
catch (RequestException $e) {
watchdog_exception('awesome_module', $e);
}
}
/**
* Get all available pois
*
* @param int $amount
*
* @return array
*/
public function poi(int $id) {
try {
$response = $this->client->get('poi/'.$id);
$data = Json::decode($response->getBody());
return $data;
}
catch (RequestException $e) {
watchdog_exception('awesome_module', $e);
}
}
/**
* Add a new Poi from website to API
*
* @param array $api_data
*
* @return object
*/
public function add_api_poi(array $api_data) {
try {
$headers = ['Content-Type' => 'application/json'];
$options = [
'body' => Json::encode($api_data),
'headers' => $headers,
];
return $response;
}
catch (RequestException $e) {
watchdog_exception('awesome_module', $e);
}
}
/**
* Get all available poi crowdiness stats
*
* @param int $id
*
* @return array
*/
public function poi_crowdiness(int $id,$timestamp_start = '2020-01-01 00:00:00',$timestamp_end = '2020-12-01 00:00:00') {
try {
$options = [
'form_params' => [
//%Y-%m-%d %H:%M:%S
'timestamp_start' => $timestamp_start,
'timestamp_end' => $timestamp_end,
],
];
$response = $this->client->get('poi/'.$id.'/stats',$options);
$data = Json::decode($response->getBody());
return $data;
}
catch (RequestException $e) {
watchdog_exception('awesome_module', $e);
}
}
public function manage_poi($poi_api_data){
$poi_exists_id = $this->poi_exists_to_db($poi_api_data['id']);
if (!$poi_exists_id){
$this->add_poi_to_db($poi_api_data);
}
else {
$this->update_poi_to_db($poi_api_data,$poi_exists_id);
}
}
private function poi_exists_to_db($poi_api_id){
$query = \Drupal::entityQuery('node')
->condition('type', 'poi')
->condition('field_api_id', $poi_api_id);
$nids = $query->execute();
if (!empty($nids)){
return current($nids);
}
return false;
}
private function update_poi_to_db($poi_api_data,$id){
$node = Node::load($id);
if ($node instanceof NodeInterface) {
try {
$lat_lng = explode(',',$poi_api_data['geo_data']);
$node->set('title', $poi_api_data['name']);
$node->set('field_max_poi_population', $poi_api_data['max_poi_population']);
$node->set('field_poi_type_ref',$this->manage_term($poi_api_data['type'],'poi_type'));
$node->set('field_poi_address',$poi_api_data['address']);
$node->set('field_poi_category_ref',$this->manage_term($poi_api_data['category'],'poi_category'));
$node->set('field_geo_point',['lat' => trim($lat_lng[0]), 'lng' => trim($lat_lng[1])]);
$node->save();
}
catch (\Exception $e) {
watchdog_exception('Poi API ERROR UPDATE', $e);
}
}
}
private function add_poi_to_db($poi_api_data){
// 'address' => string(12) "Ormos Thiras"
// 'category' => string(4) "Port"
// 'geo_data' => string(20) "36.386122, 25.428413"
// 'id' => integer1
// 'max_poi_population' => integer2500
// 'name' => string(14) "Santorini Port"
// 'type' => string(20) "terminal_station_poi"
$lat_lng = explode(',',$poi_api_data['geo_data']);
$node = Node::create([
'type' => 'poi',
'title' => $poi_api_data['name'],
'field_api_id' => $poi_api_data['id'],
'field_geo_point' => ['lat' => trim($lat_lng[0]), 'lng' => trim($lat_lng[1])],
'field_max_poi_population' => $poi_api_data['max_poi_population'],
'field_poi_type_ref' => $this->manage_term($poi_api_data['type'],'poi_type'),
'field_poi_address' => $poi_api_data['address'],
'field_poi_category_ref' => $this->manage_term($poi_api_data['category'],'poi_category'),
]);
$node->save();
}
private function manage_term($term_name,$vid){
$term = \Drupal::entityTypeManager()->getStorage('taxonomy_term')
->loadByProperties(['name' => $term_name, 'vid' => $vid]);
$term = reset($term);
if ($term){
return $term->id();
}
$term =Term::create([
'name' => $term_name,
'vid' => $vid,
])
->save();
//Returns $term id value e.g 2,3,577
Now i just wanted to add a custom Block too just for testing how to apply the Service with a Dependency Injection. So i added a /web/custom/modules/src/Plugin/Block/AwesomePois.php
AwesomeClient = $awesome_client;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('awesome_client')
);
}
/**
* {@inheritdoc}
*/
public function build() {
//Get all available Pois
$pois_list = $this->peopleFlowClient->pois();
return [
'#theme' => 'item_list',
'#items' => $pois_list,
];
}
}
?>
And that's about it :)