Search by

ijeffro / laravel-airports

ijeffro

IATA airport codes for Laravel: an in-memory lookup API, validation rule and optional database table.

Package info

github.com/FLAIRUK/laravel-airports

pkg:composer/ijeffro/laravel-airports

Statistics

Installs: 3 667

Dependents: 0

Suggesters: 0

Stars: 4

Open Issues: 0

v1.0.0 2026-10-05 09:32 UTC

This package is auto-updated.

Last update: 2026-11-02 13:14:36 UTC


README

Laravel Airports

PHP 8.2+  Laravel 12 or 13  Lint  Tests  Downloads on Packagist  MIT licence  IATA 
 

Laravel Airports — Over 10,000 IATA airport codes (LHR, JFK, DXB, …) for Laravel 12 and 13.

  • No database required. Look airports up through a facade backed by an in-memory dataset.
  • Typed results. Every lookup returns readonly Airport objects in Laravel collections keyed by code.
  • Locations. City, coordinates, type, scheduled service and ICAO code for over 8,400 of them, from OurAirports, and a nearest() lookup.
  • Validation rule. new AirportCode accepts known codes only.
  • Optional table. Publish a migration and seed an airports table when other tables need to reference airports.

📦 Installation · 🚀 Usage · 💾 Database table · 🗂️ Dataset · 🔄 Upgrading



📦 Installation

composer require flairuk/laravel-airports

Requires PHP 8.2 or later with Laravel 12, or PHP 8.3 or later with Laravel 13.

Laravel discovers the service provider and the Airports facade automatically.



🚀 Usage

use FLAIRUK\Airports\Facades\Airports;

Airports::find('lhr');           // Airport { id: 4329, code: "LHR", name: "Heathrow", countryCode: "GB", city: "London", … }
Airports::findOrFail('LHR');     // throws ItemNotFoundException for unknown codes
Airports::exists('JFK');         // true
Airports::findById(4329);

Airports::all();                 // Collection<string, Airport> keyed by code
Airports::inCountry('GB');       // airports in the UK
Airports::search('heathrow');    // matches on name, city or exact code
Airports::codes();               // ['AAA', 'AAB', ...]

Nearest airports

Airports::nearest(51.754, -1.254);                                 // the 5 nearest to Oxford, nearest first
Airports::nearest(51.754, -1.254, limit: 8, scheduledOnly: true);  // LTN, LHR, BHX, … with passenger flights
Airports::nearest(51.754, -1.254, withinKm: 50);                   // only those within 50 km
Airports::nearest(51.754, -1.254, types: ['large', 'medium']);

Airports::find('LHR')->distanceTo(51.754, -1.254);                 // 63.2 (km, great circle)

nearest() returns a collection keyed by code and skips airports without coordinates and closed ones. Each Airport carries city, latitude, longitude, type (large, medium, small, heliport, seaplane, closed or null), scheduled and icao; any of them is null (or false) when OurAirports has no match for the code.

Select options

Airports::options();             // ['LHR' => 'Heathrow', ...] sorted by name
Airports::options('id');         // [4329 => 'Heathrow', ...]

Validation

use FLAIRUK\Airports\Rules\AirportCode;

$request->validate([
    'origin' => ['required', new AirportCode],
    'destination' => ['required', new AirportCode, 'different:origin'],
]);

AirportCode ignores case but different does not, so LHR and lhr would pass as different airports. Uppercase both fields first (for example in a form request's prepareForValidation()) when that matters.

Dependency injection

The facade resolves a singleton FLAIRUK\Airports\Airports, which you can type-hint instead.



💾 Database table (optional)

php artisan airports:install             # publish config + migration, then ask to migrate and seed
php artisan airports:install --migrate   # migrate and seed without asking
php artisan airports:seed            # insert / update (safe to re-run)
php artisan airports:seed --prune    # also delete rows no longer in the dataset

You can also call the seeder from your own DatabaseSeeder:

$this->call(\FLAIRUK\Airports\Database\AirportsSeeder::class);

Query the table through the bundled Eloquent model:

use FLAIRUK\Airports\Models\Airport;

Airport::code('LHR')->first();
Airport::inCountry('GB')->scheduled()->orderBy('name')->get();

The table name and connection come from AIRPORTS_TABLE and AIRPORTS_DB_CONNECTION, or from the published config.



🗂️ Rebuilding the dataset

data/airports.php keeps the original IATA rows (id, code, name, country_code) and adds the location fields from OurAirports (public domain), matched on IATA code and country. To refresh them from a newer export:

curl -sSfLo /tmp/airports.csv https://davidmegginson.github.io/ourairports-data/airports.csv
php bin/build-dataset.php /tmp/airports.csv
composer test

The script only reads the original four fields, so re-running it is safe; it prints how many rows matched.



🔄 Upgrading

From 1.0 to 1.1

1.1 is additive. If you seeded the table, add the new columns and re-seed:

php artisan airports:install   # publishes the upgrade migration next to your 1.0 one
php artisan migrate
php artisan airports:seed

Until you migrate, airports:seed keeps filling the 1.0 columns only.

From dev-master

Version 1.0 is a rewrite. Breaking changes:

dev-master 1.0
Package ijeffro/laravel-airports flairuk/laravel-airports
ijeffro\Airports\… namespace FLAIRUK\Airports\…
Facade ijeffro\Airports\AirportsFacade FLAIRUK\Airports\Facades\Airports (auto-discovered)
Airports::getList($sort) (array) Airports::all()->sortBy($property, SORT_NATURAL | SORT_FLAG_CASE) (Collection of Airport; properties are camelCase, e.g. countryCode)
Airports::getOne($id) Airports::findById($id) or Airports::find($code)
Airports::getListForSelect() (keyed by id) Airports::options('id')
php artisan airports:migration php artisan airports:install / airports:seed
Config key airports.table_name airports.table

Row ids and columns (code, name, country_code) are unchanged, so existing tables and foreign keys stay valid.



🧪 Testing

composer test



📄 License

MIT. See LICENSE.