Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Geocoder Bundle Laravel Package

willdurand/geocoder-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require willdurand/geocoder-bundle
    

    Enable the bundle in config/bundles.php:

    return [
        // ...
        WillDurand\GeocoderBundle\WillDurandGeocoderBundle::class => ['all' => true],
    ];
    
  2. Configuration: Define providers in config/packages/willdurand_geocoder.yaml:

    willdurand_geocoder:
        default_provider: google_maps
        providers:
            google_maps:
                key: '%env(GOOGLE_MAPS_API_KEY)%'
                http_client: http_client
            bing:
                key: '%env(BING_MAPS_API_KEY)%'
    
  3. First Use Case: Inject the GeocoderInterface into a service/controller:

    use Geocoder\Geocoder;
    use Geocoder\Provider\GoogleMapsProvider;
    
    public function __construct(private Geocoder $geocoder) {}
    
    public function findCoordinates(string $address): array
    {
        $coordinates = $this->geocoder->geocode($address)->first();
        return $coordinates ? $coordinates->getCoordinates() : null;
    }
    
  4. Symfony Profiler Integration: Automatically enabled. Check the profiler toolbar for geocoding metrics (queries, execution time, results).


Implementation Patterns

Common Workflows

1. Reverse Geocoding (Coordinates → Address)

public function getAddressFromCoordinates(float $latitude, float $longitude): ?string
{
    $coordinates = new \Geocoder\Model\Coordinates($latitude, $longitude);
    $addresses = $this->geocoder->reverse($coordinates)->first();

    return $addresses ? $addresses->getStreet() : null;
}

2. Batch Geocoding

Useful for bulk operations (e.g., importing addresses):

public function batchGeocode(array $addresses): array
{
    $results = [];
    foreach ($addresses as $address) {
        $results[$address] = $this->geocoder->geocode($address)->first()?->getCoordinates();
    }
    return $results;
}

3. Fallback Providers

Configure multiple providers and let the bundle handle fallbacks:

willdurand_geocoder:
    providers:
        primary:
            key: '%env(PRIMARY_API_KEY)%'
        fallback:
            key: '%env(FALLBACK_API_KEY)%'

In code:

$this->geocoder->geocode($address); // Automatically falls back if primary fails.

4. Custom Providers

Extend the bundle with custom providers (e.g., internal databases):

willdurand_geocoder:
    providers:
        custom_db:
            class: App\Geocoder\CustomDbProvider

Implement Geocoder\Provider\ProviderInterface:

class CustomDbProvider implements ProviderInterface {
    public function geocode($query, $options = []): iterable {
        // Fetch from DB or other source.
    }
    // ... other required methods.
}

5. Caching

Cache results to reduce API calls (e.g., using Symfony Cache component):

willdurand_geocoder:
    providers:
        google_maps:
            key: '%env(GOOGLE_MAPS_API_KEY)%'
            cache: cache.app

Requires symfony/cache bundle.


Integration Tips

Form Integration

Use with Symfony Forms for address validation:

use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Validator\Constraints\Geocoder;

$builder->add('address', TextType::class, [
    'constraints' => [
        new Geocoder(['provider' => 'google_maps']),
    ],
]);

Event Listeners

Trigger geocoding on entity events (e.g., prePersist):

public function onPrePersist(User $user)
{
    if (!$user->getCoordinates() && $user->getAddress()) {
        $coordinates = $this->geocoder->geocode($user->getAddress())->first()?->getCoordinates();
        $user->setCoordinates($coordinates);
    }
}

Command-Line Geocoding

Create a console command for bulk operations:

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class GeocodeCommand extends Command {
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $addresses = $this->getAddressesFromCsv();
        foreach ($addresses as $address) {
            $coordinates = $this->geocoder->geocode($address)->first()?->getCoordinates();
            $this->saveToDatabase($address, $coordinates);
        }
        return Command::SUCCESS;
    }
}

Gotchas and Tips

Pitfalls

1. API Key Management

  • Issue: Hardcoding API keys in config files.
  • Fix: Use environment variables (%env(KEY)%) and restrict bundle access to sensitive keys.
  • Tip: Rotate keys periodically and revoke old ones.

2. Rate Limiting

  • Issue: Hitting API rate limits during batch operations.
  • Fix:
    • Implement exponential backoff in custom providers.
    • Use caching aggressively.
    • Monitor profiler metrics for query counts.
  • Example:
    $this->geocoder->geocode($address, ['timeout' => 2, 'retry_on_failure' => 3]);
    

3. Provider-Specific Quirks

  • Google Maps: Requires https:// URLs and may block non-commercial use.
  • Bing Maps: Needs a valid subscription key and may return partial results.
  • OpenStreetMap: Free but slower; use openstreetmap provider with caution in production.
  • Fix: Test providers in staging with real-world data before deployment.

4. Time Zone Handling

  • Issue: Coordinates may not match expected time zones (e.g., daylight saving).
  • Fix: Store time zone data alongside coordinates if needed:
    $result = $this->geocoder->geocode($address)->first();
    $timeZone = $result->getTimezone(); // Requires provider support.
    

5. Profiler Overhead

  • Issue: Profiler data may slow down requests in production.
  • Fix: Disable profiler in config/packages/dev/willdurand_geocoder.yaml:
    willdurand_geocoder:
        profiler: false
    

Debugging Tips

1. Enable Verbose Logging

Configure Monolog to log geocoding attempts:

monolog:
    handlers:
        main:
            level: debug
            channels: ["geocoder"]

Then, in code:

$this->geocoder->geocode($address, ['logger' => true]);

2. Inspect Raw Responses

Use a custom provider to log raw API responses:

class DebugProvider implements ProviderInterface {
    public function geocode($query, $options = []): iterable {
        $response = $this->httpClient->request('GET', $this->url($query));
        $this->logger->debug('Raw response:', ['data' => $response->getContent()]);
        // Parse and return results.
    }
}

3. Handle Partial Results

Some providers (e.g., Bing) may return partial results. Validate responses:

$results = $this->geocoder->geocode($address);
if ($results->count() === 0) {
    throw new \RuntimeException('No results found for address: ' . $address);
}

4. Test Edge Cases

  • Empty strings or null addresses.
  • Non-existent addresses (e.g., "123 Fake Street, Mars").
  • Malformed coordinates (e.g., NaN or null values).

Extension Points

1. Custom Provider Factories

Override provider creation in a compiler pass:

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use WillDurand\GeocoderBundle\DependencyInjection\Compiler\GeocoderPass;

class CustomGeocoderPass implements CompilerPassInterface {
    public function process(ContainerBuilder $container) {
        $definition = $container->findDefinition('geocoder.provider.custom');
        $definition->setClass('App\Geocoder\CustomProvider');
    }
}
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
anousss007/vigilance
supportpal/eloquent-model
ardenexal/fhir-models
laravel-at/laravel-image-sanitize
romalytar/yammi-audit-log-laravel
ardenexal/fhir-validation
arshaviras/weather-widget
laravel-chronicle/core
sunchayn/nimbus
daikazu/eloquent-salesforce-objects
unseen-codes/chat
romalytar/yammi-jobs-monitoring-laravel
kisame76/filament-db-table-state
nqxcode/laravel-lucene-search
dpfx/laravel-livewire-wizards
workos/workos-php-laravel
sofa/laravel-global-scope
nawasara/auth-primitives
adhocrat-io/arkhe-main
make-dev/orca-harpoon