thehocinesaad/laravel-algereography
Laravel package that ships Algeria's administrative geography out of the box: wilayas, dairas, and communes with ready-made migrations, Eloquent models, seeders, and relationships, so you can add reliable location data to your app quickly.
Install and publish
composer require thehocinesaad/laravel-algereography
php artisan vendor:publish --provider="Thehocinesaad\AlgeriaGeography\AlgeriaGeographyServiceProvider"
The provider publishes up to three asset groups — migrations, seeders, and (usually) a config file. Publish selectively so you can inspect before running:
php artisan vendor:publish --tag=algereography-migrations
php artisan vendor:publish --tag=algereography-seeders
Check vendor/thehocinesaad/laravel-algereography/src/ for the actual tag names and namespace before wiring anything — this package is small enough that reading the source is faster than guessing.
First run
php artisan migrate
php artisan db:seed --class=WilayaSeeder
php artisan db:seed --class=DairaSeeder
Order matters: Dairas carry a wilaya_id foreign key, so WilayaSeeder must run first.
First use case
A two-level address form. On a registration or checkout page, render a Wilaya <select> from Wilaya::orderBy('code')->get(), then load Dairas for the chosen Wilaya via a small JSON endpoint. That's 90% of what this package is used for in real projects — the package is a data seed, not a UI library, so the form logic is yours.
Verify the install before building anything: Wilaya::count() should return 58, not 48. Algeria reorganized from 48 to 58 wilayas in 2019; anything returning 48 is stale data and you should stop and check the seeder contents.
Adding the FK to your own tables
Match the package's column type exactly. If the package uses unsignedTinyInteger for wilaya_id, your FK column must be unsignedTinyInteger too — a mismatch produces a cryptic errno: 150 on migrate.
Schema::create('addresses', function (Blueprint $table) {
$table->id();
$table->unsignedTinyInteger('wilaya_id');
$table->unsignedSmallInteger('daira_id')->nullable();
$table->string('street');
$table->timestamps();
$table->foreign('wilaya_id')->references('id')->on('wilayas');
$table->foreign('daira_id')->references('id')->on('dairas');
});
Use restrictOnDelete() on the wilaya FK. Cascading a user address because someone re-ran a geography seeder is a bad day.
Cascading dropdown endpoint
Route::get('/wilayas/{wilaya}/dairas', fn (Wilaya $wilaya) =>
Daira::where('wilaya_id', $wilaya->id)->orderBy('name')->get(['id', 'name'])
);
Wrap the response in a JsonResource if you already have an API layer; the payload is tiny and rarely changes, so also consider caching per-wilaya with Cache::rememberForever("dairas.{$wilaya->id}", ...).
Validation
'daira_id' => [
'required',
Rule::exists('dairas', 'id')->where('wilaya_id', $request->wilaya_id),
],
The where clause is the important part — it prevents a client from submitting a Daira from a different Wilaya, which is a real bug class in these forms.
Seeding in deploy pipelines
Geography data is reference data, not user data. Do not put it in DatabaseSeeder where migrate:fresh --seed will run it locally every time and slow down your test suite. Instead:
GeographySeeder that is idempotent (updateOrCreate keyed on code, or guard with if (Wilaya::count() > 0) return;).php artisan db:seed --class=GeographySeeder --force.Testing
RefreshDatabase plus the full seeder in every test is slow (58 + ~550 rows). Seed once per test suite using a setUpTraits hook or a Pest beforeAll, or stub a handful of rows in the test that actually needs them.
Reporting / filtering
Joining users to wilayas for a dashboard is fine but note the table is small — a with('wilaya') eager load is cheaper than a join for list views. For aggregate reports, join and group by wilayas.code, and remember wilaya code (the official 1–58 numbering) is the stable identifier for external reporting, not the autoincrement id.
Extending the models
If you need HasMany relations from your User model, don't modify vendor code. Either add the relations on your side ($user->addresses()->with('wilaya')) or extend the package model in app/Models/Wilaya.php and bind your subclass via the config file if it exposes a model mapping.
Wilaya::count() immediately after install.composer.json and don't assume counts match whatever source your business team quotes.insert() in their seeders. Running the seeder twice creates duplicate rows or fails on unique constraints. Confirm whether it uses truncate(), updateOrCreate, or nothing, and wrap it in your own idempotent wrapper before running in production.loadMigrationsFrom(), publishing the migrations first and then running migrate can create duplicate or conflicting schema definitions. Read the provider. If migrations auto-load, do not publish them.wilaya_id may be unsignedTinyInteger (58 values fit in 1 byte) while your own tables default to unsignedBigInteger. Mismatched FK types are the #1 cause of errno: 150 with this kind of package.migrate:fresh or truncate geography tables in production once user data references them. If wilaya IDs are autoincrement and not derived from code, a reseed can renumber everything and silently corrupt addresses.id vs code. Use code for anything external (reports, exports, third-party APIs). Use id only internally. They may coincidentally be equal on a fresh seed but that's an implementation detail, not a contract.utf8mb4 / utf8mb4_unicode_ci. Default utf8 will mangle some characters.name_en, no translation files. Add an accessor or a small lookup table keyed on code if you need consistent English display.rememberForever, remember to Cache::forget() it after any deploy that reseeds geography, or stale lists will linger past the data change.illuminate/support constraint conflict. Try composer require thehocinesaad/laravel-algereography --with-all-dependencies; if it still fails, either fork and bump the constraint in composer.json or copy the migrations and seeder files directly into your project and drop the dependency — for a package that is fundamentally a data seed, vendoring the files is a legitimate and often preferable exit strategy.Thehocinesaad\AlgeriaGeography\Models\... — but verify in the source rather than trusting the README. A wrong use statement produces a confusing "class not found" that looks like an install failure.wilayas or dairas table in your schema, the published migration will fail. Check the config for table-name overrides or rename your existing tables before installing.How can I help you explore Laravel packages today?