Installation:
composer require chaplean/geolocation-bundle
Ensure Cravler\MaxMindGeoIpBundle and Bazinga\GeocoderBundle are also installed (required dependencies).
Register Bundles in config/bundles.php (Symfony 4+):
return [
// ...
Cravler\MaxMindGeoIpBundle\CravlerMaxMindGeoIpBundle::class => ['all' => true],
Bazinga\Bundle\GeocoderBundle\BazingaGeocoderBundle::class => ['all' => true],
Chaplean\Bundle\GeolocationBundle\ChapleanGeolocationBundle::class => ['all' => true],
];
Configure API Key:
Add to .env (or parameters.yml for Symfony <5.1):
CHAPLEAN_GEOLOCATION_API_KEY=AIzaSyA4iQOHlCF5nqaEDKk-9IsYAMapdOvIATc
Import Config:
Add to config/packages/chaplean_geolocation.yaml:
imports:
- { resource: "@ChapleanGeolocationBundle/Resources/config/config.yml" }
Inject the service and fetch location data:
use Chaplean\Bundle\GeolocationBundle\Service\GeolocationService;
class MyController extends AbstractController {
public function showLocation(GeolocationService $geolocation) {
$coordinates = [48.8566, 2.3522]; // [lat, lng]
$location = $geolocation->reverseGeocode($coordinates);
return $this->render('template.html.twig', ['location' => $location]);
}
}
Forward Geocoding (Address → Coordinates):
$address = '1600 Amphitheatre Parkway, Mountain View, CA';
$coordinates = $geolocation->geocode($address);
Reverse Geocoding (Coordinates → Address):
$coordinates = [40.7128, -74.0060]; // [lat, lng]
$location = $geolocation->reverseGeocode($coordinates);
Distance Calculation:
$distance = $geolocation->calculateDistance(
[48.8566, 2.3522], // Point A
[51.5074, -0.1278] // Point B
);
BazingaGeocoderBundle:
# config/packages/bazinga_geocoder.yaml
bazinga_geocoder:
providers:
google_maps:
cache: app.cache.geocoder
MaxMindGeoIpBundle for IP-based geolocation:
$ipGeolocation = $this->container->get('cravler_maxmind_geoip.geolocation');
{{ dump(app.service('chaplean_geolocation').reverseGeocode([48.8566, 2.3522])) }}
Listen for geocoding events (e.g., chaplean.geolocation.post_geocode):
// src/EventListener/GeocodeListener.php
public static function getSubscribedEvents() {
return [
'chaplean.geolocation.post_geocode' => 'onPostGeocode',
];
}
public function onPostGeocode(GeocodeEvent $event) {
$result = $event->getResult();
// Enrich or modify $result
}
API Key Limits:
apcu or redis) to avoid hitting limits:
# config/packages/bazinga_geocoder.yaml
bazinga_geocoder:
providers:
google_maps:
cache: cache.adapter.redis
Coordinate Order:
[latitude, longitude] (not [longitude, latitude]). Reverse inputs cause incorrect results.if (!is_array($coordinates) || count($coordinates) !== 2) {
throw new \InvalidArgumentException('Coordinates must be [lat, lng]');
}
Deprecated Dependencies:
Cravler\MaxMindGeoIpBundle is unmaintained. Replace with geoip2/intl if needed:
composer require geoip2/intl
Enable Verbose Logging:
# config/packages/monolog.yaml
monolog:
handlers:
main:
level: debug
channels: ["!event"]
Check logs for API errors (e.g., HTTP/2 403 Forbidden due to invalid keys).
Validate Responses:
$result = $geolocation->geocode('invalid address');
if ($result->hasError()) {
$error = $result->getMessage();
// Handle gracefully (e.g., fallback to IP geolocation)
}
Custom Providers:
Override the default Google provider by configuring BazingaGeocoderBundle:
# config/packages/bazinga_geocoder.yaml
bazinga_geocoder:
providers:
my_custom_provider:
service_id: my.custom.geocoder.service
Then update ChapleanGeolocationBundle's configuration to use my_custom_provider.
Model Binders: Bind geolocation data to Eloquent models (Symfony 4+):
use Symfony\Component\Validator\Constraints as Assert;
class User {
/**
* @Assert\Type("array", message="Coordinates must be an array.")
* @Assert\Callback(methods={"validateCoordinates"})
*/
public $coordinates;
}
public function validateCoordinates($coordinates) {
if (count($coordinates) !== 2) {
$this->addViolation('Coordinates must be [lat, lng].');
}
}
Batch Processing:
Use Symfony’s Messenger component to queue geocoding tasks:
$this->messageBus->dispatch(
new GeocodeMessage('1600 Amphitheatre Parkway')
);
Implement a worker to process messages asynchronously.
How can I help you explore Laravel packages today?