tmcycyit/geo-bridge-bundle
tmcycyit/geo-bridge-bundle is a lightweight integration layer that connects your PHP application to external geo services. It provides straightforward configuration and service wiring so you can add location-aware features such as geocoding and distance lookups without boilerplate.
Three commands get you from zero to a working call:
composer require vendor/package
php artisan vendor:publish --provider="Vendor\Package\PackageServiceProvider"
php artisan migrate # only if the package ships migrations
Then look at three things, in this order:
config/package.php — published config is the contract. Every default, driver name, and timeout the package exposes lives here. Read it end to end before reading any docs; it's more current than the README.vendor/vendor/package/src/*ServiceProvider.php — the register() method tells you what's bound as a singleton vs. scoped, which interfaces have defaults, and what extend() hooks exist. This is where you learn what you can swap out.src/Contracts/ directory — if it exists, that's your stable seam. Code against the interface, not the concrete class.First use case: don't wire it into a controller yet. Open php artisan tinker and make one happy-path call:
app(\Vendor\Package\Contracts\Client::class)->ping();
If that returns cleanly, your credentials and config are correct and everything after this is application design. If it throws, you've isolated the problem to configuration rather than your integration code — which is where most of the debugging time in the first day goes.
Wrap it behind your own interface. The single highest-value thing you can do on day one. One class, one or two methods, delegating to the package:
interface SendsNotifications
{
public function send(Notification $n): Result;
}
final class PackageNotifier implements SendsNotifications
{
public function __construct(private \Vendor\Package\Client $client) {}
public function send(Notification $n): Result
{
return Result::fromVendor($this->client->dispatch($n->toVendorPayload()));
}
}
Bind it in your own service provider. When the package has a breaking change, or you want to swap it, you edit one file instead of grepping forty call sites. This also gives you a natural place to fake in tests.
Resolve from the container, never new. The package's client is almost certainly configured through a singleton binding. new Client($apiKey) bypasses the package's own config resolution, retry logic, and HTTP client middleware. Use app() or constructor injection.
Push slow work to queues. Anything that makes a network call — sending, syncing, importing — belongs in a job, not a request lifecycle. Combine with the package's own retry config rather than duplicating it:
final class SyncAccount implements ShouldQueue
{
public int $tries = 3;
public array $backoff = [10, 60, 300];
public function __construct(public int $accountId) {}
public function handle(SendsNotifications $notifier): void
{
$notifier->send(Notification::forAccount($this->accountId));
}
}
Listen to package events instead of polling. If the package dispatches events (PackageEvent::class), register listeners in EventServiceProvider rather than scheduling a cron job to check state. Cheaper and less racy. Check vendor/ for event(new calls to find them.
Feature-flag the rollout. Put the integration behind a config flag or Laravel Pennant so you can ship the code, deploy it dark, and enable per-user or per-environment while you watch error rates. Much easier than a revert.
Test against the interface, not the network. Bind a fake implementation of your SendsNotifications interface in the test container. If you must exercise the package itself, use Http::fake() — most well-built Laravel packages use the HTTP client underneath, so recorded fixtures usually work.
Config caching bites on deploy. php artisan config:cache freezes config/*.php. If you change a package config value and see no effect, that's why. Run php artisan config:clear locally; on deploy, ensure config:cache runs after your env is set.
Re-publish and diff config on every minor bump. New config keys added by the package won't appear in your already-published file, and missing keys often fall back to null rather than the documented default — producing confusing "works in a fresh install, breaks in ours" bugs. Publish to a temp directory and diff:
php artisan vendor:publish --provider="Vendor\Package\PackageServiceProvider" --force
git diff config/package.php
Watch singleton vs. scoped bindings under Octane. A singleton that accumulates per-request state will leak it across requests. Look for singleton() in the provider; if the object is stateful, rebind it as scoped() in your own provider. This is a classic "works in php artisan serve, corrupts data under Octane" failure.
Migrations published after install. If the package ships migrations, they land in database/migrations with a timestamp from publish time, not release time. On an existing app this can order them after tables they depend on. Rename the file to an earlier timestamp if you hit a foreign key error on a fresh migrate:fresh.
Nested retries. If you put a package call in a queued job and the package has its own retry/backoff, you multiply attempts (3 job tries × 3 internal retries = 9 network calls). Pick one layer. Generally set the package's retries to 1 and let the queue own the policy.
Debugging is mostly verbose logging plus one raw call. Wrap the call sites in Log::debug() with the correlation/request ID, and when something fails, drop to tinker and reproduce with the exact same arguments — the package's own exception messages are usually precise if you don't swallow them with a broad catch.
Extension points, in order of preference: (1) config values, (2) binding a custom implementation of the package's contract in your provider, (3) Manager::extend() if it exposes a driver manager, (4) macros, (5) forking. Almost everything a Laravel package does is swappable at levels 1–3, and reaching for level 5 is usually a sign you missed a contract.
Read the changelog before minor bumps, not just majors. Laravel-ecosystem packages frequently change defaults — retry counts, driver names, payload shapes — inside patch releases. If a composer update changes behavior without changing your code, this is where to look first.
How can I help you explore Laravel packages today?