ChainProvider integrates seamlessly with Laravel’s service container, facades, and dependency injection, enabling clean architecture patterns like:
GeocoderInterface into a LocationRepository.Geocoder::geocode() in controllers without tight coupling.Geocoded/GeocodeFailed events for observability.spatie/laravel-activitylog or custom middleware).cache()->tags(['addresses'])->remember).Mockery or PHPUnit, with support for Http::fake() to simulate provider failures.Http facade) and PSR-3 logging (e.g., Log facade)..env (e.g., GOOGLE_MAPS_KEY).GeocodeJob).Algorithmic or Meilisearch providers).| Risk | Mitigation |
|---|---|
| Provider API Deprecation | Monitor provider changelogs (e.g., Google Maps API deprecations); use feature flags to toggle providers. |
| Rate Limiting Throttling | Implement Laravel’s ThrottleRequests middleware or Guzzle retry middleware. |
| Caching Stale Data | Use tagged caching with invalidation (e.g., cache()->forget('geocode_$query') on address updates). |
| Error Handling Complexity | Centralize exceptions via Laravel Exceptions (app/Exceptions/Handler.php) or Sentry. |
| Performance Bottlenecks | Benchmark providers; use async queues for non-critical paths. |
ChainProvider::create([new CheapProvider(), new AccurateProvider()]).geocode_$address) or user session (e.g., user_$id_geocodes)?| Laravel Feature | Integration Point |
|---|---|
| Service Container | Bind ChainProvider in AppServiceProvider::boot() with provider configs from .env. |
| Facades | Use Geocoder::geocode()/reverse() in controllers/services. |
| HTTP Client | Leverage Laravel’s Http facade (PSR-18 compliant) or custom Psr18Client. |
| Logging | Configure Monolog via config/logging.php for PSR-3 support (e.g., log provider failures). |
| Cache | Integrate with Laravel’s cache (e.g., Cache::remember('geocode_$query', 3600, ...)). |
| Queues | Offload geocoding to queues (e.g., GeocodeJob) with dispatch() or dispatchSync(). |
| Events | Dispatch Geocoded/GeocodeFailed events for analytics (e.g., track provider usage). |
| Scout | Use with geospatial providers (e.g., AlgorithmicScout) for search. |
Phase 1: Single Provider (PoC)
composer require geocoder-php/geocoder geocoder-php/chain-provider
config/services.php:
'geocoder' => [
'providers' => [
'openstreetmap' => [
'http_client' => Http::macro('create', fn() => new Psr18Client()),
],
],
],
$geocoder = new \Geocoder\ChainProvider([new \Geocoder\Provider\OpenStreetMapProvider()]);
$result = $geocoder->geocode('1600 Amphitheatre Parkway, Mountain View');
Phase 2: Multi-Provider Chaining
$geocoder = new \Geocoder\ChainProvider([
new \Geocoder\Provider\OpenStreetMapProvider(),
new \Geocoder\Provider\GoogleMapsProvider(env('GOOGLE_MAPS_KEY')),
]);
AppServiceProvider:
$this->app->singleton(\Geocoder\GeocoderInterface::class, fn() => $geocoder);
use Geocoder\Facades\Geocoder;
public function showAddress(Request $request) {
$result = Geocoder::geocode($request->address);
return response()->json($result);
}
Phase 3: Production Hardening
$result = Cache::remember("geocode_{$address}", 3600, fn() => $geocoder->geocode($address));
ThrottleRequests middleware or Guzzle’s retry config.GeocodeJob::dispatch($address)->onQueue('geocoding');
Http facade or custom PSR-18 clients (e.g., nyholm/psr7 + guzzlehttp/psr7).Log facade or custom Monolog setups..env (e.g., GOOGLE_MAPS_KEY, MAPBOX_KEY).config/services.php or a dedicated config/geocoder.php.ChainProvider in AppServiceProvider with dependency injection.public function __construct(private GeocoderInterface $geocoder) {}
How can I help you explore Laravel packages today?