Installation Guide
Comprehensive guide for installing Filament Address Pro in different scenarios.
Table of Contents
- Getting Access
- CI/CD Environments
- Fresh Laravel App (No Filament Panel)
- Existing Filament App
- Customizing Form Appearance
- Database Setup
- Troubleshooting
Getting Access
Filament Address Pro is a private package distributed via a licensed Composer repository. You must configure repository access before running composer require.
Beta Access
Beta access is available by invitation during the pre-release period. Beta licenses expire after 180 days and are for evaluation purposes only.
If you have received a beta invitation:
You will receive an email with your license key and a link to your license portal.
Add the Anystack repository to your project's
composer.json:
{
"repositories": [
{
"type": "composer",
"url": "https://filament-address-pro.composer.sh"
}
]
}- Store your credentials in your global Composer auth file so they are never inside a project directory:
composer global config http-basic.filament-address-pro.composer.sh your-email@example.com YOUR-BETA-LICENSE-KEYThis writes to ~/.composer/auth.json (or ~/.config/composer/auth.json on Linux), which applies to all your projects automatically and is never committed to version control.
You are now ready to run composer require viewflex/filament-address-pro.
Note: Beta licenses expire after 180 days. You will need a Solo or Agency license to continue using the package after the beta period ends.
Solo & Agency Access
Purchase a license at viewflex.net and note your license key.
Add the Anystack repository to your project's
composer.json:
{
"repositories": [
{
"type": "composer",
"url": "https://filament-address-pro.composer.sh"
}
]
}- Store your credentials in your global Composer auth file so they are never inside a project directory:
composer global config http-basic.filament-address-pro.composer.sh your-email@example.com YOUR-LICENSE-KEYThis writes to ~/.composer/auth.json (or ~/.config/composer/auth.json on Linux), which applies to all your projects automatically and is never committed to version control.
Solo licenses cover up to two production installations — enough for your own site and a client-managed staging environment on a separate domain. Agency licenses cover up to 50 production installations. Both plans include unlimited development and staging environments and CI/CD pipelines.
Moving a Solo license to a different domain: Run the deactivation command on the old server first, then let the new installation activate on next boot:
php artisan addresses:license:deactivateThis calls the Anystack API to release the activation slot and clears the cached activation record. On the next request to your new installation, the license activates automatically using the domain from APP_URL. If you cannot access the old server, email support@viewflex.net — we can release the activation slot manually.
Updates & Support: Your license includes one year of updates and support from the date of purchase. After that period, you may continue using the last version released within your paid period. Renew at any time to restore access to new releases and support.
After expiry: Composer will silently stop showing newer versions —
composer updatewill report nothing to update, and fresh installs will resolve to your last accessible release without any error. If updates stop appearing or an install resolves to an older version than expected, check your renewal status at your license portal.
CI/CD Environments
In CI/CD pipelines, credentials cannot be stored in ~/.composer/auth.json on a local machine. Instead, use Composer's COMPOSER_AUTH environment variable, which Composer reads automatically during composer install.
Store the value as a secret in your CI/CD platform — never in your repository or workflow files directly.
The secret value is a JSON string:
{"http-basic":{"filament-address-pro.composer.sh":{"username":"your-email@example.com","password":"YOUR-LICENSE-KEY"}}}GitHub Actions
Add a repository secret named COMPOSER_AUTH under Settings → Secrets and variables → Actions, then reference it in your workflow:
- name: Cache Composer dependencies
uses: actions/cache@v4
with:
path: ~/.composer/cache/files
key: ${{ runner.os }}-composer-${{ hashFiles('composer.json') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies
env:
COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }}
run: composer install --no-interaction --prefer-dist --optimize-autoloaderThe Composer cache stores resolved package archives, not credentials. Caching is safe and does not expose your license key.
GitLab CI
Add a CI/CD variable named COMPOSER_AUTH under Settings → CI/CD → Variables (mark it as masked), then reference it in your pipeline:
composer:install:
script:
- composer install --no-interaction --prefer-dist --optimize-autoloader
variables:
COMPOSER_AUTH: $COMPOSER_AUTHBitbucket Pipelines
Add a repository variable named COMPOSER_AUTH under Repository settings → Repository variables, then reference it:
- step:
script:
- composer install --no-interaction --prefer-dist --optimize-autoloader
variables:
COMPOSER_AUTH: $COMPOSER_AUTHOther CI Systems
Any CI system that supports secret environment variables works the same way. Set COMPOSER_AUTH as a secret in the platform, ensure it is exposed to the shell that runs composer install, and never echo or log it.
Solo Licenses in CI/CD
CI/CD pipelines — including builds, test runs, and staging deployments — do not count as activations and are not restricted by the Solo single-site limit. Only production environments are subject to the activation limit.
Scenario 1: Fresh Laravel App (No Filament Panel)
Installing in a brand new Laravel application that doesn't have Filament yet.
Step 1: Install Laravel
composer create-project laravel/laravel my-app
cd my-appStep 2: Install Filament Panel
composer require filament/filament
php artisan filament:install --panelsThis creates an admin panel at /admin with:
- Service Provider:
app/Providers/Filament/AdminPanelProvider.php - Login/Registration routes
- User authentication
Step 3: Create Your First User
php artisan make:filament-userStep 4: Install Filament Address Pro
composer require viewflex/filament-address-proStep 5: Publish Configuration
php artisan vendor:publish --tag=filament-address-configThis creates config/addresses.php.
Step 6: Configure Environment
Add to .env:
# Optional (but recommended): Google Maps API Keys
# Without these keys, Places autocomplete, map preview, and geocoding are disabled.
# Manual address entry still works — fields and country/subdivision dropdowns are fully functional.
# Production (recommended): two keys with separate restrictions
GOOGLE_MAPS_SERVER_KEY=your_server_key_here # Geocoding API + Maps Static API + Address Validation API — restrict to server IP
GOOGLE_MAPS_BROWSER_KEY=your_browser_key_here # Maps JavaScript API + Places API — restrict to your domain
# Simple setup / local dev: single key as fallback (both above default to this when not set)
# GOOGLE_MAPS_API_KEY=your_google_maps_api_key_here
# Optional: Address Verification
# Set to 'none' to skip verification (no API calls, no log noise).
# Set to 'auto' to use the best available provider for each country.
ADDRESS_VERIFICATION_ENABLED=true
ADDRESS_VERIFICATION_PROVIDER=auto
# Optional: USPS (Free for US addresses)
USPS_CONSUMER_KEY=your_usps_consumer_key
USPS_CONSUMER_SECRET=your_usps_consumer_secretGetting Google Maps API Keys
- Go to Google Cloud Console
- Create a new project or select existing
- Enable these APIs: Geocoding API, Maps JavaScript API, Places API, Maps Static API, and optionally Address Validation API
- Create two API keys:
- Server key — restrict to your server's IP address; enable Geocoding API, Maps Static API, Address Validation API
- Browser key — restrict to your domain (
yoursite.com/*); enable Maps JavaScript API, Places API
- Set
GOOGLE_MAPS_SERVER_KEYandGOOGLE_MAPS_BROWSER_KEYin.env
Single-key fallback: For local dev or simple setups, set only
GOOGLE_MAPS_API_KEY— both server and browser keys default to this value. Not recommended for production (a single unrestricted key is harder to secure).
Getting USPS Credentials (Optional)
- Register at developer.usps.com
- Create an OAuth 2.0 app
- Get Consumer Key and Consumer Secret
- Free for US addresses, authoritative data
Step 7: Run Migrations & Seed Data
Option A — Interactive install wizard (recommended):
php artisan addresses:installThis wizard runs migrations, seeds country data, and walks you through panel registration — all in one step.
Option B — Manual steps:
# Run package migrations (auto-discovered from the package)
php artisan migrate
# Seed country data (256 countries + subdivisions)
php artisan db:seed --class="Viewflex\FilamentAddress\Database\Seeders\CountriesDomainSeeder"This seeds:
- 256 countries and territories
- 17,000+ subdivisions (states, provinces, districts)
- Country address definitions
- Country groups (EU, ASEAN, etc.)
Note: Seeding takes ~2-3 minutes. Safe to re-run (uses updateOrCreate).
Troubleshooting: If
php artisan migratereports "Nothing to migrate", run migrations with an explicit path:bashphp artisan migrate --path=vendor/viewflex/filament-address-pro/database/migrations
Step 8: Add Trait to Your Models
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Viewflex\FilamentAddress\Concerns\HasAddresses;
class User extends Authenticatable
{
use HasAddresses;
// Now you have:
// - addresses() relationship
// - primaryAddress() method
// - setPrimaryAddress($address) method
}Add the trait to any model that needs addresses: User, Customer, Company, Location, etc.
Step 8b: Register Entity Types for the Address Resource
The standalone Addresses resource uses a "Belongs To" dropdown to associate addresses with your models. You must register each model class in config/addresses.php:
// config/addresses.php
'import' => [
'allowed_entity_types' => [
'App\Models\User',
'App\Models\Customer', // any model using HasAddresses
],
],Without this, the create form will show an empty "Belongs To" dropdown and addresses cannot be created from the standalone resource. (When using AddressesRelationManager on a resource, this is not needed — the owner is set automatically.)
Step 9: Create a Resource with Address Management
php artisan make:filament-resource CustomerEdit app/Filament/Resources/CustomerResource.php:
<?php
namespace App\Filament\Resources;
use App\Models\Customer;
use Filament\Resources\Resource;
use Filament\Forms;
use Filament\Tables;
use Viewflex\FilamentAddress\Filament\RelationManagers\AddressesRelationManager;
class CustomerResource extends Resource
{
protected static ?string $model = Customer::class;
public static function form(Forms\Form $form): Forms\Form
{
return $form->schema([
Forms\Components\TextInput::make('name')->required(),
Forms\Components\TextInput::make('email')->email()->required(),
Forms\Components\TextInput::make('phone'),
]);
}
public static function table(Tables\Table $table): Tables\Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('name')->searchable(),
Tables\Columns\TextColumn::make('email')->searchable(),
Tables\Columns\TextColumn::make('phone'),
])
->filters([])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
// Add the Address Relation Manager
public static function getRelations(): array
{
return [
AddressesRelationManager::class,
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListCustomers::route('/'),
'create' => Pages\CreateCustomer::route('/create'),
'edit' => Pages\EditCustomer::route('/{record}/edit'),
];
}
}Step 10: Test It!
php artisan serveVisit http://localhost:8000/admin and:
- Create a customer
- Go to the "Addresses" tab
- Click "New Address"
- Try entering an address!
Scenario 2: Existing Filament App
You already have Filament installed and resources set up.
Quick Start
# 1. Install package
composer require viewflex/filament-address-pro
# 2. Publish config
php artisan vendor:publish --tag=filament-address-config
# 3. Add API keys to .env (optional — manual entry works without them)
echo "GOOGLE_MAPS_SERVER_KEY=your_server_key_here" >> .env
echo "GOOGLE_MAPS_BROWSER_KEY=your_browser_key_here" >> .env
# 4. Run migrations (auto-discovered from the package)
php artisan migrate
# 5. Seed country data
php artisan db:seed --class="Viewflex\FilamentAddress\Database\Seeders\CountriesDomainSeeder"
# 6. Add trait to your modelsThen add the HasAddresses trait to any model and the AddressesRelationManager to any resource.
Customizing Form Appearance
You can customize the address form without editing the package files.
Method 1: CSS Classes (Non-Invasive)
Create a custom theme in your AdminPanelProvider:
use Filament\Support\Colors\Color;
public function panel(Panel $panel): Panel
{
return $panel
->id('admin')
->path('admin')
->colors([
'primary' => Color::Amber,
])
->viteTheme('resources/css/filament/admin/theme.css'); // Custom CSS
}Create resources/css/filament/admin/theme.css:
@import '/vendor/filament/filament/resources/css/theme.css';
@config 'tailwind.config.js';
/* Customize address form sections */
.fi-fo-section[data-address-subdivisions] {
@apply border-l-4 border-blue-500;
}
.fi-fo-section[data-street-address] {
@apply border-l-4 border-green-500;
}
/* Customize address preview */
.fi-fo-field-wrp[data-address-preview] {
@apply bg-gray-50 dark:bg-gray-900 rounded-lg p-4;
}
/* Make geocoding notifications more subtle */
.fi-no-notification[data-geocoding-result] {
@apply opacity-90;
}Run: npm run build to compile.
Method 2: Extend the Form Class
Create your own form class that extends the package:
<?php
namespace App\Filament\Schemas;
use Viewflex\FilamentAddress\Filament\Schemas\AddressForm as BaseAddressForm;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
class CustomAddressForm extends BaseAddressForm
{
public static function schema(): array
{
$schema = parent::schema();
// Add custom fields
$schema[] = TextInput::make('delivery_notes')
->label('Delivery Instructions')
->helperText('Gate codes, special instructions, etc.')
->maxLength(500)
->columnSpanFull();
$schema[] = Toggle::make('is_billing_address')
->label('Use as billing address')
->default(false);
return $schema;
}
}Then use your custom form in resources:
use App\Filament\Schemas\CustomAddressForm;
public function form(Form $form): Form
{
return $form->schema([
...CustomAddressForm::schema(),
]);
}Method 3: Modify Individual Fields
Wrap the schema and modify specific fields:
use Viewflex\FilamentAddress\Filament\Schemas\AddressForm;
use Filament\Forms\Components\Section;
public function form(Form $form): Form
{
$addressFields = AddressForm::schema();
// Wrap in a custom section
return $form->schema([
Section::make('Customer Address')
->description('Enter the customer\'s mailing address')
->icon('heroicon-o-map-pin')
->collapsible()
->schema($addressFields),
]);
}Method 4: Override Specific Field Properties
Use modifyQueryUsing() to customize field behavior:
use Filament\Forms\Get;
use Filament\Forms\Set;
$schema = AddressForm::schema();
// Find and modify the country field
foreach ($schema as &$section) {
if ($section instanceof Section) {
foreach ($section->getChildComponents() as $field) {
// Example: Make country default to US
if ($field->getName() === 'country_code') {
$field->default('US');
}
// Example: Hide postal code for certain countries
if ($field->getName() === 'postal_code') {
$field->hidden(fn (Get $get) => in_array($get('country_code'), ['IE', 'HK']));
}
}
}
}
return $form->schema($schema);Method 5: Custom Relation Manager
Extend the package relation manager for full control:
<?php
namespace App\Filament\RelationManagers;
use Viewflex\FilamentAddress\Filament\RelationManagers\AddressesRelationManager as BaseAddressesRelationManager;
use Filament\Forms;
class CustomAddressesRelationManager extends BaseAddressesRelationManager
{
// Change the tab label
protected static ?string $title = 'Locations';
// Customize the form
public function form(Forms\Form $form): Forms\Form
{
return parent::form($form)
->columns(3); // Change column layout
}
// Add custom table columns
public function table(Tables\Table $table): Tables\Table
{
return parent::table($table)
->columns([
// Override with custom columns
Tables\Columns\TextColumn::make('address_line_1')
->label('Street')
->searchable()
->sortable(),
Tables\Columns\TextColumn::make('locality')
->label('City')
->searchable(),
Tables\Columns\IconColumn::make('is_primary')
->boolean()
->trueColor('success'),
]);
}
}Database Setup
Understanding the Schema
The package creates these tables:
countries Table
- Country master data (256 countries)
- ISO codes, names, flags
- Dependent territories have parent IDs
country_subdivisions Table
- Hierarchical administrative divisions
- Up to 3 levels: State → City → District
- Including local names, ISO codes and abbreviations
- 17,000+ records globally
country_address_definitions Table
- Country-specific address formats
- Field requirements and labels
- Postal code validation patterns
country_groups Table
- Regional groupings (EU, ASEAN, etc.)
country_group_memberspivot table
addresses Table
Polymorphic, works with any model. Key columns:
| Column | Description |
|---|---|
addressable_type, addressable_id | Polymorphic owner relationship |
label | Address label (Home, Work, Billing, etc.) |
country_code | ISO 3166-1 alpha-2 (e.g. US, GB) |
administrative_area, locality, dependent_locality | Subdivision text values |
*_id variants | Subdivision ULID references for data integrity |
address_line_1, address_line_2 | Street address |
postal_code | ZIP or postal code |
bldg, box, ico | Building, PO Box, in-care-of fields |
lat, lon | Geographic coordinates |
is_primary | One primary address per entity |
is_verified | Verified by an external service |
needs_review | Verification found suggested changes awaiting user review |
verification_provider | Which service verified: usps, google (v1.1+: smarty, loqate) |
verified_at | When verification occurred |
verification_metadata | Raw verification response and context (JSON) |
quality_score | 0–100 quality score (auto-calculated) |
Existing addresses Table?
If your application already has an addresses table with a different schema, set a custom table name in your .env:
ADDRESS_TABLE_NAME=filament_addressesThe package model and migration will both use this name automatically. You can then run php artisan migrate to create the filament_addresses table without touching your existing one.
Customizing Migrations (Optional)
If you need to customize the tables:
# Publish migrations to your app
php artisan vendor:publish --tag=filament-address-migrations
# Edit the migrations in database/migrations/
# Then run them
php artisan migrateNote: Only publish migrations if you need to customize them. By default, they auto-load from the package.
Troubleshooting
"Class Not Found" Errors
# Clear all caches
php artisan optimize:clear
# Dump autoload
composer dump-autoload
# Restart server
php artisan serveGeocoding Not Working
Check API key:
php artisan tinker
>>> config('addresses.google_maps_api_key')Enable APIs in Google Cloud:
- Geocoding API
- Maps JavaScript API
- Places API (for map place search)
Check API restrictions:
- Make sure your domain/IP is allowed
Verification Failing
Check provider configuration:
php artisan tinker
>>> config('addresses.verification')For USPS issues:
- Verify Consumer Key/Secret
- Check that address is in US
- Review
/storage/logs/laravel.logfor USPS errors
For Google issues:
- Enable Address Validation API
- Check billing is enabled
- Review error messages in logs
Subdivisions Not Loading
Verify data is seeded:
php artisan tinker
>>> \Viewflex\FilamentAddress\Models\Country::count()
>>> \Viewflex\FilamentAddress\Models\CountrySubdivision::count()Re-seed if needed:
php artisan db:seed --class="Viewflex\FilamentAddress\Database\Seeders\CountriesDomainSeeder"Performance Issues
Cache subdivision queries:
// config/addresses.php
'cache' => [
'enabled' => true,
'ttl' => 3600, // 1 hour
],Use eager loading:
$users = User::with('addresses', 'addresses.country')->get();Next Steps
- Read the main README.md for full feature documentation
- Review configuration options
- Check out customization examples
Support
For issues or questions:
- 📧 Email: support@viewflex.net
- 📖 Documentation: [Link]
- 🐛 Bug Reports: [GitHub Issues]
Built with ❤️ by Viewflex