maxiewright / laravel-tt-addresses
Trinidad and Tobago address management for Laravel applications - divisions, cities, and address formatting
Fund package maintenance!
MaxieWright
Installs: 15
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/maxiewright/laravel-tt-addresses
Requires
- php: ^8.4
- illuminate/contracts: ^11.0||^12.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/boost: ^1.8
- laravel/pint: ^1.26
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^10.0.0||^9.0.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
README
A Laravel package providing Trinidad and Tobago administrative divisions and cities/towns/villages for address management. Includes all 15 administrative divisions and 500+ communities.
Features
- ๐๏ธ 15 Administrative Divisions - All Regional Corporations, Boroughs, City Corporations, and Tobago
- ๐๏ธ 500+ Cities/Towns/Villages - Comprehensive coverage of Trinidad and Tobago communities
- ๐ Eloquent Relationships - Ready-to-use models with proper relationships
- ๐จ Filament Support - Enums implement
HasLabelfor seamless Filament integration - โ๏ธ Configurable - Customise table names to avoid conflicts
- ๐งช Tested - Full test coverage with Pest
Requirements
- PHP 8.4+
- Laravel 10.x, 11.x, or 12.x
Installation
Install the package via Composer:
composer require maxiewright/laravel-tt-addresses
Quick Install (Recommended)
Run the install command which will guide you through the setup:
php artisan tt-addresses:install
This will:
- Publish the configuration file
- Publish the migrations
- Optionally run the migrations
Manual Installation
If you prefer to install manually:
# Publish the config file php artisan vendor:publish --tag="laravel-tt-addresses-config" # Publish the migrations php artisan vendor:publish --tag="laravel-tt-addresses-migrations" # Run migrations php artisan migrate
Seed the Data
After running migrations, seed the divisions and cities:
php artisan db:seed --class="MaxieWright\TrinidadAndTobagoAddresses\Database\Seeders\DivisionSeeder" php artisan db:seed --class="MaxieWright\TrinidadAndTobagoAddresses\Database\Seeders\CitySeeder"
Or add them to your DatabaseSeeder.php:
use MaxieWright\TrinidadAndTobagoAddresses\Database\Seeders\DivisionSeeder; use MaxieWright\TrinidadAndTobagoAddresses\Database\Seeders\CitySeeder; public function run(): void { $this->call([ DivisionSeeder::class, CitySeeder::class, // ... your other seeders ]); }
Configuration
The configuration file is located at config/tt-addresses.php:
return [ // Customise table names if they conflict with existing tables 'tables' => [ 'divisions' => 'tt_divisions', 'cities' => 'tt_cities', ], // ISO country code 'country_code' => 'TT', ];
Usage
Basic Queries
use MaxieWright\TrinidadAndTobagoAddresses\Models\Division; use MaxieWright\TrinidadAndTobagoAddresses\Models\City; use MaxieWright\TrinidadAndTobagoAddresses\Enums\DivisionType; // Get all divisions $divisions = Division::all(); // Get only Trinidad divisions $trinidadDivisions = Division::trinidad()->get(); // Get only Tobago $tobago = Division::tobago()->first(); // Get divisions by type $boroughs = Division::ofType(DivisionType::Borough)->get(); $regionalCorporations = Division::ofType(DivisionType::RegionalCorporation)->get(); // Get all cities in a division $chaguanasCities = Division::where('abbreviation', 'CHA') ->first() ->cities; // Find a city $portOfSpain = City::where('name', 'Port-of-Spain')->first(); $portOfSpain->division->name; // "Port of Spain" $portOfSpain->island; // "Trinidad" // Get full location string $city = City::where('name', 'Scarborough')->first(); $city->full_location; // "Scarborough, Tobago"
Division Types
The package includes a DivisionType enum with the following values:
| Value | Label | Island |
|---|---|---|
RegionalCorporation |
Regional Corporation | Trinidad |
Borough |
Borough | Trinidad |
CityCorporation |
City Corporation | Trinidad |
Ward |
Ward | Tobago |
use MaxieWright\TrinidadAndTobagoAddresses\Enums\DivisionType; $type = DivisionType::RegionalCorporation; $type->label(); // "Regional Corporation" $type->island(); // "Trinidad" // Filament support $type->getLabel(); // "Regional Corporation"
Adding Addresses to Your Models
Use the HasTrinidadAndTobagoAddress trait on any model that needs Trinidad and Tobago address fields:
use Illuminate\Database\Eloquent\Model; use MaxieWright\TrinidadAndTobagoAddresses\Concerns\HasTrinidadAndTobagoAddress; class Customer extends Model { use HasTrinidadAndTobagoAddress; protected $fillable = [ 'name', 'address_line_1', 'address_line_2', 'division_id', 'city_id', ]; }
Create a migration for your model:
Schema::create('customers', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('address_line_1')->nullable(); $table->string('address_line_2')->nullable(); $table->foreignId('division_id') ->nullable() ->constrained(config('laravel-tt-addresses.tables.divisions')) ->nullOnDelete(); $table->foreignId('city_id') ->nullable() ->constrained(config('laravel-tt-addresses.tables.cities')) ->nullOnDelete(); $table->timestamps(); });
Then use it:
$customer = Customer::create([ 'name' => 'John Doe', 'address_line_1' => '123 Main Street', 'division_id' => 11, // Chaguanas 'city_id' => 88, // Chaguanas city ]); $customer->division->name; // "Chaguanas" $customer->city->name; // "Chaguanas" $customer->formatted_address; // "123 Main Street, Chaguanas, Chaguanas" $customer->island; // "Trinidad"
Filament Integration
The models and enums are designed to work seamlessly with FilamentPHP.
Select Fields
use Filament\Forms\Components\Select; use MaxieWright\TrinidadAndTobagoAddresses\Models\Division; use MaxieWright\TrinidadAndTobagoAddresses\Models\City; // Division select Select::make('division_id') ->label('Division') ->options(Division::pluck('name', 'id')) ->searchable() ->preload() ->live(), // City select (filtered by division) Select::make('city_id') ->label('City/Town/Village') ->options(function (callable $get) { $divisionId = $get('division_id'); if (!$divisionId) { return City::pluck('name', 'id'); } return City::where('division_id', $divisionId) ->pluck('name', 'id'); }) ->searchable() ->preload(),
Using the Enum in Filters
use Filament\Tables\Filters\SelectFilter; use MaxieWright\TrinidadAndTobagoAddresses\Enums\DivisionType; SelectFilter::make('type') ->options(DivisionType::class),
Administrative Divisions Reference
| ID | Name | Type | Abbreviation |
|---|---|---|---|
| 1 | Couva/Tabaquite/Talparo | Regional Corporation | CTT |
| 2 | Diego Martin | Regional Corporation | DMN |
| 3 | Mayaro/Rio Claro | Regional Corporation | MRC |
| 4 | Penal/Debe | Regional Corporation | PED |
| 5 | Princes Town | Regional Corporation | PRT |
| 6 | Sangre Grande | Regional Corporation | SGE |
| 7 | San Juan/Laventille | Regional Corporation | SJL |
| 8 | Siparia | Regional Corporation | SIP |
| 9 | Tunapuna/Piarco | Regional Corporation | TUP |
| 10 | Arima | Borough | ARI |
| 11 | Chaguanas | Borough | CHA |
| 12 | Point Fortin | Borough | PTF |
| 13 | Port of Spain | City Corporation | POS |
| 14 | San Fernando | City Corporation | SFO |
| 15 | Tobago | Ward | TOB |
API Reference
Division Model
Attributes
name- Division nametype- DivisionType enumabbreviation- Short code (e.g., "POS", "CHA")island- "Trinidad" or "Tobago"
Relationships
cities()- HasMany relationship to City
Scopes
scopeTrinidad()- Filter to Trinidad divisions onlyscopeTobago()- Filter to Tobago onlyscopeOfType(DivisionType $type)- Filter by division typescopeSearch(string $search)- Search by name or abbreviation
Methods
isTobago(): bool- Check if division is in TobagoisTrinidad(): bool- Check if division is in Trinidad
Accessors
full_name- Returns "Division Name (Type Label)"
City Model
Attributes
name- City/town/village namedivision_id- Foreign key to Division
Relationships
division()- BelongsTo relationship to Division
Scopes
scopeTrinidad()- Filter to Trinidad cities onlyscopeTobago()- Filter to Tobago cities onlyscopeInDivision(int|Division $division)- Filter by divisionscopeSearch(string $search)- Search by name
Methods
isTobago(): bool- Check if city is in TobagoisTrinidad(): bool- Check if city is in Trinidad
Accessors
full_location- Returns "City Name, Division Name"island- Returns "Trinidad" or "Tobago" via division
HasTrinidadAndTobagoAddress Trait
Relationships
division()- BelongsTo relationship to Divisioncity()- BelongsTo relationship to City
Methods
isInTobago(): bool- Check if address is in TobagoisInTrinidad(): bool- Check if address is in TrinidadhasCompleteAddress(): bool- Check if address has all required fields
Accessors
formatted_address- Returns formatted single-line address stringformatted_address_multiline- Returns formatted multi-line address stringisland- Returns "Trinidad" or "Tobago"country_code- Returns "TT"
Testing
composer test
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Contributions are welcome! Please see CONTRIBUTING for details.
Adding Missing Cities
If you notice a city/town/village is missing, please submit a pull request with:
- The city name (correctly spelled)
- The correct
division_id(see Administrative Divisions Reference)
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.
Made with โค๏ธ for ๐น๐น developers.