Weave 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

Flightstats Bundle Laravel Package

spiicy/flightstats-bundle

A PHP bundle for integrating the FlightStats API, with configurable clients and helpers to retrieve flight status, schedules, airport, airline, and related aviation data in your application.

View on GitHub
Deep Wiki
Context7

Getting Started

Accept the framework mismatch up front. This is a Symfony bundle. There is no Laravel service provider, facade, config publish tag, or Artisan command. Day-to-day, you will not composer require this into a Laravel app and call it — you will either (a) install the underlying FlightStats client it wraps and bind it yourself, or (b) treat the bundle as a spec and reimplement the three or four endpoints you actually need with Http::withHeaders(...).

Read these files in this order before writing any code:

  1. composer.json — find the real HTTP/client dependency and its version pins. That dependency is what you install in Laravel.
  2. DependencyInjection/Configuration.php — the authoritative config tree: app_id, app_key, base URL, sandbox toggle, timeouts.
  3. Resources/config/services.* — how the client is constructed and which arguments it takes. This tells you whether it is cleanly injectable or reaches into the container.
  4. DependencyInjection/*Extension.php — default values and env var names, so your config/services.php mirrors them.

Minimal Laravel wiring once the client is identified:

composer require spiicy/flightstats   # the client, NOT the bundle, if it exists standalone
php artisan make:provider FlightStatsServiceProvider
// app/Providers/FlightStatsServiceProvider.php
public function register(): void
{
    $this->app->singleton(FlightStatsClient::class, fn () => new FlightStatsClient(
        appId:   config('services.flightstats.app_id'),
        appKey:  config('services.flightstats.app_key'),
        sandbox: ! app()->isProduction(),
    ));
}
// config/services.php
'flightstats' => [
    'app_id'  => env('FLIGHTSTATS_APP_ID'),
    'app_key' => env('FLIGHTSTATS_APP_KEY'),
],

First use case: one flight-status lookup by carrier + number + date. It is deliberately narrow and it exercises the three things that break first — auth, the response envelope, and date/timezone handling:

$status = app(FlightStatsClient::class)->getFlightStatus('AA', 100, '2025-01-15');

Get that single call returning parsed data in a tinker session before building anything around it. If auth or payload shape is wrong, you want to learn that with one line, not a service class.

Implementation Patterns

Inject an interface, not the client. Controllers, jobs, and notifications should type-hint a small repository so you can swap in a fake or a second provider later — this is the cheapest insurance against the package being abandoned:

interface FlightStatusRepository {
    public function status(string $carrier, int $flight, string $date): FlightStatus;
}

Bind FlightStatsRepository to the client in the service provider and resolve the interface everywhere.

Normalize at the boundary. The vendor payload is verbose and nested (flightStatuses[], appendix, segment-level equipment and times). Map it to a flat DTO inside the repository and never let the vendor array shape escape into your app. When the API drifts — and it will, on an unmaintained wrapper — you change one mapper, not twenty call sites.

final class FlightStatus {
    public function __construct(
        public readonly string $carrier,
        public readonly int $number,
        public readonly string $scheduledGate,
        public readonly ?string $estimatedGate,
        public readonly string $status, // scheduled|active|landed|cancelled|diverted
        public readonly ?int $delayMinutes,
    ) {}
}

Cache on the natural key with a status-aware TTL. carrier:number:date is your cache key. Past dates are immutable — cache for days or forever. Today's status changes every few minutes — cache 60–300 seconds. Never cache "unknown" results, or a transient error becomes a stuck value.

return Cache::remember("flight:{$carrier}:{$number}:{$date}", $ttl, fn () => $this->fetch(...));

Separate reference data from live data. Airports, airlines, and equipment change rarely. Cache them with long TTLs (hours/days) and refresh on a schedule. Only flight status should be touched on the request path, and only for the specific flight the user asked about.

Push bulk and non-interactive lookups to queues. An ops dashboard refreshing forty flights, or a nightly delay-scan job, should not be doing synchronous HTTP in the request cycle. Queue the job, apply backoff() and a retry cap, and store the last known status with a timestamp so the UI can degrade gracefully.

Keep a sandbox flag in config, not in code. app()->isProduction() in the provider is fine, but expose it as services.flightstats.sandbox so tests and preview deploys can flip it without touching code.

Log the raw payload on parse failure only. In normal operation log carrier/number/date, latency, and status code. When the mapper throws, log the full response body at that point — that is the only time you need it, and capturing it always is noisy and quota-adjacent.

Gotchas and Tips

Do not fight Composer constraints — fork instead. If the bundle (or its transitive client) pins Symfony 2/3-era components, an old Guzzle, or php ^5.6|^7.0, it will not resolve against Laravel 10/11 on PHP 8.2+. A two-minute composer conflict is the signal to vendor the source into app/Services/FlightStats or a private path repository, not to start pinning your whole app backwards.

Timezone and date semantics are the number-one source of "correct code, wrong behavior" bugs. Flight status is keyed on the departure date in the origin airport's local time, not UTC and not the user's timezone. Normalize to the flight's local departure date before keying the cache or calling the API, and document that decision — a status lookup for the wrong date returns a valid but useless (or empty) response.

Distinguish scheduled, estimated, and actual. They are different fields with different update timings. A delay is estimated - scheduled; it is not the same as the gate change. Decide up front which one drives your UI copy and alerts, and keep that mapping in the DTO mapper, not in Blade.

Silent nulls are the failure mode, not exceptions. Small wrappers like this typically return null or an empty flightStatuses array on a bad request, an auth failure, or a schema change — all three look identical downstream. Treat an empty result as "unknown", never as "on time", and log the HTTP status separately so quota-exhaustion 429s are visible.

Quota and auth are the operational risks, not PHP. Credential expiry and token/API-key rotation are the most common silent outages; 429s are the second. Add a circuit breaker or a hard failure switch so a vendor outage degrades to the stale cached value with a visible timestamp rather than a 500 page.

Swap the HTTP client at the boundary. Because the bundle wires Guzzle via the container, in Laravel you are re-providing the transport anyway. Use Laravel's Http client with ->timeout(5)->retry(2, 200) and record fixtures with Http::fake() in tests — do not write tests that hit FlightStats live, and do not assume sandbox credentials are available in CI until you have confirmed them with the vendor.

Extension points worth knowing. The bundle's value is mostly its endpoint mapping and config tree. If you fork, keep the Configuration.php key names identical to your config/services.php block so developers reading either file see the same vocabulary, and expose the base URL so you can point at the sandbox without code changes.

Ramp-up reality check. A mid-level Laravel engineer unfamiliar with Symfony bundle internals needs 3–5 days to strip the DI wiring into a working adapter, plus 2–3 for retries, caching, and observability. If that is more than you can afford this sprint, the honest move is to reimplement the specific endpoint against Http::withHeaders() in a day and use the bundle as documentation only.

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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels