markup/addressing-bundle
Symfony bundle for adding postal addressing to your application: address entities, country and province data, form types, and validation, so you can model, store, and validate addresses without building the fundamentals yourself.
The bundle is a thin Symfony wrapper around usemarkup/addressing, so the fastest path is to read the underlying library first — the bundle mostly contributes DI wiring, form types, and validator plumbing.
composer require markup/addressing-bundle
If you are on Symfony 2/3, register new Markup\AddressingBundle\MarkupAddressingBundle() in AppKernel; on Symfony 4+ add it to config/bundles.php and check whether the bundle actually supports your Symfony version before committing (last release April 2019 — expect to vendor or fork if you are on 6/7). Then inspect the config tree:
bin/console config:dump-reference markup_addressing # root key may differ; check Configuration.php
bin/console debug:container | grep -i address
Where to look first, in order:
DependencyInjection/Configuration.php — the real config surface, usually just a few scalar options.Resources/config/services.xml (or .yml) — the list of services you can inject; this is the actual API of the bundle.Form/Type/ and Validator/Constraints/ — the day-to-day value, usually a country ChoiceType and a country-aware postal-code constraint.usemarkup/addressing README for the address model, formatter, and country repository interfaces.A realistic first use case: an order page that stores structured address components but renders a correctly formatted address for the destination country.
public function show(Order $order)
{
return $this->render('order/show.html.twig', [
// signature depends on your version — check the formatter interface
'shipping' => $this->addressFormatter->format($order->getShippingAddress(), 'GB'),
]);
}
Keep components, format late. Persist discrete fields (line1, line2, locality, administrativeArea, postalCode, countryCode) and treat formatting as a presentation concern. Store GB, not United Kingdom; store SW1A 1AA, not a pre-formatted blob. This lets you re-render for invoices, labels, and localized emails without a migration.
Adapt your entity to the library's address interface. The cleanest integration is a small adapter or a Doctrine embeddable that implements the bundled address interface, so you can pass entities straight into the formatter and validator:
class ShippingAddress implements AddressInterface
{
public function getCountryCode(): string { return $this->countryCode; }
// ...
}
Country selection. Drive country dropdowns from the library's country repository rather than a hardcoded array — you get ISO codes and names in one place, and it stays consistent with the formatter/validator:
$builder->add('country', ChoiceType::class, [
'choices' => $this->countryChoices(), // ['United Kingdom' => 'GB', ...]
]);
Country-conditional validation. Postal code rules, required administrative areas (US/CA/AU), and phone formats all vary by country. Validate postal code with the bundle's constraint only when the country requires one, and use validation groups keyed on country where possible instead of stuffing a giant Callback into every form.
Parsing free text is a prefill, not a source of truth. If the bundle exposes an address parser, use it to populate a form from a pasted address, but always let the user correct the components. Never silently normalize a billing address from parsed text — parse failures become failed payments.
Integration tips.
debug:container to find the IDs, alias them in services.yaml, and mark services public: true if you still use $container->get().usemarkup/addressing on its own is usually easier to install and wire by hand than to keep a stale bundle alive. You lose the form types, which are typically a few lines to reimplement.replace/fork before you fight it. If Composer refuses the bundle on Symfony 6+, either alias the underlying library services yourself or fork the bundle with updated constraints — do not pin Symfony down for an address formatter.gb/GB silently falls through to a default format.config/services.yaml rather than editing vendor files.How can I help you explore Laravel packages today?