konekt/address
Konekt Address is a Laravel package for managing countries, provinces, and addresses with Eloquent models. It offers address types, validation, formatting helpers, and seed data for building location-aware applications.
konekt/address is not a typical auto-discovered Laravel package — it is a Concord module. Nothing works until you register the module provider.
composer require konekt/address
// config/concord.php
return [
'modules' => [
Konekt\Address\Providers\ModuleServiceProvider::class,
],
];
Then publish/run migrations and seed the reference data:
php artisan vendor:publish --provider="Konekt\Address\Providers\ModuleServiceProvider"
php artisan migrate
php artisan concord:seed # countries (and provinces, where available)
Skipping the seed step is the single most common "the package is broken" report — you get an empty countries table and therefore an empty dropdown everywhere.
First use case: a checkout/billing address form. Countries come straight from the package table, keyed by ISO alpha-2:
$countries = Country::orderBy('name')->pluck('name', 'id');
$provinces = Province::where('country_id', $request->country_id)->pluck('name', 'id');
Where to look first:
src/Contracts/ — this is the real public API (Country, Province, Address, Organization, Person). Code against these.src/Models/ — default implementations, read them for actual column names (the README is thin; the models are the documentation).database/migrations/ — confirms types (country_id is a 2-char string, not an integer).src/Models/AddressType, Gender, NameOrder — konekt/enum classes used as casts.Type-hint contracts, resolve through Concord. Never new Country in your own code; that bypasses model customization.
use Konekt\Address\Contracts\Country;
public function index(Country $country) // container resolves the configured model
Override a model instead of forking. Add methods (scopes, accessors, fullName) by swapping the implementation in the module config:
// config/concord.php
'modules' => [
Konekt\Address\Providers\ModuleServiceProvider::class => [
'models' => [
Konekt\Address\Contracts\Person::class => App\Models\Person::class,
],
],
],
namespace App\Models;
class Person extends \Konekt\Address\Models\Person
{
public function getFullNameAttribute(): string
{
return trim("{$this->firstname} {$this->lastname}");
}
}
Everything typed against the contract — including the package's own relations — now picks up your class. php artisan concord:make:model scaffolds a drop-in replacement if you prefer.
Address records with a type. Addresses are ordinary rows referencing a country/province; organizations and people attach them via the package's relations (check src/Models/Address.php for the exact relation name in your version before designing around it).
$address = Address::create([
'name' => $person->full_name,
'country_id' => 'DE',
'province_id'=> null,
'city' => 'Berlin',
'address' => 'Unter den Linden 1',
'postcode' => '10117',
'type' => AddressType::SHIPPING(),
]);
Validation that matches the schema.
$request->validate([
'country_id' => ['required', Rule::exists('countries', 'id')],
'province_id' => ['nullable', Rule::exists('provinces', 'id')
->where('country_id', $request->country_id)],
'city' => ['required', 'string', 'max:255'],
'postcode' => ['required', 'string', 'max:16'],
]);
Add fields in your own migration. Want vat_id or label on addresses? Create a separate migration adding columns; the published package migrations stay untouched so upgrades remain clean.
Cache the static data. Countries/provinces change only on deploy: Cache::remember('countries', 3600, fn () => Country::pluck('name','id')), and clear it in your deploy script right after concord:seed.
country_id is a string, not an FK integer. Any table you create referencing countries must use char(2) (or string, 2) with a matching collation, or MySQL strict mode will reject the foreign key.provinces as "best effort": make the field nullable and fall back to a free-text state field when the list is empty.new Country silently ignores your override. Concord binds contract → implementation; instantiating a model directly defeats it. Debug with get_class(app(Country::class)) when a custom method "isn't there".name_local column (or your own lookup table) and populate it yourself.AddressType, Gender, NameOrder are stored as strings via konekt/enum casts. Renaming or removing enum values leaves orphaned strings in the database — write a data migration when you change them, and prefer adding values.vendor:publish --force will clobber edited migrations/configs. Keep your customization in config/concord.php and your own migrations only.concord:seed in every test; insert only the handful of countries your fixtures need inside a RefreshDatabase setup.composer.json, read the migration/contract files before upgrading, and prefer Concord-based overrides over patching vendor code — you may end up maintaining this yourself.config/concord.php to [], or caching config before registering the module, produces confusing "class not found / model not registered" errors. php artisan config:clear after any change to the module map is the first debugging step.How can I help you explore Laravel packages today?