The project
On the occasion of the redesign of the CGT unionization form, we wanted to offer users a simpler journey. The aim of this project is to simplify the entry of their mailing address.
Users here have an autocompletion field when entering their address. This feature, often provided by (paid) geocoding services from Google, is here offered via the API of the French Open Public Data Platform. By filling in the address field, the module suggests addresses (street number + street name) and then completes the supplementary address fields (postal code, city, etc.)
Module purpose
The module is available here:
https://www.drupal.org/project/api_adresses_open_data_france -
The code is available here:
https://git.drupalcode.org/project/api_sirene_open_data_france/commit/4d7abc1 -
The module can be used to simplify any registration that requires information about addresses available on French territory.
The Address API
https://geo.api.gouv.fr/adresse
As part of the Public Data Service, many datasets are open. The ADDRESS database can be freely and freely accessed!
This service queries the address API developed by Étalab.
This website and its API are open-source: you can download the code on GitHub.
The SIRENE reference data is available on Data.gouv.fr.
The demo
Autocompletion from an address field:
Creating the route
Creation of the route for the Ajax callback in the module's .routing.yml file:
bluedropfr_syndicalisation_new.address_autocomplete:
path: '/cgt-syndicalisation/address-autocomplete'
defaults:
_controller: '\Drupal\bluedropfr_syndicalisation_new\Controller\AddressAjaxController::address_autocomplete'
_title: 'Autocomplete'
_format: json
requirements:
_access: 'TRUE'The manager file
A Manager file is created (to be extended) - For now, it only contains the methods necessary for address search and autocompletion of fields.
GeoAPIGouvFrManager.php
error($msg);
return;
}
$this->client = \Drupal::httpClient();
}
/**
* Do CURL request with authorization.
*
* @param string $resource
* A request action of api.
* @param string $method
* A method of curl request.
* @param Array $inputs
* A data of curl request.
*
* @return array
* An associate array with respond data.
*/
private function executeCurl($resource, $method, $inputs) {
if (!function_exists('curl_init')) {
$msg = 'Geo API gouv.fr requires CURL module';
\Drupal::logger('bluedropfr_syndicalisation_new')->error($msg);
return NULL;
}
$api_url = self::API_URL . "/" . $resource;
$options = [
'headers' => [
'Content-Type' => 'application/json'
],
];
if (!empty($inputs)) {
if($method == 'GET'){
$api_url.= '?' . self::arrayKeyfirst($inputs) . '=' . array_shift($inputs);
foreach($inputs as $param => $value){
$api_url.= '&' . $param . '=' . $value;
}
}else{
//POST request send data in array index form_params.
//$options['body'] = $inputs;
}
}
try {
$clientRequest = $this->client->request($method, $api_url, $options);
$body = $clientRequest->getBody();
} catch (RequestException $e) {
\Drupal::logger('bluedropfr_syndicalisation_new')->error('Curl error: @error', ['@error' => $e->getMessage()]);
}
return Json::decode($body);
}
/**
* Get Request of API.
*
* @param string $resource
* A request action.
* @param string $input
* A data of curl request.
*
* @return array
* A respond data.
*/
public function curlGet($resource, $inputs) {
return $this->executeCurl($resource, "GET", $inputs);
}
/**
* Post Request of API.
*
* @param string $resource
* A request action.
* @param string $inputs
* A data of curl request.
*
* @return array
* A respond data.
*/
public function curlPost($resource, $inputs) {
return $this->executeCurl($resource, "POST", $inputs);
}
/**
* Search place by street number, city name, street name...
*
* @param array $options
* An array of search options.
*
* @return array
* An array of search results.
*/
public function searchPlace($options) {
return $this->curlGet("search", $options);
}
/**
* Get campaigns by type.
*
* @param string $type
* A campaign type.
*
* @return array
* An array of options.
*/
public function buildOptionsSearchAutocomplete($searchString, $type, $limit = 5, $autocomplete = 1) {
$options = [
"q" => $searchString,
"type" => $type,
"autocomplete" => $autocomplete,
"limit" => $limit,
];
return $options;
}
/**
* Function to return first element of the array, compatability with PHP 5, note that array_key_first is only available for PHP > 7.3.
*
* @param array $array
* Associative array.
*
* @return string
* The first key data.
*/
public static function arrayKeyfirst($array){
if (!function_exists('array_key_first')) {
foreach($array as $key => $unused) {
return $key;
}
return NULL;
}else{
return array_key_first($array);
}
}
}The controller
A controller serves as a callback for the Ajax call.
AddressAjaxController.php
request->get('name_startsWith');
$type = "housenumber"; // can be housenumber, street, locality or municipality.
$geoApi = new GeoAPIGouvFrManager();
$return = $geoApi->searchPlace($geoApi->buildOptionsSearchAutocomplete($search_string, $type, 10));
return new JsonResponse(json_encode($return['features']), 200, [], true);
}
}The javascript component for user interaction
(function ($) {
$(document).ready(function() {
$('#edit-adresse-rue').autocomplete({
source : function(requete, reponse){ // both arguments represent the necessary data for the plugin
$.ajax({
url : Drupal.url('cgt-syndicalisation/address-autocomplete'), // calls the JSON script
dataType : 'json', // specify that the data type is JSON
type: "POST",
data : {
//variable sent with the request to the server
name_startsWith : $('#edit-adresse-rue').val(), // supplies the string typed in the search field
},
success : function(donnee){
//donnee is the variable received from the server with the results
reponse($.map(donnee, function(objet){
return {'label':objet.properties.label,
'value':objet.properties.name,
'postcode':objet.properties.postcode,
'id':objet.properties.id,
'city':objet.properties.city,}; // return this form of suggestion
}));
}
});
}
});
$('#edit-adresse-rue').on( "autocompleteselect", function( event, ui ) {
var postcode = ui.item.postcode;
var city = ui.item.city;
$('#edit-code-postal').val(postcode);
$('#edit-ville').val(city);
});
});
})(jQuery);For more information on the API's JSON attributes: https://github.com/geocoders/geocodejson-spec/tree/master/draft
Try it out!
Drupal API Addresses Open Data France Module
Many thanks to @elie for this contribution!!
?>