Automatically Generating and Storing Coordinates
Introduction
This guide describes how coordinates can be automatically generated from address data and stored in a MetaModels installation.
Requirements
Contao 5.3 with MetaModels 2.4, also successfully tested with Contao 4.13 and MetaModels 2.3
Google Maps API key for coordinate generation →
API_KEY_SERVER(Application: IP addresses; API: Geocoding API)Fields in the MetaModel:
street,postcode,city,latitude,longitudeThe fields
latitudeandlongitudeare of type “Decimal” (metamodels/attribute_decimal)
Step 1: Creating the EventListener
To automatically retrieve and store coordinates from address data, a listener for the PrePersistModelEvent is created. The event is fired before the model is written to the database.
Create a file src/EventListener/PrePersistModelEventListener.php with the
following content. Note: API_KEY_SERVER must be replaced with the
corresponding API key.
<?php
// src/EventListener/PrePersistModelEventListener.php
namespace App\EventListener;
use ContaoCommunityAlliance\DcGeneral\Event\PrePersistModelEvent;
use App\Service\GoogleMaps;
class PrePersistModelEventListener
{
public function __invoke(PrePersistModelEvent $event) {
$model = $event->getModel();
if (!$model->getProperty('latitude') || !$model->getProperty('longitude')) {
$street = $model->getProperty('street');
$postcode = $model->getProperty('postcode');
$city = $model->getProperty('city');
$address = $street . ', ' . $postcode . ' ' . $city;
$googleMaps = new GoogleMaps();
$apiToken = 'API_KEY_SERVER';
$coordinates = $googleMaps->getCoordinates($address, $apiToken);
if (null !== $coordinates) {
$model->setProperty('latitude', $coordinates->getLatitude());
$model->setProperty('longitude', $coordinates->getLongitude());
}
}
}
}
Step 2: Creating the GoogleMaps Class
Create the file src/Service/GoogleMaps.php with the following content:
<?php
// src/Service/GoogleMaps.php
namespace App\Service;
use App\Service\Coordinates;
class GoogleMaps
{
public function getCoordinates($address, $apiKey) {
$url = \sprintf('https://maps.googleapis.com/maps/api/geocode/json?address=%s&key=%s', urlencode($address), $apiKey);
$response = file_get_contents($url);
$data = json_decode($response);
if ($data->status === 'OK') {
$loc = $data->results[0]->geometry->location;
return new Coordinates($loc->lat, $loc->lng);
}
return null;
}
}
Step 3: Creating the Coordinates Class
Create the file src/Service/Coordinates.php with the following content:
<?php
// src/Service/Coordinates.php
namespace App\Service;
class Coordinates
{
private $lat;
private $lng;
public function __construct($lat, $lng) {
$this->lat = $lat;
$this->lng = $lng;
}
public function getLatitude() {
return $this->lat;
}
public function getLongitude() {
return $this->lng;
}
}
Step 4: Registering the EventListener
Create or edit the file src/Resources/config/service.yml:
# src/Resources/config/service.yml
services:
App\EventListener\PrePersistModelEventListener:
public: true
tags:
- { name: kernel.event_listener, event: dc-general.model.pre-persist }
Ensure that this file is included in the main configuration, e.g. in
config/services.yaml:
# config/services.yaml
imports:
- { resource: '../src/Resources/config/service.yml' }
Testing
Edit an existing record in the backend, fill in the street, postcode,
and city fields and leave the latitude and longitude fields empty.
The coordinates will be generated automatically on saving.
Notes
The
PrePersistModelEventis only triggered for changed records.How the stored coordinates can be displayed as markers on a map is described under “Displaying Locations as Markers on a Google Map”.
If the perimeter search filter (metamodels/filter_perimetersearch) is implemented, one of its implemented services can also be used to determine the coordinates.
The EventListener can also be registered via PHP annotations or attributes — see Registering Services.
Thanks
Thanks to Nicole Weiß - Webstylisten.de for the article.