├── database ├── database.sqlite ├── migrations │ └── 2024_10_26_000000_create_cities_table.php.stub └── seeders │ ├── WilayaCommuneSeeder.php │ └── json │ └── Wilaya_Of_Algeria.json ├── docs ├── package-social-preview.png └── package-social-preview-readme.png ├── .gitignore ├── phpstan.neon ├── config └── algerian-cities.php ├── ignore-by-php-version.neon.php ├── .github ├── dependabot.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ └── bug.yml └── workflows │ ├── update-changelog.yml │ ├── dependabot-auto-merge.yml │ └── laravel.yml ├── rector.php ├── tests ├── WilayaTest.php ├── CommuneTest.php ├── ApiTest.php └── TestCase.php ├── src ├── Wilaya.php ├── Controllers │ └── Api │ │ ├── CommuneController.php │ │ └── WilayaController.php ├── Console │ └── Commands │ │ └── AlgerianCitiesCommand.php ├── helpers.php ├── Commune.php └── Providers │ └── AlgerianCitiesServiceProvider.php ├── routes └── api.php ├── LICENSE.md ├── baseline-8.1.neon ├── CHANGELOG.md ├── phpunit.xml ├── composer.json └── README.md /database/database.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kossa/algerian-cities/HEAD/database/database.sqlite -------------------------------------------------------------------------------- /docs/package-social-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kossa/algerian-cities/HEAD/docs/package-social-preview.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /.idea 3 | composer.lock 4 | .phpunit.result.cachecomposer.lock 5 | /.phpunit.cache 6 | /coverage-report -------------------------------------------------------------------------------- /docs/package-social-preview-readme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kossa/algerian-cities/HEAD/docs/package-social-preview-readme.png -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | includes: 2 | - vendor/larastan/larastan/extension.neon 3 | - ignore-by-php-version.neon.php 4 | parameters: 5 | level: max 6 | paths: 7 | - src/ 8 | - tests/ 9 | - routes/ -------------------------------------------------------------------------------- /config/algerian-cities.php: -------------------------------------------------------------------------------- 1 | env('ALGERIAN_CITIES_API_ENABLED', true), 11 | ]; 12 | -------------------------------------------------------------------------------- /ignore-by-php-version.neon.php: -------------------------------------------------------------------------------- 1 | withPaths([ 9 | __DIR__.'/config', 10 | __DIR__.'/routes', 11 | __DIR__.'/src', 12 | __DIR__.'/tests', 13 | ]) 14 | ->withPreparedSets( 15 | deadCode: true, 16 | codeQuality: true, 17 | codingStyle: true, 18 | typeDeclarations: true, 19 | // privatization: true, 20 | // naming: true, 21 | rectorPreset: true 22 | )->withPhpSets(); 23 | -------------------------------------------------------------------------------- /tests/WilayaTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(58, $count); 14 | } 15 | 16 | public function test_if_wilaya_details_are_correct(): void 17 | { 18 | $sampleWilaya = Wilaya::where('name', 'Alger')->firstOrFail(['id', 'name', 'arabic_name', 'longitude', 'latitude']); 19 | $this->assertJsonStringEqualsJsonString( 20 | $sampleWilaya->toJson(), 21 | '{"id":16,"name":"Alger","arabic_name":"الجزائر","longitude":36.753768,"latitude":3.0587561}' 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Wilaya.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | public static function rules(): array 23 | { 24 | return [ 25 | 'name' => 'required', 26 | ]; 27 | } 28 | 29 | /** 30 | * @return HasMany 31 | */ 32 | public function communes(): HasMany 33 | { 34 | return $this->hasMany(Commune::class); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.github/workflows/update-changelog.yml: -------------------------------------------------------------------------------- 1 | name: "Update Changelog" 2 | 3 | on: 4 | release: 5 | types: [released] 6 | 7 | permissions: 8 | contents: write 9 | 10 | jobs: 11 | update: 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 5 14 | 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v4 18 | with: 19 | ref: master 20 | 21 | - name: Update Changelog 22 | uses: stefanzweifel/changelog-updater-action@v1 23 | with: 24 | latest-version: ${{ github.event.release.name }} 25 | release-notes: ${{ github.event.release.body }} 26 | 27 | - name: Commit updated CHANGELOG 28 | uses: stefanzweifel/git-auto-commit-action@v5 29 | with: 30 | branch: master 31 | commit_message: Update CHANGELOG 32 | file_pattern: CHANGELOG.md 33 | -------------------------------------------------------------------------------- /tests/CommuneTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(1541, $count); 14 | } 15 | 16 | public function test_if_commune_details_are_correct(): void 17 | { 18 | $sampleCommune = Commune::where('name', 'Alger Centre') 19 | ->firstOrFail(['id', 'name', 'arabic_name', 'post_code', 'longitude', 'latitude']); 20 | 21 | $this->assertJsonStringEqualsJsonString( 22 | $sampleCommune->toJson(), 23 | '{"id":554,"name":"Alger Centre","arabic_name":"الجزائر الوسطى","longitude":3.061224,"latitude":36.771225,"post_code": "16001"}' 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Controllers/Api/CommuneController.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | public function index(): Collection 18 | { 19 | return Commune::all(); 20 | } 21 | 22 | /** 23 | * Get a specified Commune. 24 | */ 25 | public function show(int $id): Commune 26 | { 27 | return Commune::findOrFail($id); 28 | } 29 | 30 | /** 31 | * Search wilaya by name or arabic_name 32 | * 33 | * @return Collection 34 | */ 35 | public function search(string $keyword): Collection 36 | { 37 | return Commune::where('name', 'like', sprintf('%%%s%%', $keyword)) 38 | ->orWhere('arabic_name', 'like', sprintf('%%%s%%', $keyword)) 39 | ->get(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/ApiTest.php: -------------------------------------------------------------------------------- 1 | get('/api/wilayas') 12 | ->assertStatus(200) 13 | ->assertJsonCount(58); 14 | 15 | $this->get('/api/wilayas/1') 16 | ->assertStatus(200) 17 | ->assertJsonFragment(['name' => 'Adrar']); 18 | 19 | $this->get('/api/search/wilaya/adrar') 20 | ->assertStatus(200) 21 | ->assertJsonFragment(['name' => 'Adrar']); 22 | 23 | $this->get('/api/communes') 24 | ->assertStatus(200) 25 | ->assertJsonCount(1541); 26 | 27 | $this->get('/api/communes/1') 28 | ->assertStatus(200) 29 | ->assertJsonFragment(['name' => 'Adrar']); 30 | 31 | $this->get('/api/search/commune/adrar') 32 | ->assertStatus(200) 33 | ->assertJsonFragment(['name' => 'Adrar']); 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | middleware('api') 11 | ->group(function (): void { 12 | // Wilayas 13 | Route::controller(WilayaController::class)->group(function (): void { 14 | Route::get('wilayas', 'index'); // Get all wilayas 15 | Route::get('wilayas/{id}', 'show'); // Get specific wilaya 16 | Route::get('wilayas/{id}/communes', 'communes'); // Get communes by wilaya 17 | Route::get('search/wilaya/{q}', 'search'); // Search wilayas 18 | }); 19 | 20 | // Communes 21 | Route::controller(CommuneController::class)->group(function (): void { 22 | Route::get('communes', 'index'); // Get all communes 23 | Route::get('communes/{id}', 'show'); // Get specific commune 24 | Route::get('search/commune/{q}', 'search'); // Search communes 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Kouceyla 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Console/Commands/AlgerianCitiesCommand.php: -------------------------------------------------------------------------------- 1 | \Kossa\AlgerianCities\Providers\AlgerianCitiesServiceProvider::class]); 43 | 44 | Artisan::call('db:seed --class=WilayaCommuneSeeder'); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.github/workflows/dependabot-auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: dependabot-auto-merge 2 | on: pull_request_target 3 | 4 | permissions: 5 | pull-requests: write 6 | contents: write 7 | 8 | jobs: 9 | dependabot: 10 | runs-on: ubuntu-latest 11 | timeout-minutes: 5 12 | if: ${{ github.actor == 'dependabot[bot]' }} 13 | steps: 14 | 15 | - name: Dependabot metadata 16 | id: metadata 17 | uses: dependabot/fetch-metadata@v2.4.0 18 | with: 19 | github-token: "${{ secrets.GITHUB_TOKEN }}" 20 | 21 | - name: Auto-merge Dependabot PRs for semver-minor updates 22 | if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}} 23 | run: gh pr merge --auto --merge "$PR_URL" 24 | env: 25 | PR_URL: ${{github.event.pull_request.html_url}} 26 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 27 | 28 | - name: Auto-merge Dependabot PRs for semver-patch updates 29 | if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} 30 | run: gh pr merge --auto --merge "$PR_URL" 31 | env: 32 | PR_URL: ${{github.event.pull_request.html_url}} 33 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 34 | -------------------------------------------------------------------------------- /baseline-8.1.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | ignoreErrors: 3 | - 4 | message: "#^Method Kossa\\\\AlgerianCities\\\\Commune\\:\\:wilaya\\(\\) should return Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsTo\\ but returns Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsTo\\\\.$#" 5 | count: 1 6 | path: src/Commune.php 7 | 8 | - 9 | message: "#^Generic type Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\HasMany\\ in PHPDoc tag @return specifies 2 template types, but class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\HasMany supports only 1\\: TRelatedModel$#" 10 | count: 1 11 | path: src/Wilaya.php 12 | 13 | - 14 | message: "#^Method Kossa\\\\AlgerianCities\\\\Wilaya\\:\\:communes\\(\\) should return Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\HasMany\\ but returns Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\HasMany\\\\.$#" 15 | count: 1 16 | path: src/Wilaya.php 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `kossa/algerian-cities` will be documented in this file. 4 | 5 | ## v4.1.2 - 2025-03-02 6 | 7 | ### What's Changed 8 | 9 | * Extend CI Workflow for PHP 8.4 & Stability Testing; Add Larastan ^3.0 Support by @n4ss1m in https://github.com/kossa/algerian-cities/pull/24 10 | * Bump dependabot/fetch-metadata from 2.2.0 to 2.3.0 by @dependabot in https://github.com/kossa/algerian-cities/pull/25 11 | * Bump aglipanci/laravel-pint-action from 2.4 to 2.5 by @dependabot in https://github.com/kossa/algerian-cities/pull/26 12 | * Support Laravel 12 by @n4ss1m in https://github.com/kossa/algerian-cities/pull/27 13 | 14 | ### New Contributors 15 | 16 | * @dependabot made their first contribution in https://github.com/kossa/algerian-cities/pull/25 17 | 18 | **Full Changelog**: https://github.com/kossa/algerian-cities/compare/v4.1.1...v4.1.2 19 | 20 | ## v4.0.1 - 2024-10-31 21 | 22 | ### What's Changed 23 | 24 | * Update changelog workflow to use master branch for changelog updates by @n4ss1m in https://github.com/kossa/algerian-cities/pull/18 25 | * feature : add API enable/disable configuration for Algerian Cities package by @n4ss1m in https://github.com/kossa/algerian-cities/pull/21 26 | 27 | **Full Changelog**: https://github.com/kossa/algerian-cities/compare/v4.0.0...v4.0.1 28 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | tests 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | ./src 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/Controllers/Api/WilayaController.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | public function index(): Collection 19 | { 20 | return Wilaya::all(); 21 | } 22 | 23 | /** 24 | * Get a specified Wilaya. 25 | */ 26 | public function show(int $id): Wilaya 27 | { 28 | return Wilaya::findOrFail($id); 29 | } 30 | 31 | /** 32 | * Get communes of wilayas_id. 33 | * 34 | * @return Collection 35 | */ 36 | public function communes(int $id): Collection 37 | { 38 | return Commune::where('wilaya_id', $id)->get(); 39 | } 40 | 41 | /** 42 | * Search wilaya by name or arabic_name 43 | * 44 | * @return Collection 45 | */ 46 | public function search(string $keyword): Collection 47 | { 48 | return Wilaya::where('name', 'like', sprintf('%%%s%%', $keyword)) 49 | ->orWhere('arabic_name', 'like', sprintf('%%%s%%', $keyword)) 50 | ->get(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | up(); 28 | } 29 | 30 | Artisan::call('db:seed', ['--class' => 'WilayaCommuneSeeder']); 31 | } 32 | 33 | /** 34 | * Include the package's service provider(s) 35 | ** 36 | * @param Application $app 37 | */ 38 | protected function getPackageProviders($app): array 39 | { 40 | return [ 41 | \Kossa\AlgerianCities\Providers\AlgerianCitiesServiceProvider::class, 42 | ]; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /database/migrations/2024_10_26_000000_create_cities_table.php.stub: -------------------------------------------------------------------------------- 1 | id(); 22 | $table->string('name'); 23 | $table->string('arabic_name'); 24 | $table->decimal('longitude', 9, 6); 25 | $table->decimal('latitude', 9, 6); 26 | $table->timestamps(); 27 | }); 28 | 29 | Schema::create('communes', function (Blueprint $table) { 30 | $table->id(); 31 | $table->string('name'); 32 | $table->string('arabic_name'); 33 | $table->string('post_code'); 34 | $table->foreignId('wilaya_id')->constrained()->onDelete('cascade'); 35 | $table->decimal('longitude', 9, 6); 36 | $table->decimal('latitude', 9, 6); 37 | $table->timestamps(); 38 | }); 39 | } 40 | 41 | /** 42 | * Reverse the migrations. 43 | * 44 | * @return void 45 | */ 46 | public function down() 47 | { 48 | Schema::dropIfExists('communes'); 49 | Schema::dropIfExists('wilayas'); 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /src/helpers.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | function communes(?int $wilaya_id = null, bool $withWilaya = false, string $name = 'name'): array 16 | { 17 | $communes = Commune::query(); 18 | 19 | if ($wilaya_id) { 20 | $communes->where('wilaya_id', $wilaya_id); 21 | } 22 | 23 | if ($withWilaya) { 24 | $communes->withWilaya($name); 25 | } 26 | 27 | return $communes->pluck('name', 'id')->toArray(); 28 | } 29 | } 30 | 31 | if (! function_exists('wilayas')) { 32 | 33 | /** 34 | * Get list of wilayas 35 | * 36 | * @return array 37 | */ 38 | function wilayas(string $name = 'name'): array 39 | { 40 | return wilaya::pluck($name, 'id')->toArray(); 41 | } 42 | } 43 | 44 | if (! function_exists('commune')) { 45 | 46 | /** 47 | * Get single commune 48 | */ 49 | function commune(int $id, bool $withWilaya = false): Commune 50 | { 51 | $commune = Commune::findOrFail($id); 52 | 53 | if ($withWilaya) { 54 | $commune->load('wilaya'); 55 | } 56 | 57 | return $commune; 58 | } 59 | } 60 | 61 | if (! function_exists('wilaya')) { 62 | 63 | /** 64 | * Get single wilaya 65 | **/ 66 | function wilaya(int $id): Wilaya 67 | { 68 | return wilaya::findOrFail($id); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /.github/workflows/laravel.yml: -------------------------------------------------------------------------------- 1 | name: Laravel Test Suite 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | timeout-minutes: 5 9 | strategy: 10 | fail-fast: true 11 | matrix: 12 | php: [8.1, 8.2, 8.3, 8.4] 13 | laravel: [ "^12.0", "^11.0", "^10.0" ] 14 | dependency-version: [prefer-lowest, prefer-stable] 15 | include: 16 | - laravel: "^12.0" 17 | testbench: 10.* 18 | - laravel: "^11.0" 19 | testbench: 9.* 20 | - laravel: "^10.0" 21 | testbench: 8.* 22 | exclude: 23 | - laravel: "^12.0" 24 | php: 8.1 25 | - laravel: "^11.0" 26 | php: 8.1 27 | - laravel: "^9.0" 28 | php: 8.4 29 | 30 | name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} 31 | 32 | steps: 33 | - name: Checkout code 34 | uses: actions/checkout@v4 35 | 36 | - name: Setup PHP 37 | uses: shivammathur/setup-php@v2 38 | with: 39 | php-version: ${{ matrix.php }} 40 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo 41 | coverage: xdebug 42 | 43 | - name: Validate composer.json 44 | run: composer validate 45 | 46 | - name: Install dependencies 47 | run: composer install --prefer-dist --no-interaction 48 | 49 | - name: List Installed Dependencies 50 | run: composer show -D 51 | 52 | - name: Execute tests 53 | run: composer test -------------------------------------------------------------------------------- /src/Commune.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | public static function rules(): array 25 | { 26 | return [ 27 | 'name' => 'required', 28 | 'wilaya_id' => 'required|numeric', 29 | ]; 30 | } 31 | 32 | /** 33 | * @param Builder $query 34 | */ 35 | public function scopeWithWilaya(Builder $query, string $name = 'name'): void 36 | { 37 | $query->leftJoin('wilayas', 'wilayas.id', 'communes.wilaya_id') 38 | ->select('communes.id', DB::raw(sprintf("concat(communes.%s, ', ', wilayas.%s) as name", $name, $name))); 39 | } 40 | 41 | /** 42 | * @return BelongsTo 43 | */ 44 | public function wilaya(): BelongsTo 45 | { 46 | return $this->belongsTo(Wilaya::class)->withDefault(); 47 | } 48 | 49 | /* 50 | |------------------------------------------------------------------------------------ 51 | | Attribute 52 | |------------------------------------------------------------------------------------ 53 | */ 54 | public function getWilayaNameAttribute(): string 55 | { 56 | return $this->wilaya->name; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Providers/AlgerianCitiesServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes([ 18 | __DIR__.'/../../database/migrations/2024_10_26_000000_create_cities_table.php.stub' => database_path('migrations').'/2024_10_26_000000_create_cities_table.php', 19 | 20 | ], 'migrations'); 21 | 22 | // Seeds 23 | $this->publishes([ 24 | __DIR__.'/../../database/seeders/' => database_path('seeders'), 25 | ], 'seeds'); 26 | 27 | // Commande 28 | if ($this->app->runningInConsole()) { 29 | $this->commands([ 30 | \Kossa\AlgerianCities\Console\Commands\AlgerianCitiesCommand::class, 31 | ]); 32 | } 33 | 34 | // Config file 35 | $this->publishes([ 36 | __DIR__.'/../../config/algerian-cities.php' => config_path('algerian-cities.php'), 37 | ], 'config'); 38 | 39 | if (config('algerian-cities.api_enabled')) { 40 | $this->loadRoutesFrom(__DIR__.'/../../routes/api.php'); 41 | } 42 | 43 | require __DIR__.'/../helpers.php'; 44 | } 45 | 46 | /** 47 | * Register the application services. 48 | */ 49 | public function register(): void 50 | { 51 | /** 52 | * Config file 53 | * 54 | * Uncomment this function call to load the config file. 55 | * If the config file is also publishable, it will merge with that file 56 | */ 57 | $this->mergeConfigFrom( 58 | __DIR__.'/../../config/algerian-cities.php', 'algerian-cities' 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kossa/algerian-cities", 3 | "description": "A Laravel package to create/load wilayas and communes of Algeria", 4 | "keywords": ["Laravel", "Wilaya", "Algeria", "Commune", "Cities"], 5 | "authors": [ 6 | { 7 | "name": "kouceyla", 8 | "email": "hadjikouceyla@gmail.com" 9 | } 10 | ], 11 | "homepage": "https://github.com/kossa/algerian-cities", 12 | "require": { 13 | "php": "^8.1", 14 | "laravel/framework": "^10.0 | ^11.0 | ^12.0" 15 | }, 16 | "require-dev": { 17 | "orchestra/testbench": "^8.0 | ^9.0 | ^10.0", 18 | "laravel/pint": "^1.18", 19 | "larastan/larastan": "^2.9 | ^3.0", 20 | "rector/rector": "^1.0 | ^2.0", 21 | "pestphp/pest": "^2.36 | ^3.0" 22 | }, 23 | "license": "MIT", 24 | "autoload": { 25 | "psr-4": { 26 | "Kossa\\AlgerianCities\\": "src/", 27 | "Database\\Migrations\\": "database/migrations/", 28 | "Database\\Seeders\\": "database/seeders/" 29 | }, 30 | "files": [ 31 | "src/helpers.php" 32 | ] 33 | }, 34 | "autoload-dev": { 35 | "psr-4": { 36 | "Kossa\\AlgerianCities\\Tests\\": "tests/" 37 | } 38 | }, 39 | "extra": { 40 | "laravel": { 41 | "providers": [ 42 | "Kossa\\AlgerianCities\\Providers\\AlgerianCitiesServiceProvider" 43 | ] 44 | } 45 | }, 46 | "scripts": { 47 | "post-install-cmd": [ 48 | "Kossa\\AlgerianCities\\Console\\Commands\\AlgerianCitiesCommand::handle" 49 | ], 50 | "test:unit": "pest --parallel --coverage --min=54", 51 | "test:format": "pint --test", 52 | "test:refactor": "rector --dry-run", 53 | "test:types": "vendor/bin/phpstan analyse --memory-limit=256M", 54 | "test": [ 55 | "@test:unit", 56 | "@test:format", 57 | "@test:refactor", 58 | "@test:types" 59 | ], 60 | "refactor": "rector", 61 | "format": "vendor/bin/pint" 62 | }, 63 | "minimum-stability": "dev", 64 | "prefer-stable": true, 65 | "config": { 66 | "allow-plugins": { 67 | "pestphp/pest-plugin": true 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report an Issue or Bug with the Package 3 | title: "[Bug]: " 4 | labels: ["bug"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | We're sorry to hear you have a problem. Can you help us solve it by providing the following details. 10 | - type: textarea 11 | id: what-happened 12 | attributes: 13 | label: What happened? 14 | description: What did you expect to happen? 15 | placeholder: I cannot currently do X thing because when I do, it breaks X thing. 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: how-to-reproduce 20 | attributes: 21 | label: How to reproduce the bug 22 | description: How did this occur, please add any config values used and provide a set of reliable steps if possible. 23 | placeholder: When I do X I see Y. 24 | validations: 25 | required: true 26 | - type: input 27 | id: package-version 28 | attributes: 29 | label: Package Version 30 | description: What version of our Package are you running? Please be as specific as possible 31 | placeholder: 4.0.0 32 | validations: 33 | required: true 34 | - type: input 35 | id: php-version 36 | attributes: 37 | label: PHP Version 38 | description: What version of PHP are you running? Please be as specific as possible 39 | placeholder: 8.1.0 40 | validations: 41 | required: true 42 | - type: input 43 | id: laravel-version 44 | attributes: 45 | label: Laravel Version 46 | description: What version of Laravel are you running? Please be as specific as possible 47 | placeholder: 10.0.0 48 | validations: 49 | required: true 50 | - type: dropdown 51 | id: operating-systems 52 | attributes: 53 | label: Which operating systems does with happen with? 54 | description: You may select more than one. 55 | multiple: true 56 | options: 57 | - macOS 58 | - Windows 59 | - Linux 60 | - type: textarea 61 | id: notes 62 | attributes: 63 | label: Notes 64 | description: Use this field to provide any other notes that you feel might be relevant to the issue. 65 | validations: 66 | required: false -------------------------------------------------------------------------------- /database/seeders/WilayaCommuneSeeder.php: -------------------------------------------------------------------------------- 1 | count(); 24 | $communes = DB::table('communes')->count(); 25 | 26 | if (! $wilayas && ! $communes) { 27 | $this->loadData(); 28 | $this->command->info('Success!! wilayas and communes are loaded successfully'); 29 | 30 | return; 31 | } 32 | 33 | $this->command->comment('Wilayas/Communes already loaded'); 34 | } 35 | 36 | protected function loadData(): void 37 | { 38 | $this->insertWilayas(); 39 | $this->insertCommunes(); 40 | } 41 | 42 | protected function insertWilayas(): void 43 | { 44 | // Load wilayas from json 45 | try { 46 | $wilayas_json = json_decode(file_get_contents(database_path('/seeders/json/Wilaya_Of_Algeria.json'))); 47 | } catch (ErrorException $e) { 48 | $wilayas_json = json_decode(file_get_contents(__DIR__.'/json/Wilaya_Of_Algeria.json')); 49 | } 50 | // Insert Wilayas 51 | $data = []; 52 | foreach ($wilayas_json as $wilaya) { 53 | $data[] = [ 54 | 'name' => $wilaya->name, 55 | 'arabic_name' => $wilaya->ar_name, 56 | 'longitude' => $wilaya->longitude, 57 | 'latitude' => $wilaya->latitude, 58 | 'created_at' => now(), 59 | ]; 60 | } 61 | DB::table('wilayas')->insert($data); 62 | } 63 | 64 | protected function insertCommunes(): void 65 | { 66 | // Load wilayas from json 67 | try { 68 | $communes_json = json_decode(file_get_contents(database_path('/seeders/json/Commune_Of_Algeria.json'))); 69 | } catch (ErrorException $e) { 70 | $communes_json = json_decode(file_get_contents(__DIR__.'/json/Commune_Of_Algeria.json')); 71 | } 72 | // Insert communes 73 | $data = []; 74 | foreach ($communes_json as $commune) { 75 | $data[] = [ 76 | 'name' => $commune->name, 77 | 'arabic_name' => $commune->ar_name, 78 | 'post_code' => $commune->post_code, 79 | 'wilaya_id' => $commune->wilaya_id, 80 | 'longitude' => $commune->longitude, 81 | 'latitude' => $commune->latitude, 82 | 'created_at' => now(), 83 | ]; 84 | } 85 | DB::table('communes')->insert($data); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /database/seeders/json/Wilaya_Of_Algeria.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"id": 1,"code": 1,"name": "Adrar","ar_name": "أدرار","longitude": 28.0174403,"latitude": -0.2642497 }, 3 | {"id": 2,"code": 2,"name": "Chlef","ar_name": "الشلف","longitude": 36.1579664,"latitude": 1.3372823 }, 4 | {"id": 3,"code": 3,"name": "Laghouat","ar_name": "الأغواط","longitude": 33.8078341,"latitude": 2.8628294 }, 5 | {"id": 4,"code": 4,"name": "Oum El Bouaghi","ar_name": "أم البواقي","longitude": 35.8688789,"latitude": 7.1108266 }, 6 | {"id": 5,"code": 5,"name": "Batna","ar_name": "باتنة","longitude": 35.5634192,"latitude": 6.1889996 }, 7 | {"id": 6,"code": 6,"name": "Béjaïa","ar_name": "بجاية","longitude": 36.7515258,"latitude": 5.0556837 }, 8 | {"id": 7,"code": 7,"name": "Biskra","ar_name": "بسكرة","longitude": 34.8449437,"latitude": 5.7248567 }, 9 | {"id": 8,"code": 8,"name": "Bechar","ar_name": "بشار","longitude": 31.6238098,"latitude": -2.2162443 }, 10 | {"id": 9,"code": 9,"name": "Blida","ar_name": "البليدة","longitude": 36.4735715,"latitude": 2.8323153 }, 11 | {"id": 10,"code": 10,"name": "Bouira","ar_name": "البويرة","longitude": 36.3691846,"latitude": 3.9006194 }, 12 | {"id": 11,"code": 11,"name": "Tamanrasset","ar_name": "تمنراست","longitude": 22.7902972,"latitude": 5.5193268 }, 13 | {"id": 12,"code": 12,"name": "Tbessa","ar_name": "تبسة","longitude": 35.1290691,"latitude": 7.9592863 }, 14 | {"id": 13,"code": 13,"name": "Tlemcen","ar_name": "تلمسان","longitude": 34.8828864,"latitude": -1.3166815 }, 15 | {"id": 14,"code": 14,"name": "Tiaret","ar_name": "تيارت","longitude": 35.3708689,"latitude": 1.3217852 }, 16 | {"id": 15,"code": 15,"name": "Tizi Ouzou","ar_name": "تيزي وزو","longitude": 36.706911,"latitude": 4.2333355 }, 17 | {"id": 16,"code": 16,"name": "Alger","ar_name": "الجزائر","longitude": 36.753768,"latitude": 3.0587561 }, 18 | {"id": 17,"code": 17,"name": "Djelfa","ar_name": "الجلفة","longitude": 34.6703956,"latitude": 3.2503761 }, 19 | {"id": 18,"code": 18,"name": "Jijel","ar_name": "جيجل","longitude": 36.8210144,"latitude": 5.7634126 }, 20 | {"id": 19,"code": 19,"name": "Setif","ar_name": "سطيف","longitude": 36.1897593,"latitude": 5.4107984 }, 21 | {"id": 20,"code": 20,"name": "Saefda","ar_name": "سعيدة","longitude": 36.7505029,"latitude": 3.4695305 }, 22 | {"id": 21,"code": 21,"name": "Skikda","ar_name": "سكيكدة","longitude": 36.8662658,"latitude": 6.9062556 }, 23 | {"id": 22,"code": 22,"name": "Sidi Bel Abbes","ar_name": "سيدي بلعباس","longitude": 35.2105876,"latitude": -0.629983 }, 24 | {"id": 23,"code": 23,"name": "Annaba","ar_name": "عنابة","longitude": 36.9142081,"latitude": 7.7426673 }, 25 | {"id": 24,"code": 24,"name": "Guelma","ar_name": "قالمة","longitude": 36.4627444,"latitude": 7.4330833 }, 26 | {"id": 25,"code": 25,"name": "Constantine","ar_name": "قسنطينة","longitude": 36.3570052,"latitude": 6.6390282 }, 27 | {"id": 26,"code": 26,"name": "Medea","ar_name": "المدية","longitude": 36.2637078,"latitude": 2.7587857 }, 28 | {"id": 27,"code": 27,"name": "Mostaganem","ar_name": "مستغانم","longitude": 35.9311454,"latitude": 0.0909414 }, 29 | {"id": 28,"code": 28,"name": "M'Sila","ar_name": "المسيلة","longitude": 35.7186646,"latitude": 4.5233423 }, 30 | {"id": 29,"code": 29,"name": "Mascara","ar_name": "معسكر","longitude": 35.4020988,"latitude": 0.1400257 }, 31 | {"id": 30,"code": 30,"name": "Ouargla","ar_name": "ورقلة","longitude": 31.9527411,"latitude": 5.3335348 }, 32 | {"id": 31,"code": 31,"name": "Oran","ar_name": "وهران","longitude": 35.6987388,"latitude": -0.6349319 }, 33 | {"id": 32,"code": 32,"name": "El Bayadh","ar_name": "البيض","longitude": 33.6854149,"latitude": 1.0303543 }, 34 | {"id": 33,"code": 33,"name": "Illizi","ar_name": "إليزي","longitude": 26.4775863,"latitude": 8.4776326 }, 35 | {"id": 34,"code": 34,"name": "Bordj Bou Arreridj","ar_name": "برج بوعريريج","longitude": 36.0739925,"latitude": 4.7630271 }, 36 | {"id": 35,"code": 35,"name": "Boumerdes","ar_name": "بومرداس","longitude": 36.7510697,"latitude": 3.4788905 }, 37 | {"id": 36,"code": 36,"name": "El Tarf","ar_name": "الطارف","longitude": 36.7576678,"latitude": 8.3076343 }, 38 | {"id": 37,"code": 37,"name": "Tindouf","ar_name": "تندوف","longitude": 27.6719159,"latitude": -8.1398003 }, 39 | {"id": 38,"code": 38,"name": "Tissemsilt","ar_name": "تيسمسيلت","longitude": 35.8174139,"latitude": 1.5983959 }, 40 | {"id": 39,"code": 39,"name": "El Oued","ar_name": "الوادي","longitude": 33.367811,"latitude": 6.8516511 }, 41 | {"id": 40,"code": 40,"name": "Khenchela","ar_name": "خنشلة","longitude": 35.4269404,"latitude": 7.1460155 }, 42 | {"id": 41,"code": 41,"name": "Souk Ahras","ar_name": "سوق أهراس","longitude": 36.2801062,"latitude": 7.9384033 }, 43 | {"id": 42,"code": 42,"name": "Tipaza","ar_name": "تيبازة","longitude": 36.5906719,"latitude": 2.4433723 }, 44 | {"id": 43,"code": 43,"name": "Mila","ar_name": "ميلة","longitude": 36.4519049,"latitude": 6.2584338 }, 45 | {"id": 44,"code": 44,"name": "Ain Defla","ar_name": "عين الدفلى","longitude": 36.1585027,"latitude": 2.0665197 }, 46 | {"id": 45,"code": 45,"name": "Naama","ar_name": "النعامة","longitude": 33.4350615,"latitude": -0.9056623 }, 47 | {"id": 46,"code": 46,"name": "Ain Temouchent","ar_name": "عين تموشنت","longitude": 35.2974176,"latitude": -1.135302 }, 48 | {"id": 47,"code": 47,"name": "Ghardaefa","ar_name": "غرداية","longitude": 32.4943741,"latitude": 3.64446 }, 49 | {"id": 48,"code": 48,"name": "Relizane","ar_name": "غليزان","longitude": 35.7383405,"latitude": 0.7532809 }, 50 | {"id": 49,"code": 49,"name": "Timimoun","ar_name": "تيميمون","longitude": 29.2616911,"latitude": 0.2415964 }, 51 | {"id": 50,"code": 50,"name": "Bordj Baji Mokhtar","ar_name": "برج باجي مختار","longitude": 21.32551,"latitude": 0.9524803 }, 52 | {"id": 51,"code": 51,"name": "Ouled Djellal","ar_name": "اولاد جلال","longitude": 34.4294289,"latitude": 5.0681159 }, 53 | {"id": 52,"code": 52,"name": "Béni Abbès","ar_name": "بني عباس","longitude": 30.1312217,"latitude": -2.1662258 }, 54 | {"id": 53,"code": 53,"name": "In Salah","ar_name": "عين صالح","longitude": 27.2026311,"latitude": 2.4878487 }, 55 | {"id": 54,"code": 54,"name": "In Guezzam","ar_name": "عين قزام","longitude": 19.5632255,"latitude": 5.7717952 }, 56 | {"id": 55,"code": 55,"name": "Touggourt","ar_name": "تقرت","longitude": 33.1049642,"latitude": 6.0662834 }, 57 | {"id": 56,"code": 56,"name": "Djanet","ar_name": "جانت","longitude": 24.554151,"latitude": 9.485429 }, 58 | {"id": 57,"code": 57,"name": "El M'ghair","ar_name": "المغير","longitude": 33.950285,"latitude": 5.9244238 }, 59 | {"id": 58,"code": 58,"name": "El Menia","ar_name": "المنيعة","longitude": 30.5833161,"latitude": 2.8836701 } 60 | ] 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | Laravel Algerian Cities 3 |
4 | GitHub Workflow Status (master) 5 | Total Downloads 6 | Latest Version 7 | GitHub Workflow Status (master) 8 | License 9 |
10 |
11 | 12 | ------ 13 | 14 | **Laravel Algerian Cities** : A comprehensive Laravel package to easily manage and interact with Algerian administrative divisions. 15 | 16 | It provides functionality to load Wilayas (provinces) and Communes (municipalities) in both Arabic and French, complete with postal codes and precise latitude/longitude coordinates for each commune. 17 | 18 | ## Features 19 | - Includes all 58 Algerian Wilayas and 1541 Communes. 20 | - Wilaya and Commune Eloquent models with relationships. 21 | - Supports Arabic and French languages. 22 | - Includes postal codes and latitude/longitude for each commune. 23 | - [Helper functions for easy integration in Blade views](#using-helper-functions). 24 | - [Available as API endpoints](#using-the-package-as-an-api). 25 | 26 | ## Requirements 27 | 28 | - **PHP** : 8.1 or higher 29 | - **Laravel** : 10 or higher 30 | 31 | ## Installation 32 | 33 | You can install the package via composer: 34 | 35 | ```bash 36 | composer require kossa/algerian-cities 37 | ``` 38 | 39 | Next, publish the migrations and seeders by running the installation command: 40 | 41 | 42 | ```bash 43 | php artisan algerian-cities:install 44 | ``` 45 | 46 | ## Usage 47 | ### Basic usage 48 | 49 | The package provides two models: `Wilaya` and `Commune`. 50 | 51 | A `Wilaya` has many `Commune`, and you can interact with them just like any other Eloquent models. 52 | 53 | ```php 54 | // Retrieve all Wilayas 55 | $wilayas = Wilaya::all(); 56 | 57 | // Retrieve all Communes 58 | $communes = Commune::all(); 59 | 60 | // Get all Communes belonging to Algiers (Wilaya ID: 16) 61 | $algiers_communes = Commune::where('wilaya_id', 16)->get(); 62 | ``` 63 | 64 | ### Using Helper Functions 65 | 66 | The package provides several helper functions for convenient data retrieval: 67 | 68 | ```php 69 | $wilayas = wilayas(); // Get all Wilayas as $id => $name 70 | $wilayas = wilayas('arabic_name'); // Get all Wilayas with names in Arabic 71 | $communes = communes(); // Get all Communes as $id => $name 72 | $communes = communes(16); // Get all Communes of Algiers (Wilaya ID: 16) as $id => $name 73 | $communes = communes(16, $withWilaya = true); // Get all Communes of Algiers (16) including Wilayas: "Alger Centre, Alger" 74 | $communes = communes(16, $withWilaya = true, $name = 'arabic_name'); // Get all Communes of Algiers (16) with Wilayas in Arabic: "الجزائر الوسطى, الجزائر" 75 | 76 | $single_commune = commune(1); // Retrieve a single Commune model 77 | $single_commune = commune(1, $withWilaya = true); // Retrieve a single Commune model, including its Wilaya 78 | $single_wilaya = wilaya(1); // Retrieve a single Wilaya model 79 | ``` 80 | 81 | 82 | ### Blade Templates / Views 83 | 84 | You can leverage the provided helpers or models to populate ` 89 | @foreach (wilayas() as $id => $wilaya) 90 | 91 | @endforeach 92 | 93 | 94 | 95 | 100 | 101 | 102 | 107 | 108 | 109 | 114 | 115 | 116 | 121 | ``` 122 | 123 | ## Using the Package as an API 124 | 125 | This package includes `api.php` routes, allowing you to interact with the data through a RESTful API. Here are the available endpoints: 126 | 127 | | Verb | URI | Description | 128 | |------|------------------------------|----------------------------------------------------| 129 | | GET | `/api/wilayas` | Retrieve all Wilayas | 130 | | GET | `/api/wilayas/{id}` | Retrieve a specific Wilaya by ID | 131 | | GET | `/api/wilayas/{id}/communes` | Retrieve all Communes from a specific Wilaya by ID | 132 | | GET | `/api/communes` | Retrieve all Communes | 133 | | GET | `/api/communes/{id}` | Retrieve a specific Commune by ID | 134 | | GET | `/api/search/wilaya/{q}` | Search Wilayas by name or Arabic name | 135 | | GET | `/api/search/commune/{q}` | Search Communes by name or Arabic name | 136 | 137 | ### API Availability Toggle 138 | 139 | You can enable or disable the Algerian Cities API endpoints by setting the following option in your `.env` file: 140 | 141 | ```dotenv 142 | ALGERIAN_CITIES_API_ENABLED=false # Default: true 143 | ``` 144 | 145 | ---- 146 | 147 | ## Future Planned Features 148 | 149 | - [ ] Add support for Dairas (districts), including relationships with Wilayas and Communes 150 | - [ ] Add support for additional languages 151 | - [ ] Add a configuration file to allow customizing package behaviors 152 | - [ ] Add support for caching to optimize API responses 153 | - [ ] fix PHPUnit Deprecations 154 | 155 | ## Contribution 156 | 157 | We welcome all contributions! Please follow these guidelines: 158 | 159 | 1. Document any changes in behavior — ensure `README.md` updated accordingly. 160 | 2. Write tests to cover any new functionality. 161 | 3. Please ensure that your pull request passes all tests. 162 | 163 | ## Issues & Suggesting Features 164 | 165 | If you encounter any issues or have ideas for new features, please [open an issue](https://github.com/kossa/algerian-cities/issues/new). 166 | 167 | We appreciate your feedback and contributions to help improve this package. 168 | 169 | ## Credits 170 | 171 | - [Kouceyla](https://github.com/kossa) , and all [contributors](https://github.com/kossa/algerian-cities/graphs/contributors) who have helped improve and enhance the project. 172 | - The list of Wilayas and Communes is sourced from [Wilaya-Of-Algeria](https://github.com/bahinapster/Wilaya-Of-Algeria/). 173 | 174 | ## Security Reports 175 | 176 | If you discover any security vulnerabilities, please report them by emailing the package maintainer at `hadjikouceyla at gmail`. 177 | 178 | ## ⭐ Support Us 179 | 180 | If you find this package helpful, please consider giving it a ⭐ on [GitHub](https://github.com/kossa/algerian-cities) ! 181 | Your support encourages us to keep improving the project. 182 | Thank you! 183 | 184 | [![Stargazers repo roster for @kossa/algerian-cities](https://reporoster.com/stars/dark/kossa/algerian-cities)](https://github.com/kossa/algerian-cities/stargazers) 185 | 186 | ## License 187 | 188 | This package is open-sourced software licensed under the [MIT License](https://opensource.org/licenses/MIT). --------------------------------------------------------------------------------