fndmiranda/simple-address
Laravel package for Brazilian addresses: ships migrations, models and seeders for states and cities, plus a CEP (zip code) lookup service that fills address data automatically. Simple to install and easy to integrate into any Eloquent application.
Install the package:
composer require fndmiranda/simple-address
Publish config and migrations:
php artisan vendor:publish --provider="Fndmiranda\SimpleAddress\SimpleAddressServiceProvider"
php artisan migrate
If the provider is not auto-listed, run php artisan vendor:publish and select the simple-address config/migration tags.
Review config/simple-address.php immediately. Set the default postcode driver, any API keys, geocoder driver, table names, and model overrides before building on top of it.
Add the package’s address trait to the model that owns addresses, usually User or Company:
use Fndmiranda\SimpleAddress\Traits\Addressable; // confirm exact trait name in README
class User extends Authenticatable
{
use Addressable;
}
First use case: a profile address form with postcode autofill. When the user enters a CEP/postcode, call your endpoint, use the package’s postcode lookup to hydrate street/district/city/state, then save the address through the model relationship.
Look first at:
config/simple-address.phpsrc/Modelssrc/Services or postcode/gecoder driver classesPolymorphic address ownership Use the package trait on every entity that can own an address. This keeps addresses reusable across users, companies, orders, and stores:
$user->addresses()->create($validated);
$company->addresses()->create($validated);
Postcode lookup as an application service Wrap the package lookup in your own action so validation, normalization, caching, and fallback logic stay in one place:
class ResolvePostcode
{
public function __construct(private Postcode $postcode) {}
public function handle(string $postcode): array
{
$postcode = preg_replace('/\D/', '', $postcode);
return Cache::remember(
"postcode:{$postcode}",
now()->addDay(),
fn () => $this->postcode->search($postcode)->toArray()
);
}
}
Create or update addresses without losing user input Treat postcode API data as defaults, not authoritative overrides. Users often correct number, complement, and district:
$lookup = $resolver->handle($data['postcode']);
$address = $user->addresses()->updateOrCreate(
['postcode' => $data['postcode'], 'is_default' => true],
array_merge($lookup, array_filter($data))
);
Geocode asynchronously Geocoding is slow and can fail independently of address creation. Dispatch a job after save:
static::saved(function (Address $address) {
if ($address->wasChanged(['postcode', 'street', 'number', 'city_id'])) {
GeocodeAddress::dispatch($address)->onQueue('geocoding');
}
});
Store latitude/longitude only when the geocoder returns a confident result.
Use a frontend endpoint for postcode autofill Create a small JSON endpoint or Livewire action for the postcode field. Debounce requests, show loading state, and let the user keep typing. Do not block form submission on a third-party API unless the address is required for checkout.
Fallback across postcode providers If the package supports multiple drivers, configure a primary and fallback. If not, implement a small wrapper that tries providers in order and caches the successful result. Keep provider-specific response mapping out of controllers.
Custom driver extension
Implement the package’s driver contract and register it in config/simple-address.php when you need another postcode or geocoding provider. This is the cleanest extension point; avoid editing vendor files.
Eager load address relationships Address listings often need city/state/country data:
User::with('addresses.city.state.country')->paginate();
Without eager loading, address tables page will generate N+1 queries.
Testing
Fake external HTTP calls with Http::fake() and assert your action maps provider responses into address fields. Keep one test per driver plus one fallback test.
.env, queue geocoding, and handle “no result” without failing the address save.addresses, cities, states, or countries tables. If migrating an existing app, map old address columns deliberately.firstOrCreate with unique constraints or you will accumulate duplicate cities.Relation::enforceMorphMap(), register the addressable models consistently or polymorphic address lookups will break.config/simple-address.php, run php artisan config:clear in local development.$address->exists and avoid dispatching in test factories unless explicitly needed.How can I help you explore Laravel packages today?