Skip to content

Commit b213086

Browse files
committed
feat(regions): implement world regions
1 parent d682821 commit b213086

34 files changed

Lines changed: 2433 additions & 0 deletions

database/factories/CountryFactory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public function definition(): array
2727
'num_code' => str_pad(fake()->numberBetween(1, 999), 3, '0', STR_PAD_LEFT),
2828
'name' => fake()->country(),
2929
'flag' => fake()->emoji(),
30+
'region_id' => null,
3031
];
3132
}
3233
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
namespace Eclipse\World\Factories;
4+
5+
use Eclipse\World\Models\Country;
6+
use Eclipse\World\Models\CountrySpecialRegion;
7+
use Eclipse\World\Models\Region;
8+
use Illuminate\Database\Eloquent\Factories\Factory;
9+
10+
class CountrySpecialRegionFactory extends Factory
11+
{
12+
/**
13+
* The name of the factory's corresponding model.
14+
*
15+
* @var string
16+
*/
17+
protected $model = CountrySpecialRegion::class;
18+
19+
/**
20+
* Define the model's default state.
21+
*
22+
* @return array<string, mixed>
23+
*/
24+
public function definition(): array
25+
{
26+
return [
27+
'country_id' => Country::factory(),
28+
'region_id' => Region::factory()->special(),
29+
'start_date' => $this->faker->dateTimeBetween('-2 years', 'now')->format('Y-m-d'),
30+
'end_date' => $this->faker->optional(0.3)->dateTimeBetween('now', '+2 years')?->format('Y-m-d'),
31+
];
32+
}
33+
34+
/**
35+
* Set the region as active.
36+
*
37+
* @return $this
38+
*/
39+
public function active(): static
40+
{
41+
return $this->state([
42+
'start_date' => $this->faker->dateTimeBetween('-1 year', 'now')->format('Y-m-d'),
43+
'end_date' => null,
44+
]);
45+
}
46+
47+
/**
48+
* Set the region as ended.
49+
*
50+
* @return $this
51+
*/
52+
public function ended(): static
53+
{
54+
return $this->state([
55+
'start_date' => $this->faker->dateTimeBetween('-2 years', '-6 months')->format('Y-m-d'),
56+
'end_date' => $this->faker->dateTimeBetween('-6 months', 'now')->format('Y-m-d'),
57+
]);
58+
}
59+
60+
/**
61+
* Set the region as future.
62+
*
63+
* @return $this
64+
*/
65+
public function future(): static
66+
{
67+
return $this->state([
68+
'start_date' => $this->faker->dateTimeBetween('now', '+6 months')->format('Y-m-d'),
69+
'end_date' => $this->faker->optional(0.5)->dateTimeBetween('+6 months', '+2 years')?->format('Y-m-d'),
70+
]);
71+
}
72+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
namespace Eclipse\World\Factories;
4+
5+
use Eclipse\World\Models\Region;
6+
use Illuminate\Database\Eloquent\Factories\Factory;
7+
8+
class RegionFactory extends Factory
9+
{
10+
/**
11+
* The name of the factory's corresponding model.
12+
*
13+
* @var string
14+
*/
15+
protected $model = Region::class;
16+
17+
/**
18+
* Define the model's default state.
19+
*
20+
* @return array<string, mixed>
21+
*/
22+
public function definition(): array
23+
{
24+
return [
25+
'code' => strtoupper($this->faker->unique()->lexify('???')),
26+
'name' => $this->faker->unique()->words(2, true),
27+
'is_special' => false,
28+
];
29+
}
30+
31+
/**
32+
* Set the region as special.
33+
*
34+
* @return $this
35+
*/
36+
public function special(): static
37+
{
38+
return $this->state(['is_special' => true]);
39+
}
40+
41+
/**
42+
* Set the region as geographical.
43+
*
44+
* @return $this
45+
*/
46+
public function geographical(): static
47+
{
48+
return $this->state(['is_special' => false]);
49+
}
50+
51+
/**
52+
* Set the region as a child of a parent region.
53+
*
54+
* @return $this
55+
*/
56+
public function withParent(Region $parent): static
57+
{
58+
return $this->state(['parent_id' => $parent->id]);
59+
}
60+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
public function up(): void
10+
{
11+
Schema::create('world_regions', function (Blueprint $table) {
12+
$table->id();
13+
$table->string('code')->nullable()->unique();
14+
$table->unsignedBigInteger('parent_id')->nullable();
15+
$table->boolean('is_special')->default(false);
16+
$table->string('name');
17+
$table->timestamps();
18+
$table->softDeletes();
19+
20+
$table->foreign('parent_id')->references('id')->on('world_regions')->onDelete('cascade');
21+
$table->index(['is_special', 'parent_id']);
22+
});
23+
}
24+
25+
public function down(): void
26+
{
27+
Schema::dropIfExists('world_regions');
28+
}
29+
};
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
public function up(): void
10+
{
11+
Schema::table('world_countries', function (Blueprint $table) {
12+
$table->unsignedBigInteger('region_id')->nullable()->after('flag');
13+
$table->foreign('region_id')->references('id')->on('world_regions')->onDelete('set null');
14+
$table->index('region_id');
15+
});
16+
}
17+
18+
public function down(): void
19+
{
20+
Schema::table('world_countries', function (Blueprint $table) {
21+
$table->dropForeign(['region_id']);
22+
$table->dropIndex(['region_id']);
23+
$table->dropColumn('region_id');
24+
});
25+
}
26+
};
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
public function up(): void
10+
{
11+
Schema::create('world_country_in_special_region', function (Blueprint $table) {
12+
$table->id();
13+
$table->string('country_id', 2);
14+
$table->unsignedBigInteger('region_id');
15+
$table->date('start_date');
16+
$table->date('end_date')->nullable();
17+
$table->timestamps();
18+
19+
$table->foreign('country_id')->references('id')->on('world_countries')->onDelete('cascade');
20+
$table->foreign('region_id')->references('id')->on('world_regions')->onDelete('cascade');
21+
22+
$table->unique(['country_id', 'region_id', 'start_date'], 'unique_country_region_start');
23+
$table->index(['region_id', 'start_date', 'end_date'], 'idx_region_dates');
24+
});
25+
}
26+
27+
public function down(): void
28+
{
29+
Schema::dropIfExists('world_country_in_special_region');
30+
}
31+
};
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
use Eclipse\World\Models\Country;
4+
use Eclipse\World\Models\Region;
5+
use Illuminate\Database\Migrations\Migration;
6+
use Illuminate\Support\Carbon;
7+
8+
return new class extends Migration
9+
{
10+
public function up(): void
11+
{
12+
// Create EU special region
13+
$euRegion = Region::create([
14+
'code' => 'EU',
15+
'name' => 'European Union',
16+
'is_special' => true,
17+
]);
18+
19+
$euMemberCountries = [
20+
'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR',
21+
'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL',
22+
'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE',
23+
];
24+
25+
$existingCountries = Country::whereIn('id', $euMemberCountries)->pluck('id');
26+
$membershipData = $existingCountries->mapWithKeys(fn ($countryId) => [
27+
$countryId => [
28+
'start_date' => Carbon::now()->toDateString(),
29+
'created_at' => now(),
30+
'updated_at' => now(),
31+
],
32+
]);
33+
34+
$euRegion->specialCountries()->attach($membershipData);
35+
}
36+
37+
public function down(): void
38+
{
39+
Region::where('code', 'EU')->delete();
40+
}
41+
};

resources/lang/en/countries.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@
2222
'num_code' => [
2323
'label' => 'Num. Code',
2424
],
25+
'region' => [
26+
'label' => 'Region',
27+
],
28+
'special_regions' => [
29+
'label' => 'Special Regions',
30+
],
2531
],
2632

2733
'actions' => [
@@ -67,6 +73,10 @@
6773
'label' => 'Numeric code',
6874
'helper' => 'Numeric code (ISO-3166)',
6975
],
76+
'region' => [
77+
'label' => 'Geographical Region',
78+
'helper' => 'The geographical region this country belongs to',
79+
],
7080
],
7181

7282
'import' => [
@@ -86,4 +96,10 @@
8696
'message' => 'Failed to import countries data.',
8797
],
8898
],
99+
100+
'filters' => [
101+
'region' => [
102+
'label' => 'Region',
103+
],
104+
],
89105
];

resources/lang/en/regions.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
return [
4+
'nav_label' => 'Regions',
5+
'breadcrumb' => 'Regions',
6+
'plural' => 'Regions',
7+
8+
'form' => [
9+
'name' => [
10+
'label' => 'Name',
11+
],
12+
'code' => [
13+
'label' => 'Code',
14+
'helper' => 'Optional unique code for the region (e.g., EU, ASEAN)',
15+
],
16+
'parent' => [
17+
'label' => 'Parent Region',
18+
'helper' => 'Select a parent region to create a sub-region',
19+
],
20+
'is_special' => [
21+
'label' => 'Special Region',
22+
'helper' => 'Special regions are custom regions like EU or EEA that countries can join/leave',
23+
],
24+
],
25+
26+
'table' => [
27+
'name' => [
28+
'label' => 'Name',
29+
],
30+
'code' => [
31+
'label' => 'Code',
32+
],
33+
'parent' => [
34+
'label' => 'Parent Region',
35+
],
36+
'is_special' => [
37+
'label' => 'Special',
38+
],
39+
'countries_count' => [
40+
'label' => 'Countries',
41+
],
42+
'children_count' => [
43+
'label' => 'Sub-regions',
44+
],
45+
],
46+
47+
'filters' => [
48+
'type' => [
49+
'label' => 'Type',
50+
'geographical' => 'Geographical',
51+
'special' => 'Special',
52+
],
53+
'parent' => [
54+
'label' => 'Parent Region',
55+
],
56+
],
57+
58+
'actions' => [
59+
'create' => [
60+
'label' => 'New Region',
61+
'heading' => 'Create Region',
62+
],
63+
'edit' => [
64+
'label' => 'Edit',
65+
'heading' => 'Edit Region',
66+
],
67+
'delete' => [
68+
'label' => 'Delete',
69+
'heading' => 'Delete Region',
70+
],
71+
'restore' => [
72+
'label' => 'Restore',
73+
'heading' => 'Restore Region',
74+
],
75+
'force_delete' => [
76+
'label' => 'Force Delete',
77+
'heading' => 'Force Delete Region',
78+
'description' => 'Are you sure you want to permanently delete ":name"? This action cannot be undone.',
79+
],
80+
],
81+
];

0 commit comments

Comments
 (0)