Skip to content

Installation Guide

Comprehensive guide for installing Filament Address Pro in different scenarios.

Table of Contents

  1. Getting Access
  2. CI/CD Environments
  3. Fresh Laravel App (No Filament Panel)
  4. Existing Filament App
  5. Customizing Form Appearance
  6. Database Setup
  7. 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:

  1. You will receive an email with your license key and a link to your license portal.

  2. Add the Anystack repository to your project's composer.json:

json
{
    "repositories": [
        {
            "type": "composer",
            "url": "https://filament-address-pro.composer.sh"
        }
    ]
}
  1. Store your credentials in your global Composer auth file so they are never inside a project directory:
bash
composer global config http-basic.filament-address-pro.composer.sh your-email@example.com YOUR-BETA-LICENSE-KEY

This 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

  1. Purchase a license at viewflex.net and note your license key.

  2. Add the Anystack repository to your project's composer.json:

json
{
    "repositories": [
        {
            "type": "composer",
            "url": "https://filament-address-pro.composer.sh"
        }
    ]
}
  1. Store your credentials in your global Composer auth file so they are never inside a project directory:
bash
composer global config http-basic.filament-address-pro.composer.sh your-email@example.com YOUR-LICENSE-KEY

This 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:

bash
php artisan addresses:license:deactivate

This 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 update will 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:

yaml
- 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-autoloader

The 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:

yaml
composer:install:
  script:
    - composer install --no-interaction --prefer-dist --optimize-autoloader
  variables:
    COMPOSER_AUTH: $COMPOSER_AUTH

Bitbucket Pipelines

Add a repository variable named COMPOSER_AUTH under Repository settings → Repository variables, then reference it:

yaml
- step:
    script:
      - composer install --no-interaction --prefer-dist --optimize-autoloader
    variables:
      COMPOSER_AUTH: $COMPOSER_AUTH

Other 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

bash
composer create-project laravel/laravel my-app
cd my-app

Step 2: Install Filament Panel

bash
composer require filament/filament
php artisan filament:install --panels

This 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

bash
php artisan make:filament-user

Step 4: Install Filament Address Pro

bash
composer require viewflex/filament-address-pro

Step 5: Publish Configuration

bash
php artisan vendor:publish --tag=filament-address-config

This creates config/addresses.php.

Step 6: Configure Environment

Add to .env:

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_secret

Getting Google Maps API Keys

  1. Go to Google Cloud Console
  2. Create a new project or select existing
  3. Enable these APIs: Geocoding API, Maps JavaScript API, Places API, Maps Static API, and optionally Address Validation API
  4. 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
  5. Set GOOGLE_MAPS_SERVER_KEY and GOOGLE_MAPS_BROWSER_KEY in .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)

  1. Register at developer.usps.com
  2. Create an OAuth 2.0 app
  3. Get Consumer Key and Consumer Secret
  4. Free for US addresses, authoritative data

Step 7: Run Migrations & Seed Data

Option A — Interactive install wizard (recommended):

bash
php artisan addresses:install

This wizard runs migrations, seeds country data, and walks you through panel registration — all in one step.

Option B — Manual steps:

bash
# 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 migrate reports "Nothing to migrate", run migrations with an explicit path:

bash
php artisan migrate --path=vendor/viewflex/filament-address-pro/database/migrations

Step 8: Add Trait to Your Models

php
<?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:

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

bash
php artisan make:filament-resource Customer

Edit app/Filament/Resources/CustomerResource.php:

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!

bash
php artisan serve

Visit http://localhost:8000/admin and:

  1. Create a customer
  2. Go to the "Addresses" tab
  3. Click "New Address"
  4. Try entering an address!

Scenario 2: Existing Filament App

You already have Filament installed and resources set up.

Quick Start

bash
# 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 models

Then 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:

php
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:

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
<?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:

php
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:

php
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:

php
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
<?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_members pivot table

addresses Table

Polymorphic, works with any model. Key columns:

ColumnDescription
addressable_type, addressable_idPolymorphic owner relationship
labelAddress label (Home, Work, Billing, etc.)
country_codeISO 3166-1 alpha-2 (e.g. US, GB)
administrative_area, locality, dependent_localitySubdivision text values
*_id variantsSubdivision ULID references for data integrity
address_line_1, address_line_2Street address
postal_codeZIP or postal code
bldg, box, icoBuilding, PO Box, in-care-of fields
lat, lonGeographic coordinates
is_primaryOne primary address per entity
is_verifiedVerified by an external service
needs_reviewVerification found suggested changes awaiting user review
verification_providerWhich service verified: usps, google (v1.1+: smarty, loqate)
verified_atWhen verification occurred
verification_metadataRaw verification response and context (JSON)
quality_score0–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:

env
ADDRESS_TABLE_NAME=filament_addresses

The 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:

bash
# Publish migrations to your app
php artisan vendor:publish --tag=filament-address-migrations

# Edit the migrations in database/migrations/
# Then run them
php artisan migrate

Note: Only publish migrations if you need to customize them. By default, they auto-load from the package.


Troubleshooting

"Class Not Found" Errors

bash
# Clear all caches
php artisan optimize:clear

# Dump autoload
composer dump-autoload

# Restart server
php artisan serve

Geocoding Not Working

Check API key:

bash
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:

bash
php artisan tinker
>>> config('addresses.verification')

For USPS issues:

  • Verify Consumer Key/Secret
  • Check that address is in US
  • Review /storage/logs/laravel.log for 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:

bash
php artisan tinker
>>> \Viewflex\FilamentAddress\Models\Country::count()
>>> \Viewflex\FilamentAddress\Models\CountrySubdivision::count()

Re-seed if needed:

bash
php artisan db:seed --class="Viewflex\FilamentAddress\Database\Seeders\CountriesDomainSeeder"

Performance Issues

Cache subdivision queries:

php
// config/addresses.php
'cache' => [
    'enabled' => true,
    'ttl' => 3600, // 1 hour
],

Use eager loading:

php
$users = User::with('addresses', 'addresses.country')->get();

Next Steps

Support

For issues or questions:


Built with ❤️ by Viewflex

Released under a commercial license.