Le projet
A l'occasion de la refonte du formulaire de syndicalisation de la CGT, nous avons souhaité proposer aux utilisateurs, une simplification de leur parcours. Le projet est ici de simplifier le renseignement de leur adresse postale.
Les utilisateurs disposent ici d'un champ d'autocompletion lors de la saisie de leur adresse. Cette fonctionnalité, souvent relayée par les données fournies par les services (payants) de geocoding de google, est ici fournie par l'API de la Plateforme ouverte des données publiques françaises. En renseignant le champ adresse, le module vient suggérer des adresses (numéro de la voie + libellé de la voie), puis complète les champs complémentaires d'adresse (code postale, ville, etc.)
L'objectif du module
Le module est disponible ici :
https://www.drupal.org/project/api_adresses_open_data_france -
Le code est disponible ici :
https://git.drupalcode.org/project/api_sirene_open_data_france/commit/4d7abc1 -
Le module peut servir à simplifier n'importe quelle inscription nécessitant des informations concernant les adresses disponibles sur le territoire français.
L'API adresse
https://geo.api.gouv.fr/adresse
Dans le cadre du Service Public de la Donnée, de nombreux jeux de données sont ouverts. La base de données ADRESSE est consultable librement et gratuitement !
Ce service interroge l’API adresse développée par Étalab.
Ce site web et son API sont open-source : vous pouvez télécharger le code sur GitHub.
Les données de référence SIRENE sont disponibles sur Data.gouv.fr.
La démo
Autocompletion à partir d'un champ adresse :
Création de la route
Création de la route pour l'Ajax callback dans le fichier .routing.yml du module :
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'
Le fichier manager
On crée un fichier Manager (à étendre) - Il ne contient pour l'instant que les méthodes nécessaires pour la recherche des adresses et l'autocomplétion des champs.
GeoAPIGouvFrManager.php
<?php
namespace Drupal\bluedropfr_syndicalisation_new;
use Drupal\Component\Serialization\Json;
use GuzzleHttp\Exception\RequestException;
/**
* Basic manager of module.
*/
class GeoAPIGouvFrManager {
/**
* API request url.
*/
const API_URL = 'https://api-adresse.data.gouv.fr';
public $client;
/**
* Constructor.
*/
public function __construct() {
if (!function_exists('curl_init')) {
$msg = 'Geo API gouv.fr requires CURL module';
\Drupal::logger('bluedropfr_syndicalisation_new')->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);
}
}
}
Le contrôleur
Un contrôleur sert de callback pour l'appel Ajax.
AddressAjaxController.php
<?php
namespace Drupal\bluedropfr_syndicalisation_new\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Component\Serialization\JSON;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Drupal\ebiz_syndicalisation_new\GeoAPIGouvFrManager;
class AddressAjaxController extends ControllerBase {
//function for autocomplete
public function address_autocomplete(Request $request){
$return = []; //our variable to fill with data to return to autocomplete result
$search_string = \Drupal::request()->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);
}
}
Le composant javascript pour les interactions avec l'utilisateur
(function ($) {
$(document).ready(function() {
$('#edit-adresse-rue').autocomplete({
source : function(requete, reponse){ // les deux arguments représentent les données nécessaires au plugin
$.ajax({
url : Drupal.url('cgt-syndicalisation/address-autocomplete'), // on appelle le script JSON
dataType : 'json', // on spécifie bien que le type de données est en JSON
type: "POST",
data : {
//variable envoyé avec la requête vers le serveur
name_startsWith : $('#edit-adresse-rue').val(), // on donne la chaîne de caractère tapée dans le champ de recherche
},
success : function(donnee){
//donnee est la variable reçu du serveur avec les résultats
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,}; // on retourne cette forme de 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);
Pour plus d'informations sur les attributs JSON de l'API : https://github.com/geocoders/geocodejson-spec/tree/master/draft
Testez-le !
Module Drupal API adresses Open Data France
Grand merci à @elie pour cette contribution !!