markup/addressing
Address management for Laravel: store, format, and validate postal addresses with country and subdivision data, polymorphic address models, and helpers for clean, consistent address handling across your application.
composer require markup/addressing. There is no Laravel service provider, facade, config file, or auto-discovery — it is a plain, framework-agnostic PHP library, so nothing appears in config/app.php and nothing is published.README.md usage block, then src/ to confirm the exact class names and namespace (Markup\Addressing\…), then tests/. The test fixtures are the most reliable documentation of what each formatter actually emits, since the README is thin and the package has had no release since 2019-04-15.src/ before copying any snippet — this package is old enough that blog examples and its own docs may not match the code.City, ST ZIP versus GB Street / City / POSTCODE versus JP, which orders from large to small).// Confirm namespace and constructor signature in src/ first.
$address = new Address(
'Jane Doe',
'10 Downing Street',
'London',
'SW1A 2AA',
'GB'
);
$formatter = new SomeCountryFormatter(); // see src/ for concrete classes
echo $formatter->format($address);
If that renders the expected multi-line block, the package is doing its one job and you can proceed to integration.
Store structured, render at the edge. The single most important pattern: persist the discrete address fields (or a JSON column) on your orders/addresses tables and call the formatter only at the moment of display. Never persist the formatted string — you will eventually need a different format for a shipping label than for a customer-facing invoice, and reformatting stored text is not possible.
Formatter selection by country code. The library gives you formatters; the routing between them is your job. Keep a small factory or registry:
class AddressFormatterFactory
{
public function for(string $countryCode): AddressFormatterInterface
{
return match (strtoupper($countryCode)) {
'US' => new UnitedStatesFormatter(),
'GB' => new UnitedKingdomFormatter(),
// ...
default => new GenericFormatter(),
};
}
}
Always ship a sensible default. An unmapped country should degrade to a generic "street / city / region / postal code / country" layout, never throw in a checkout confirmation email.
Bind it once in Laravel. Register the factory as a singleton in AppServiceProvider::register() and inject it, rather than new-ing formatters in controllers. Because the package has no container integration, this is the only wiring you need:
$this->app->singleton(AddressFormatterFactory::class);
Wrap it behind your own interface. Define App\Contracts\AddressFormatter with a single format(Address $address): string method and adapt the package inside. Given the package's age (see Gotchas), this one-file indirection is what makes a future swap to a maintained library cheap.
Map Eloquent models to the value object. Add a toAddressObject() method or a small mapper class on your Address model. Keep the mapping in one place so nullable columns (address_line_2, region) are normalized to null or '' consistently — formatters are sensitive to the difference.
Blade component for the view layer.
<x-address :address="$order->shippingAddress" :locale="$order->locale" />
The component resolves the factory, picks the formatter from $address->country_code, and renders {!! nl2br(e($formatted)) !!}. Escaping matters: postal addresses occasionally contain & and quotes.
API responses. Return both the structured fields and a pre-rendered convenience field:
return [
'shipping_address' => $address->toArray(),
'shipping_address_formatted' => $formatter->format($address),
];
Structured fields let clients re-render for their own locale; the formatted string saves mobile clients from reimplementing the rules.
Test with golden files. Because output is deterministic string formatting, a data-provider test asserting exact expected strings per country is the highest-value test you can write. Cover at minimum: a fully populated address, an address with a null second line, an address with no postal code (several countries legitimately have none), and an unmapped country falling back to the generic formatter.
composer.json and be prepared to fork it into your own VCS if you need a change. The low dependent count also means bugs are unlikely to have been shaken out by other users."php": "^7.x" in composer.json. On modern PHP the install fails. composer require --ignore-platform-reqs will force it in, but do that only after verifying the code actually runs — deprecated dynamic properties and removed each()-era functions are real risks. Better: fork, bump the constraint, run its test suite.src/ as the source of truth over any README, snippet, or AI-generated sample (including the ones above).Markup\Addressing\AddressingServiceProvider is referenced anywhere online, it does not exist in this package.region can yield London, , SW1A 2AA or a blank line. Sanitize on the way in, and add a test for every optional field being absent.symfony/intl or Laravel's own Countries helpers over anything shipped here.commerceguys/addressing is the de facto standard for this problem: actively maintained, backed by CLDR country data, integrates with symfony/intl, and handles the empty-field and localization cases above properly. Given that a typical integration touches one or two call sites, migrating is usually cheaper than living with the constraints. If you keep markup/addressing, the wrapper interface described in Implementation Patterns is what keeps that door open.How can I help you explore Laravel packages today?