PHPackages                             kazistm/algeria-geo - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. kazistm/algeria-geo

ActiveLaravel-package[Database &amp; ORM](/categories/database)

kazistm/algeria-geo
===================

Laravel package providing complete Algerian geo data (69 Wilayas and Communes) with slugs, coordinates and optimized seeders.

v2.0.1(1mo ago)0301MITPHPPHP ^8.1 || ^8.2 || ^8.3 || ^8.4 || ^8.5CI passing

Since May 3Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/KaziSTM/algeria-geo)[ Packagist](https://packagist.org/packages/kazistm/algeria-geo)[ Docs](https://github.com/KaziSTM/algeria-geo)[ RSS](/packages/kazistm-algeria-geo/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (17)Versions (8)Used By (0)

Algeria Geo Data for Laravel by KaziSTM
=======================================

[](#algeria-geo-data-for-laravel-by-kazistm)

[![Latest Version on Packagist](https://camo.githubusercontent.com/c397dc7d1974bd2bc67f98bd6026e7beea9f98057d57121248a175df2d4da9a6/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6b617a6973746d2f616c67657269612d67656f2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/kazistm/algeria-geo)[![Total Downloads](https://camo.githubusercontent.com/c49a75492b4fb0f511233f4997ba0ba05a125ff5abf9f626a1959038c072a99e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6b617a6973746d2f616c67657269612d67656f2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/kazistm/algeria-geo)[![License](https://camo.githubusercontent.com/eeb86bf48792da6d585ff6bebca57d7d7636d6ed74e4fd7996c1e9e115a16075/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6b617a6973746d2f616c67657269612d67656f2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/kazistm/algeria-geo)

🇩🇿 Algeria Administrative Update
--------------------------------

[](#-algeria-administrative-update)

This package fully supports Algeria’s latest administrative division:

- ✅ 69 Wilayas (updated law)
- ✅ Complete Communes dataset
- ✅ Normalized naming (FR / AR)
- ✅ Unique slugs for reliable lookups
- ✅ Geographic coordinates (lat/lng)

> Unlike many existing packages, this dataset reflects the **latest official Algerian administrative structure**.

---

Table of Contents
-----------------

[](#table-of-contents)

- [Features](#features)
- [Installation](#installation)
    - [1. Require Package](#1-require-package)
    - [2. Run Install Command](#2-run-install-command)
    - [3. Optional Publishing](#3-optional-publishing)
- [Usage](#usage)
    - [Models](#models)
        - [City (Wilaya)](#city-wilaya)
        - [Commune](#commune)
    - [Basic Retrieval](#basic-retrieval)
    - [Relationships](#relationships)
        - [City to Communes](#city-to-communes)
        - [Commune to City](#commune-to-city)
    - [Static Finders](#static-finders)
        - [City::findByCode()](#cityfindbycode)
        - [Commune::findByPostCode()](#communefindbypostcode)
    - [Query Scopes](#query-scopes)
        - [searchByName()](#searchbyname)
        - [code() (City)](#code-city)
        - [postCode() (Commune)](#postcode-commune)
        - [withinRadius() (Commune)](#withinradius-commune)
    - [Helper Methods](#helper-methods)
        - [getCoordinates()](#getcoordinates)
        - [getDisplayName()](#getdisplayname)
- [Data Source](#data-source)
- [Contributing](#contributing)
- [License](#license)

---

Features
--------

[](#features)

- **Eloquent Models:** `City` (Wilaya) and `Commune` models ready to use.
- **Relationships:** Pre-defined `hasMany` (City-&gt;Communes) and `belongsTo` (Commune-&gt;City) relationships.
- **Database Migrations:** Sets up optimized `cities` and `communes` tables with proper indexes and constraints.
- **Database Seeders:** Fully updated dataset covering **all 69 Algerian Wilayas** and their Communes.
- **Slug Support:** Unique slugs for both Cities and Communes for clean URLs and lookups.
- **Data Integrity:** Prevents duplicates via composite unique constraints (`wilaya_id + slug`).
- **Robust Seeding:** Handles missing/partial data safely and avoids crashes.
- **Easy Installation:** Single Artisan command (`algeria-geo:install`) for migration and optional seeding.
- **Helpful Query Scopes:** Includes scopes for searching by name, code, slug, postal code, and **geospatial radius**.
- **Convenience Methods:** Helpers for retrieving coordinates and display names.
- **High Test Coverage:** Fully tested with **&gt;95% Pest coverage** ensuring reliability.
- **Laravel Compatibility:** Supports Laravel 10 → 13 and PHP 8.1 → 8.5.
- **Production Ready:** Designed for SaaS and real-world geo use cases.

Installation
------------

[](#installation)

### 1. Require Package

[](#1-require-package)

Install the package via Composer:

```
composer require kazistm/algeria-geo
```

2. Run Install Command
----------------------

[](#2-run-install-command)

This command checks if the tables exist, runs the migrations if needed, and optionally seeds the database.

### Bash

[](#bash)

```
# Run migrations only (if tables don't exist)
php artisan algeria-geo:install

# Run migrations AND seed the database (recommended for first install)
php artisan algeria-geo:install --seed

# Force migrations/seeding even if tables exist (use with caution!)
php artisan algeria-geo:install --seed --force
```

The command will inform you if the tables already exist and skip the process unless --force is used.

3. Optional Publishing
----------------------

[](#3-optional-publishing)

You typically don't need to publish assets, as the package runs migrations directly from its vendor directory.
However, if you need deep customization:

### Bash

[](#bash-1)

```
# Publish Migrations (if you want to modify them BEFORE migrating)
php artisan vendor:publish --tag=algeria-geo-migrations

# Publish Seeders (if you want to modify the seeding logic)
php artisan vendor:publish --tag=algeria-geo-seeders
```

Usage
-----

[](#usage)

### Models

[](#models)

Access the models using their namespaces:

```
use KaziSTM\AlgeriaGeo\Models\City;
use KaziSTM\AlgeriaGeo\Models\Commune;
```

### City (Wilaya)

[](#city-wilaya)

Represents an Algerian Wilaya.

ColumnTypeDescription`id`Primary KeyUnique identifier`code``int`Wilaya code (e.g., `16`)`name``string`Wilaya name (e.g., `"Alger"`)`arabic_name``string`Wilaya name in Arabic (e.g., `"الجزائر"`)`slug``string`Unique slug (e.g., `"algiers"`)`longitude``floatnull``latitude``floatnull``created_at``timestamp`Timestamp`updated_at``timestamp`Timestamp---

### Commune (Daira)

[](#commune-daira)

Represents an Algerian Commune (Daira).

ColumnTypeDescription`id`Primary KeyUnique identifier`post_code``stringnull``name``string`Commune name`wilaya_id``int`Foreign key to `cities.id``arabic_name``string`Commune name in Arabic`slug``string`Unique per Wilaya (used for lookup &amp; URLs)`longitude``floatnull``latitude``floatnull``distance``floatnull``created_at``timestamp`Timestamp`updated_at``timestamp`Timestamp> ⚠️ Note: A unique constraint is enforced on (`wilaya_id`, `slug`) to prevent duplicates.

### Basic Retrieval

[](#basic-retrieval)

```
// Get all Cities (Wilayas), ordered by code
$cities = City::orderBy('code')->get();

// Get all Communes, ordered by name
$communes = Commune::orderBy('name')->get();

// Find a specific City by ID
$city = City::find(16); // Alger

// Find a specific Commune by ID
$commune = Commune::find(554); // Alger Centre
```

### Relationships

[](#relationships)

#### City to Communes

[](#city-to-communes)

```
$algiersCity = City::find(16);

if ($algiersCity) {
    // Access the collection of Communes belonging to the City
    $algiersCommunes = $algiersCity->communes; // Returns Eloquent Collection

    // You can also query the relationship
    $firstCommune = $algiersCity->communes()->orderBy('name')->first();
}
```

#### Commune to City

[](#commune-to-city)

```
$koubaCommune = Commune::find(571); // Kouba

if ($koubaCommune) {
    // Access the City model the Commune belongs to
    $city = $koubaCommune->city; // Returns City model (Alger)

    echo "Commune: " . $koubaCommune->name . " belongs to City: " . $city->name;
    // Output: Commune: Kouba belongs to City: Alger
}
```

### Static Finders

[](#static-finders)

Convenient methods to find a single record by a specific attribute.

#### `City::findByCode()`

[](#cityfindbycode)

Finds a City (Wilaya) by its official code.

```
$oranCity = City::findByCode(31); // Returns the City model for Oran, or null

if ($oranCity) {
    echo "Found: " . $oranCity->name;
} else {
    echo "City with code 31 not found.";
}
```

#### `Commune::findByPostCode()`

[](#communefindbypostcode)

Finds the first Commune matching the given postal code.
Note: Postal codes might not be strictly unique across all communes in some datasets.

```
$commune = Commune::findByPostCode('31001'); // e.g., Oran

if ($commune) {
    echo "Found Commune: " . $commune->name . " in City: " . $commune->city->name;
} else {
    echo "Commune with post code 31001 not found.";
}
```

#### `searchByName()`

[](#searchbyname)

Available on both City and Commune. Searches the `name` and `arabic_name` columns.

```
// Search Cities
$cities = City::searchByName('Bordj')->orderBy('name')->get();

// Search Communes
$communes = Commune::searchByName('وادي')->orderBy('name')->get();

// Chain with other constraints
$algiersCommunesStartingWithS = Commune::where('wilaya_id', 16)
                                    ->searchByName('S')
                                    ->orderBy('name')
                                    ->get();
```

#### `code()` (City)

[](#code-city)

Filters Cities by their official code.

```
$city = City::code(5)->first(); // Batna
```

#### `postCode()` (Commune)

[](#postcode-commune)

Filters Communes by their postal code.

```
$communes = Commune::postCode('05001')->get(); // Communes with this post code
```

#### `withinRadius()` (Commune)

[](#withinradius-commune)

Finds Communes within a specified radius of a given latitude/longitude point using the Haversine formula.

**Parameters:**

- `$latitude` (float): Latitude of the center point.
- `$longitude` (float): Longitude of the center point.
- `$radius` (int, optional): The radius distance. Defaults to 10.
- `$unit` (string, optional): The unit for the radius ('km' or 'miles'). Defaults to 'km'.

**Returns:**

- An Eloquent Builder instance. When results are retrieved (`get()`, `first()`, etc.), each Commune model will have an additional `distance` attribute containing the calculated distance from the center point in the specified unit.

**Example (Kilometers):**

```
// Find communes within 15km of central Algiers
$centerLat = 36.7753;
$centerLon = 3.0588;
$radiusKm = 15;

$nearbyCommunes = Commune::withinRadius($centerLat, $centerLon, $radiusKm, 'km')->get();

echo "Communes within {$radiusKm}km of {$centerLat},{$centerLon}:\n";
foreach ($nearbyCommunes as $comm) {
    // Access the calculated distance
    $distance = round($comm->distance, 2);
    echo "- {$comm->getDisplayName()} ({$distance} km)\n";
}
```

#### `withinRadius()` (Commune)

[](#withinradius-commune-1)

Finds Communes within a specified radius of a given latitude/longitude point using the Haversine formula.

**Parameters:**

- `$latitude` (float): Latitude of the center point.
- `$longitude` (float): Longitude of the center point.
- `$radius` (int, optional): The radius distance. Defaults to 10.
- `$unit` (string, optional): The unit for the radius ('km' or 'miles'). Defaults to 'km'.

**Returns:**

- An Eloquent Builder instance. When results are retrieved (`get()`, `first()`, etc.), each Commune model will have an additional `distance` attribute containing the calculated distance from the center point in the specified unit.

**Example (Kilometers):**

```
// Find communes within 15km of central Algiers
$centerLat = 36.7753;
$centerLon = 3.0588;
$radiusKm = 15;

$nearbyCommunes = Commune::withinRadius($centerLat, $centerLon, $radiusKm, 'km')->get();

echo "Communes within {$radiusKm}km of {$centerLat},{$centerLon}:\n";
foreach ($nearbyCommunes as $comm) {
    // Access the calculated distance
    $distance = round($comm->distance, 2);
    echo "- {$comm->getDisplayName()} ({$distance} km)\n";
}
```

Note: This query relies on the **latitude** and **longitude** columns being populated in your communes table. Accuracy depends on the quality of the coordinate data. Communes with `NULL` coordinates are automatically excluded from the radius search.

---

#### `City::findBySlug()`

[](#cityfindbyslug)

Finds a City (Wilaya) by its slug.

```
$city = City::findBySlug('oran');
```

#### `Commune::findBySlu()`

[](#communefindbyslu)

Finds a Commune by its slug.

```
$commune = Commune::findBySlug('bir-el-djir');
```

---

Helper Methods
--------------

[](#helper-methods)

Instance methods available on retrieved models.

### `getCoordinates()`

[](#getcoordinates)

Available on both **City** and **Commune**. Returns coordinates as an associative array or `null`.

**PHP Example:**

```
$oranCity = City::findByCode(31);
$coords = $oranCity?->getCoordinates();
// $coords will be ['latitude' => 35.6987..., 'longitude' => -0.6349...] or null

$oranCommune = Commune::findByPostCode('31001');
$communeCoords = $oranCommune?->getCoordinates();
// $communeCoords will be ['latitude' => 35.6987..., 'longitude' => -0.6349...] or null
```

### `getDisplayName()`

[](#getdisplayname)

Available on both **City** and **Commune**. Returns the name, optionally preferring the Arabic name.

**PHP Example:**

```
$city = City::find(16); // Alger
echo $city->getDisplayName();          // Output: Alger
echo $city->getDisplayName(true);      // Output: الجزائر

$commune = Commune::find(554); // Alger Centre
echo $commune->getDisplayName();       // Output: Alger Centre
echo $commune->getDisplayName(true);   // Output: الجزائر الوسطى
```

---

### Data Source

[](#data-source)

The dataset has been **fully updated to reflect Algeria’s latest administrative divisions (69 Wilayas)**.

### Improvements:

[](#improvements)

- ✅ Complete coverage of all **69 Wilayas**
- ✅ Updated and expanded Communes dataset
- ✅ Added geographic coordinates (lat/lng) where available
- ✅ Normalized naming (FR / AR)
- ✅ Added slugs for consistent identification
- ✅ Removed duplicates and fixed inconsistent records

The seeders are designed to be:

- **Idempotent** (safe to run multiple times)
- **Resilient** to partial or evolving datasets
- **Optimized** for performance using chunked upserts

---

Testing
-------

[](#testing)

This package includes a comprehensive test suite built with Pest.

### Coverage

[](#coverage)

- ✅ Models (City, Commune)
- ✅ Seeders (data integrity &amp; edge cases)
- ✅ Install Command (all execution paths)

> Test coverage is maintained at **&gt;95%** to ensure stability and reliability.

### Run tests

[](#run-tests)

```
composer test
```

### Run coverage

[](#run-coverage)

```
composer test:coverage
```

### Contributing

[](#contributing)

Contributions (bug reports, feature requests, pull requests) are welcome. Please refer to the GitHub repository issues and pull request sections.

---

### License

[](#license)

This package is open-source software licensed under the MIT license.

---

###  Health Score

46

—

FairBetter than 93% of packages

Maintenance92

Actively maintained with recent releases

Popularity15

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity59

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~66 days

Recently: every ~83 days

Total

6

Last Release

41d ago

Major Versions

v1.1.1 → v2.0.02026-04-01

PHP version history (2 changes)v1.0.0PHP ^8.1|^8.2|^8.3

v2.0.0PHP ^8.1 || ^8.2 || ^8.3 || ^8.4 || ^8.5

### Community

Maintainers

![](https://www.gravatar.com/avatar/0498a707a3a500a32629c9c0d5b757b31c52bc2c76b03b380396a4334ddafd31?d=identicon)[KaziSTM](/maintainers/KaziSTM)

---

Top Contributors

[![KaziSTM](https://avatars.githubusercontent.com/u/98532488?v=4)](https://github.com/KaziSTM "KaziSTM (17 commits)")

---

Tags

laravelgeolocationcoordinatesdatabase-seederWilayaAlgeriadairacommuneslaravel geo69 wilayasalgeria geoalgeria locationslaravel seederalgerian cities

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/kazistm-algeria-geo/health.svg)

```
[![Health](https://phpackages.com/badges/kazistm-algeria-geo/health.svg)](https://phpackages.com/packages/kazistm-algeria-geo)
```

###  Alternatives

[barryvdh/laravel-ide-helper

Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.

14.9k123.0M687](/packages/barryvdh-laravel-ide-helper)[tucker-eric/eloquentfilter

An Eloquent way to filter Eloquent Models

1.8k4.8M26](/packages/tucker-eric-eloquentfilter)[spatie/laravel-backup

A Laravel package to backup your application

6.0k21.8M191](/packages/spatie-laravel-backup)[owen-it/laravel-auditing

Audit changes of your Eloquent models in Laravel

3.4k33.0M95](/packages/owen-it-laravel-auditing)[clickbar/laravel-magellan

This package provides functionality for working with the postgis extension in Laravel.

423715.4k1](/packages/clickbar-laravel-magellan)[dyrynda/laravel-model-uuid

This package allows you to easily work with UUIDs in your Laravel models.

4802.8M8](/packages/dyrynda-laravel-model-uuid)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
