polifonic/addressable
Polifonic Addressable is a Laravel package that adds polymorphic address support to Eloquent models. Attach reusable addresses to users, companies, or any model via a simple trait and manage them through an intuitive API.
Before anything else, audit the source. With 1 star and zero dependents, treat this package as unreviewed third-party code: read src/, the migration stubs, and composer.json constraints before adding it to a production app.
Minimal setup:
composer require polifonic/addressable
php artisan vendor:publish --provider="Polifonic\Addressable\AddressableServiceProvider"
php artisan migrate
Check what the publish step emits — typically a config file and a migration creating an addresses table with a polymorphic addressable_type / addressable_id pair plus address fields (line 1, line 2, city, state, postal code, country, and often latitude/longitude or a type discriminator).
First use case — attach a shipping address to a User or Order:
use Polifonic\Addressable\Traits\Addressable; // verify exact namespace in src/
class User extends Model
{
use Addressable;
}
$user->addresses()->create([
'type' => 'shipping',
'line_1' => '1 Main St',
'city' => 'Springfield',
'postal_code' => '12345',
'country' => 'US',
]);
Where to look first, in order: the trait file (what relations and helpers it actually exposes), the migration stub (column names and nullability), then the config file (model swapping, table name). The README is likely thin — the source is the documentation.
Polymorphic attachment as the core workflow. The value of the package is the morphMany relationship, so the idiomatic pattern is: one Address model, many addressable parents (User, Order, Company, Warehouse). Add the trait to each model that can own an address rather than duplicating address columns across tables.
Discriminate address roles with a type column, not separate tables. Most implementations carry a type or label field. Use it for billing / shipping / home / work and add scoped accessors:
public function shippingAddress()
{
return $this->morphOne(Address::class, 'addressable')->where('type', 'shipping');
}
If the package doesn't ship these scopes, define them in your own model or a wrapper trait — don't fork the package for it.
Normalize at the boundary. Address input arrives as messy form data. Keep a FormRequest that validates and a small mapper that converts the request into the package's expected column shape. This isolates you from column-name quirks and makes swapping the package later a one-file change.
Copy, don't share. When an order should retain the address as it was at purchase time, clone the user's address into a new record owned by the order. A shared morph record means later edits to a profile silently rewrite historical orders — a classic bug in address packages.
Reuse for API and admin panels. Because the relation is standard Eloquent, $user->addresses loads into API resources, Filament/Nova relation managers, and Blade partials without adapters. Load with ->with('addresses') on index endpoints to avoid N+1.
Compose with geocoding/country packages. Keep geocoding (lat/lng population) in an observer or service that runs after save, not inside the package's model, so you retain control over API calls and failure handling.
composer.json, and be prepared to vendor the trait into your app. Budget for reading every line before trusting it with user PII.Relation::enforceMorphMap(), the stored addressable_type values will be your aliases, not FQCNs. Confirm the package doesn't hardcode class names in queries or factories.addresses and standard column names. If you need user_addresses or custom fields, check whether the config exposes table/model overrides before resorting to a subclass.morphMany has no database-level foreign key, so deleting a user orphans addresses. Add a deleting observer or a cleanup job; the package almost certainly doesn't handle this.$model->addresses returns empty, check (a) the trait is on the model, (b) the morph type in the DB matches what Eloquent writes (especially with morph maps), and (c) $fillable/$guarded on the package's Address model allows the fields you're passing — mass-assignment silently drops unknown keys.How can I help you explore Laravel packages today?