Install Dependencies Run:
composer require braune-digital/geo-bundle jms/serializer-bundle sonata-project/admin-bundle braune-digital/translation-base-bundle
Ensure doctrine/orm is also installed (required for Doctrine integration).
Enable the Bundle
Add to config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 3):
BrauneDigital\GeoBundle\BrauneDigitalGeoBundle::class => ['all' => true],
Configure Geonames API Key
Add to config/packages/braune_digital_geo.yaml:
braune_digital_geo:
geonames_user: 'YOUR_GEONAMES_USERNAME'
Generate Sonata Admin Extensions Extend the bundle for custom fields/actions:
php bin/console sonata:easy-extends:generate --dest=src BrauneDigitalGeoBundle
Register the extended bundle in AppKernel.php (Symfony 3) or config/bundles.php (Symfony 4).
First Use Case: Sync Countries Run the sync command:
php bin/console braune:geo:sync-countries
Verify data in sonata_admin dashboard under "Countries" or "Cities".
Entity Mapping
Use the provided Country and City entities (or extend them):
// src/Entity/YourEntity.php
use BrauneDigital\GeoBundle\Entity\Country;
/**
* @ORM\ManyToOne(targetEntity="BrauneDigital\GeoBundle\Entity\Country")
*/
private $country;
Form Integration Use SonataAdmin’s built-in geo fields or create custom forms:
// src/Admin/YourAdmin.php
$formMapper
->add('country', 'sonata_type_model', [
'model_manager' => $this->getModelManager(),
'property' => 'country',
'btn_add' => false,
]);
API Data Fetching Leverage the bundle’s services to fetch geo data dynamically:
// src/Service/GeoService.php
use BrauneDigital\GeoBundle\Service\GeoService;
public function __construct(private GeoService $geoService) {}
public function getCitiesByCountryCode(string $countryCode): array {
return $this->geoService->getCities($countryCode);
}
Translation Handling
Use BrauneDigitalTranslationBaseBundle for multilingual support:
# config/packages/braune_digital_translation.yaml
braune_digital_translation:
locales: [en, de, fr]
Custom Actions in SonataAdmin Extend the admin class to add geo-specific actions:
// src/Admin/CountryAdmin.php
protected function configureListFields(ListMapper $listMapper) {
$listMapper
->add('name')
->add('geonameId')
->add('_action', 'actions', [
'actions' => [
'view' => [],
'edit' => [],
'delete' => [],
'sync' => ['template' => 'BrauneDigitalGeoBundle:Admin:sync.html.twig'],
],
]);
}
API Rate Limits
// src/Service/CachedGeoService.php
use Symfony\Contracts\Cache\CacheInterface;
public function getCities(string $countryCode) {
return $this->cache->get("geonames_cities_{$countryCode}", function() use ($countryCode) {
return $this->geoService->getCities($countryCode);
}, 86400); // Cache for 1 day
}
Entity Overrides
BrauneDigital\GeoBundle\Entity\Country or City. Instead, extend them:
// src/Entity/Country.php
use BrauneDigital\GeoBundle\Entity\Country as BaseCountry;
class Country extends BaseCountry {
// Add custom fields/methods
}
sonata_easy_extends.yaml to point to your extended entities.SonataAdmin Caching Clear Sonata cache after syncing data:
php bin/console sonata:cache:clear
Doctrine Migrations
php bin/console make:migration
API Key Exposure
geonames_user to version control. Use environment variables:
# .env
GEONAMES_USER=your_username_here
# config/packages/braune_digital_geo.yaml
braune_digital_geo:
geonames_user: '%env(GEONAMES_USER)%'
Enable API Debugging Add a debug listener to log API calls:
// src/EventListener/GeoDebugListener.php
use BrauneDigital\GeoBundle\Service\GeoService;
public function onKernelRequest(GetResponseEvent $event) {
if ($event->isMainRequest()) {
$this->geoService->setDebug(true);
}
}
Check Sync Logs Run sync commands with verbose output:
php bin/console braune:geo:sync-countries --verbose
Validate Entities Use Symfony’s validator to ensure geo data integrity:
// src/Validator/Constraints/CountryExists.php
use Symfony\Component\Validator\Constraint;
class CountryExists extends Constraint {
public function validatedBy() { return static::class; }
}
Custom Geo Providers
Override the default GeoService to use alternative providers (e.g., OpenStreetMap):
// src/Service/CustomGeoService.php
use BrauneDigital\GeoBundle\Service\GeoServiceInterface;
class CustomGeoService implements GeoServiceInterface {
public function getCountries(): array {
// Implement custom logic
}
}
Register as a service:
# config/services.yaml
BrauneDigital\GeoBundle\Service\GeoServiceInterface: '@App\Service\CustomGeoService'
Custom Fields in SonataAdmin Add custom filters or fields using Sonata’s extension system:
// src/Admin/Extension/CountryAdminExtension.php
use Sonata\AdminBundle\Form\FormMapper;
public function mapFormFields(FormMapper $formMapper) {
$formMapper->add('isoCode', 'text', ['required' => false]);
}
Webhook for Syncs Trigger syncs via HTTP requests by creating a custom command:
// src/Command/SyncGeoWebhookCommand.php
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
public function onKernelRequest(GetResponseEvent $event) {
if ($event->getRequest()->getPathInfo() === '/sync-geo') {
$this->syncCountries();
$event->setResponse(new Response('Synced!'));
}
}
Bulk Operations
Use Doctrine’s BulkOperations for large-scale updates:
// src/Service/BulkGeoSyncService.php
use Doctrine\ORM\EntityManagerInterface;
public function bulkUpdateCountries(array $data) {
$conn = $this->entityManager->getConnection();
$conn->executeStatement('
INSERT INTO country (geoname_id, name, iso_code)
VALUES (:geoname_id, :name, :iso_code)
ON CONFLICT (geoname_id) DO UPDATE SET name = EXCLUDED.name
', $data);
}
How can I help you explore Laravel packages today?