Haben Sie Fragen? 04267 / 234428
Umkreissuche in Drupal 8 erstellen
In Drupal 7 sind "fertige" Proximity Search Lösungen via Location oder auch Geofield+Geocoder verfügbar. Leider haben diese Module für Drupal 8 momentan noch keine funktionierenden Plugins für die Views Exposed Filter. Als Alternative steht uns in D8 das Modul Geolocation zur Verfügung, welches eine funktionierende Views Integration für eine Umkreissuche mitbringt. Die Sache hat nur einen kleinen Haken: Der Exposed Filter erwartet Längen- und Breitengrad als Input Value.
Längen- und Breitengrade interessieren Anwender natürlich nicht, deshalb wird ein custom module die Aufgabe des Geocodings übernehmen (Geocoding: Umwandeln von Standortdaten wie Strasse, Ort oder auch PLZ in Geodaten wie Längen- und Breitengrad) und den Proximtity Views Filter von Geolocation füttern. Ich benutze hier den Geocoding Service von Google, da er meiner Meinung nach die besten Ergebnisse sehr schnell zurück liefert.
Für unsere Umkreissuche in Drupal 8 brauchen wir also:
- Google Geocoding API Schlüssel
- Drupal Geolocation Module letzte stable Version
- Views (im Core)
- unser custom module (ist unten auch zum Download angehängt)
How To
1. Google Geocoding API Key
Erstelle bei Google Developers ein Projekt und aktiviere darin die Google Maps Geocoding API. Anschließend musst du noch einen API Key generieren. Wenn du im Geolocation Form Field das Google Geocoder Widget nutzen möchtest, dann aktiviere auch gleich die Google Maps JavaScript API.
2. Geolocation Module installieren
drush:
drush dl geolocation && drush en geolocation
oder mit drupal console:
drupal module:install geolocation
oder halt via Web Oberfläche in Drupal.
In den Geolocation Settings unter config/services/geolocation trägst du den API Key, den du bei Google generiert hast, ein.
3. Drupal Entity für deine Locations erstellen
Erstelle dein Entity (Inhaltstyp) für deine Locations und füge ein Feld vom Typ "Geolocation" hinzu. Nenne das Feld "Geodaten" (Maschinenname: field_geodaten), wenn du das custom Module nicht extra anpassen willst. Hier werden bei der Node Erstellung je nach Einstellung Längen- und Breitengrad oder Standortdaten eingegeben.
Erstelle zwei, drei Entities mit Geodaten zum Testen.
4. Die View für die Umkreissuche erstellen
Erstelle eine View für deinen Inhaltstypen mit den Geodaten. Nenne die View "Proximity Search" (Maschinenname: proximity_search), wenn du das custom module nicht extra anpassen willst.
Gib deinen Views Displays unter Erweitert -> Andere einen eigenen Maschinen Namen:
Display Type Page: Searchpage
Display Type Block: Searchblock
Füge einen Exposed Filter "Proximity (field_geodaten)" hinzu. Einstellungen:
- Diesen Filter für Seitenbesucher freigeben, so dass sie die Optionen selbst wählen können
- Einzelner Filter
- Als Operator: "ist weniger als oder gleich"
- Bezeichner: umkreis
Damit haben wir schon mal eine funktionierende Umkreissuche.
Hinweis: Wenn du ein Block Display mit exposed Filter(n) verwendest, musst du im View Display unter Erweitert ->Ajax verwenden aktivieren!
5. Custom Modul zur Geocodierung und Anpassung des exposed Filter Forms
Wie eingangs erwähnt, ist die Eingabe von Lat / Long natürlich indiskutabel. Unser custom module "better_proximity_search" hat folgende Aufgaben:
- Hinzufügen eines Textfeldes zur freien Eingabe des Ortes oder der PLZ
- Geocodieren der Standorteingaben in Latitude und Longitude
- Die Auswahl des Umkreises in einer Select- List statt freier Eingabe
- Setzen eines default Umkreises von 5 km, da sonst immer gerne der altbekannte "unzulässige Eingabe"- Fehler auftaucht
In einem form_alter() hook modifizieren wir das Views exposed filter form und das Geocoding erfolgt in unserer custom Controller Class:
better_proximity_search.module:
<?php
/**
* @file
* Contains better_proximity_search.module..
*/
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Implements hook_help().
*/
function better_proximity_search_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
// Main module help for the better_proximity_search module.
case 'help.page.better_proximity_search':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('Ergaenzt Geoloction Umkreis Suche mit Views') . '</p>';
return $output;
default:
}
}
/**
* Implements hook_form_alter().
*
* 1. Hinzufügen einer Validierungsfunktion für das Geocoding
* 2. Hinzufügen eines Location Textfeldes zur Location Eingabe
* 3. Ändern des Umkreis Feldes zu Dropdown
*/
function better_proximity_search_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
if (isset($form['#id']) && ($form['#id'] == 'views-exposed-form-proximity-search-searchpage' || $form['#id'] == 'views-exposed-form-proximity-search-searchblock')) {
$form['#validate'][] = 'Drupal\better_proximity_search\Controller\ProximityController::proximity_search_form_validate';
// Kein User Access für lat und lng Felder -> Werte werden programmatisch gesetzt
$form['lat']['#access'] = FALSE;
$form['lng']['#access'] = FALSE;
// Textfeld zur Eingabe der Location
$form['location'] = array(
'#type' => 'textfield',
'#weight' => 20,
'#title' => t('Please enter location or postal code'), // Title kann entfernt werden, wenn nicht gewünscht
'#placeholder' => t('e.g. Hamburg or 20345')
);
// Umkreis Dropdown mit Option List
$form['umkreis'] = array(
'#type' => 'select',
'#options' => array(
'' => t('proximity'),
'10' => '10 km',
'25' => '25 km',
'50' => '50 km',
'75' => '75 km',
'100' => '100 km',
'150' => '150 km',
'200' => '200 km',
'250' => '250 km',
'300' => '300 km',
'500' => '500 km',
'1000' => '1000 km'
),
'#weight' => 30
);
}
}
?>
ProximityController.php
<?php
namespace Drupal\better_proximity_search\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Class ProximityController.
*
* @package Drupal\better_proximity_search\Controller
*
*/
class ProximityController extends ControllerBase {
// Form validate Funktion --> aufgerufen in better_proximity_search_form_alter()
public static function proximity_search_form_validate(&$form, FormStateInterface $form_state) {
$input = &$form_state->getUserInput();
// User Input für custom location Feld prüfen
if (!empty($input['location'])) { //Wenn Input, dann:
// Google API Key aus den Geolocation Settings holen
$api_key = \Drupal::config('geolocation.settings')->get('google_map_api_key');
// Ersetzen von Leerzeichen aus dem User Location Input mit "+"
$user_location = urlencode($form_state->getValue('location'));
// Google Geocoder Request. Hinweis: Anfragen sind für Deutschland optimiert ('&components=country:DE')
// Wenn dies nicht erwünscht ist, entferne: . '&components=country:DE' oder setze das Länderkürzel deiner Wahl
$request_url = '<a href="https://maps.googleapis.com/maps/api/geocode/json?address='">https://maps.googleapis.com/maps/api/geocode/json?address='</a> . $user_location . '&key=' . $api_key . '&components=country:DE';
$response_json = file_get_contents($request_url);
// Umwandeln des JSON Objekts in ein PHP Array
$response = json_decode($response_json, TRUE);
// Wenn Geocodierung ok, setzen der Werte für Längen- und Breitengrad
if ($response['status'] == 'OK') {
$form_state->setValue('lat', $response['results'][0]['geometry']['location']['lat']);
$form_state->setValue('lng', $response['results'][0]['geometry']['location']['lng']);
// Wenn kein Umkreis gewählt wurde, werden 5 km als default gesetzt.
if (empty($input['umkreis'])) {
$form_state->setValue('umkreis', '5');
}
}
// Fehlermeldung, wenn die Google API kein Ergebnis liefert
elseif ($response['status'] == 'ZERO_RESULTS') {
drupal_set_message(t('Your desired location is not available. Pleas check your data input '), 'error');
}
}
}
}
?>
Das Modul wie üblich installieren und ggf. Anpassungen vornehmen.
Kommentar
Vom Feinsten !!! Vielen Dank
geschrieben von Anonymous
am 14 Oktober, 2016 - 14:38
drupal.org
geschrieben von Rusty
am 20 Oktober, 2016 - 16:48
custom vs contrib
geschrieben von Guido
am 27 Oktober, 2016 - 20:29
Fehlersuche
geschrieben von Marcel
am 30 Januar, 2017 - 22:14
Es funktioniert leider nicht
geschrieben von Alexandr Evgrafov
am 30 Mai, 2017 - 08:36