diff --git a/database/migrations/create_nusa_tables.php b/database/migrations/create_nusa_tables.php index 0d76c50..1eb66ac 100644 --- a/database/migrations/create_nusa_tables.php +++ b/database/migrations/create_nusa_tables.php @@ -60,6 +60,21 @@ public function up(): void $table->foreign('regency_code')->references('code')->on($tableNames['regencies']); $table->foreign('province_code')->references('code')->on($tableNames['provinces']); }); + + Schema::create('addresses', function (Blueprint $table) use ($tableNames) { + $table->id(); + $table->string('line'); + $table->char('village_code', 10)->nullable(); + $table->char('district_code', 6)->nullable(); + $table->char('regency_code', 4)->nullable(); + $table->char('province_code', 2)->nullable(); + $table->char('postal_code', 5)->nullable(); + + $table->foreign('village_code')->references('code')->on($tableNames['villages'])->nullOnDelete(); + $table->foreign('district_code')->references('code')->on($tableNames['districts'])->nullOnDelete(); + $table->foreign('regency_code')->references('code')->on($tableNames['regencies'])->nullOnDelete(); + $table->foreign('province_code')->references('code')->on($tableNames['provinces'])->nullOnDelete(); + }); } /** @@ -69,6 +84,7 @@ public function down(): void { $tableNames = config('creasi.nusa.table_names'); + Schema::dropIfExists('address'); Schema::dropIfExists($tableNames['villages']); Schema::dropIfExists($tableNames['districts']); Schema::dropIfExists($tableNames['regencies']); diff --git a/database/nusa.sqlite b/database/nusa.sqlite index ee6f5af..be20007 100644 Binary files a/database/nusa.sqlite and b/database/nusa.sqlite differ diff --git a/src/Contracts/Address.php b/src/Contracts/Address.php new file mode 100644 index 0000000..95fda87 --- /dev/null +++ b/src/Contracts/Address.php @@ -0,0 +1,22 @@ + $villages + */ +interface District extends Model +{ + // +} diff --git a/src/Contracts/Model.php b/src/Contracts/Model.php new file mode 100644 index 0000000..2727db9 --- /dev/null +++ b/src/Contracts/Model.php @@ -0,0 +1,17 @@ + $regencies + * @property-read \Illuminate\Support\Collection $districts + * @property-read \Illuminate\Support\Collection $villages + */ +interface Province extends Model +{ + // +} diff --git a/src/Contracts/Regency.php b/src/Contracts/Regency.php new file mode 100644 index 0000000..533b9b8 --- /dev/null +++ b/src/Contracts/Regency.php @@ -0,0 +1,16 @@ + $districts + * @property-read \Illuminate\Support\Collection $villages + */ +interface Regency extends Model +{ + // +} diff --git a/src/Contracts/Village.php b/src/Contracts/Village.php new file mode 100644 index 0000000..23cdec6 --- /dev/null +++ b/src/Contracts/Village.php @@ -0,0 +1,19 @@ +casts, [ + 'village_code' => 'int', + 'district_code' => 'int', + 'regency_code' => 'int', + 'province_code' => 'int', + 'postal_code' => 'int', + ]); + } + + public function getFillable() + { + return \array_merge($this->fillable, [ + 'line', + 'village_code', + 'district_code', + 'regency_code', + 'province_code', + 'postal_code', + ]); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo|Village + */ + public function village() + { + return $this->belongsTo(Village::class); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo|District + */ + public function district() + { + return $this->belongsTo(District::class); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo|Regency + */ + public function regency() + { + return $this->belongsTo(Regency::class); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo|Province + */ + public function province() + { + return $this->belongsTo(Province::class); + } +} diff --git a/src/Models/District.php b/src/Models/District.php index c2f9ae9..fc155b4 100644 --- a/src/Models/District.php +++ b/src/Models/District.php @@ -4,16 +4,16 @@ namespace Creasi\Nusa\Models; +use Creasi\Nusa\Contracts\District as DistrictContract; + /** - * @property-read int $regency_code - * @property-read int $province_code * @property-read Province $province * @property-read Regency $regency - * @property-read \Illuminate\Database\Eloquent\Collection|Village[] $villages + * @property-read \Illuminate\Database\Eloquent\Collection $villages */ -class District extends Model +class District extends Model implements DistrictContract { - protected $fillable = ['code', 'regency_code', 'province_code', 'name']; + protected $fillable = ['regency_code', 'province_code']; protected $casts = [ 'regency_code' => 'int', diff --git a/src/Models/Model.php b/src/Models/Model.php index 69eea4f..8de6d9d 100644 --- a/src/Models/Model.php +++ b/src/Models/Model.php @@ -4,18 +4,13 @@ namespace Creasi\Nusa\Models; +use Creasi\Nusa\Contracts\Model as ModelContract; use Illuminate\Database\Eloquent\Model as EloquentModel; /** - * @property-read int $code - * @property-read string $name - * @property-read ?float $latitude - * @property-read ?float $longintude - * @property-read ?array $coordinates - * * @mixin \Illuminate\Contracts\Database\Eloquent\Builder */ -abstract class Model extends EloquentModel +abstract class Model extends EloquentModel implements ModelContract { public $incrementing = false; @@ -31,6 +26,7 @@ public function getConnectionName() public function getCasts() { return \array_merge($this->casts, [ + 'code' => 'int', 'latitude' => 'float', 'longitude' => 'float', 'coordinates' => 'array', @@ -39,6 +35,6 @@ public function getCasts() public function getFillable() { - return \array_merge($this->fillable, ['latitude', 'longitude', 'coordinates']); + return \array_merge($this->fillable, ['code', 'name', 'latitude', 'longitude', 'coordinates']); } } diff --git a/src/Models/Province.php b/src/Models/Province.php index 24bc94d..026b462 100644 --- a/src/Models/Province.php +++ b/src/Models/Province.php @@ -4,14 +4,16 @@ namespace Creasi\Nusa\Models; +use Creasi\Nusa\Contracts\Province as ProvinceContract; + /** - * @property-read \Illuminate\Database\Eloquent\Collection|Regency[] $regencies - * @property-read \Illuminate\Database\Eloquent\Collection|District[] $districts - * @property-read \Illuminate\Database\Eloquent\Collection|Village[] $villages + * @property-read \Illuminate\Database\Eloquent\Collection $regencies + * @property-read \Illuminate\Database\Eloquent\Collection $districts + * @property-read \Illuminate\Database\Eloquent\Collection $villages */ -class Province extends Model +class Province extends Model implements ProvinceContract { - protected $fillable = ['code', 'name']; + protected $fillable = []; protected $casts = []; diff --git a/src/Models/Regency.php b/src/Models/Regency.php index f719b83..f8d38ec 100644 --- a/src/Models/Regency.php +++ b/src/Models/Regency.php @@ -4,15 +4,16 @@ namespace Creasi\Nusa\Models; +use Creasi\Nusa\Contracts\Regency as RegencyContract; + /** - * @property-read int $province_code * @property-read Province $province - * @property-read \Illuminate\Database\Eloquent\Collection|District[] $districts - * @property-read \Illuminate\Database\Eloquent\Collection|Village[] $villages + * @property-read \Illuminate\Database\Eloquent\Collection $districts + * @property-read \Illuminate\Database\Eloquent\Collection $villages */ -class Regency extends Model +class Regency extends Model implements RegencyContract { - protected $fillable = ['code', 'province_code', 'name']; + protected $fillable = ['province_code']; protected $casts = [ 'province_code' => 'int', diff --git a/src/Models/Village.php b/src/Models/Village.php index 5ecf070..5ef976b 100644 --- a/src/Models/Village.php +++ b/src/Models/Village.php @@ -4,18 +4,16 @@ namespace Creasi\Nusa\Models; +use Creasi\Nusa\Contracts\Village as VillageContract; + /** - * @property-read int $district_code - * @property-read int $regency_code - * @property-read int $province_code - * @property-read int $postal_code * @property-read Province $province * @property-read Regency $regency * @property-read District $district */ -class Village extends Model +class Village extends Model implements VillageContract { - protected $fillable = ['code', 'district_code', 'regency_code', 'province_code', 'name', 'postal_code']; + protected $fillable = ['district_code', 'regency_code', 'province_code', 'postal_code']; protected $casts = [ 'district_code' => 'int', diff --git a/tests/Models/DistrictTest.php b/tests/Models/DistrictTest.php index 366a690..d6b82d8 100644 --- a/tests/Models/DistrictTest.php +++ b/tests/Models/DistrictTest.php @@ -4,10 +4,11 @@ namespace Creasi\Tests\Models; +use Creasi\Nusa\Contracts\District as DistrictContract; use Creasi\Nusa\Models\District; -use Creasi\Nusa\Models\Province; -use Creasi\Nusa\Models\Regency; -use Creasi\Nusa\Models\Village; +use Creasi\Nusa\Contracts\Province; +use Creasi\Nusa\Contracts\Regency; +use Creasi\Nusa\Contracts\Village; use Creasi\Tests\TestCase; use Illuminate\Support\Collection; use PHPUnit\Framework\Attributes\Depends; @@ -19,45 +20,56 @@ #[Group('districts')] class DistrictTest extends TestCase { + /** + * @param Collection $districts + * @return Collection + */ #[Test] - #[DependsExternal(RegencyTest::class, 'it_should_be_true')] - public function it_should_be_true() + #[DependsExternal(ProvinceTest::class, 'it_should_has_many_districts')] + public function it_should_has_many_districts(Collection $districts) { - $this->assertTrue(\class_exists(District::class)); - - return District::with([ - 'province', - 'regency', - 'villages' => function ($query) { - $query->take(10); - }, - ])->take(10)->get(); + $districts->each(function (District $district) { + $this->assertIsInt($district->code, 'Code should be int'); + + $this->assertInstanceOf(DistrictContract::class, $district); + }); + + return $districts; } + /** + * @param Collection $districts + */ #[Test] - #[Depends('it_should_be_true')] + #[Depends('it_should_has_many_districts')] public function it_should_belongs_to_province(Collection $districts) { - $districts->each(function (District $dis) { - $this->assertInstanceOf(Province::class, $dis->province); + $districts->each(function (District $district) { + $this->assertInstanceOf(Province::class, $district->province); }); } + /** + * @param Collection $districts + */ #[Test] - #[Depends('it_should_be_true')] + #[Depends('it_should_has_many_districts')] public function it_should_belongs_to_regency(Collection $regencies) { - $regencies->each(function (District $dis) { - $this->assertInstanceOf(Regency::class, $dis->regency); + $regencies->each(function (District $district) { + $this->assertInstanceOf(Regency::class, $district->regency); }); } + /** + * @param Collection $districts + */ #[Test] - #[Depends('it_should_be_true')] + #[Depends('it_should_has_many_districts')] public function it_should_has_many_villages(Collection $districts) { - $districts->each(function (District $dis) { - $this->assertTrue($dis->villages->every(fn ($vil) => $vil instanceof Village)); + $districts->each(function (District $district) { + $this->assertTrue($district->villages->every(fn ($vil) => $vil instanceof Village)); }); } } diff --git a/tests/Models/ProvinceTest.php b/tests/Models/ProvinceTest.php index ab38b4d..3137087 100644 --- a/tests/Models/ProvinceTest.php +++ b/tests/Models/ProvinceTest.php @@ -4,13 +4,14 @@ namespace Creasi\Tests\Models; -use Creasi\Nusa\Models\District; +use Creasi\Nusa\Contracts\District; +use Creasi\Nusa\Contracts\Province as ProvinceContract; use Creasi\Nusa\Models\Province; -use Creasi\Nusa\Models\Regency; -use Creasi\Nusa\Models\Village; +use Creasi\Nusa\Contracts\Regency; +use Creasi\Nusa\Contracts\Village; use Creasi\Tests\NusaTest; use Creasi\Tests\TestCase; -use Illuminate\Database\Eloquent\Collection; +use Illuminate\Support\Collection; use PHPUnit\Framework\Attributes\Depends; use PHPUnit\Framework\Attributes\DependsExternal; use PHPUnit\Framework\Attributes\Group; @@ -21,12 +22,13 @@ class ProvinceTest extends TestCase { #[Test] - #[DependsExternal(NusaTest::class, 'it_should_be_true')] public function it_should_be_true() { - $this->assertTrue(\class_exists(Province::class)); + if (! env('GIT_BRANCH')) { + $this->artisan('nusa:sync'); + } - return Province::with([ + $provinces = Province::with([ 'regencies' => function ($query) { $query->take(10); }, @@ -37,32 +39,70 @@ public function it_should_be_true() $query->take(10); }, ])->get(); + + $provinces->each(function (Province $province) { + $this->assertIsInt($province->code, 'Code should be int'); + $this->assertIsFloat($province->latitude, 'Latitude should be float'); + $this->assertIsFloat($province->longitude, 'Longitude should be float'); + $this->assertIsArray($province->coordinates, 'Coordinates should be array'); + + $this->assertInstanceOf(ProvinceContract::class, $province); + }); + + return $provinces; } + /** + * @param Collection $provinces + */ #[Test] #[Depends('it_should_be_true')] public function it_should_has_many_regencies(Collection $provinces) { - $provinces->each(function (Province $prov) { - $this->assertTrue($prov->regencies->every(fn ($reg) => $reg instanceof Regency)); - }); + $regencies = collect(); + + foreach ($provinces as $province) { + $this->assertTrue($province->regencies->every(fn ($reg) => $reg instanceof Regency)); + + $regencies->push(...$province->regencies); + } + + return $regencies; } + /** + * @param Collection $provinces + */ #[Test] #[Depends('it_should_be_true')] public function it_should_has_many_districts(Collection $provinces) { - $provinces->each(function (Province $prov) { - $this->assertTrue($prov->districts->every(fn ($dis) => $dis instanceof District)); - }); + $districts = \collect(); + + foreach ($provinces as $province) { + $this->assertTrue($province->districts->every(fn ($dis) => $dis instanceof District)); + + $districts->push(...$province->districts); + } + + return $districts; } + /** + * @param Collection $provinces + */ #[Test] #[Depends('it_should_be_true')] public function it_should_has_many_villages(Collection $provinces) { - $provinces->each(function (Province $prov) { - $this->assertTrue($prov->villages->every(fn ($vil) => $vil instanceof Village)); - }); + $villages = \collect(); + + foreach ($provinces as $province) { + $this->assertTrue($province->villages->every(fn ($vil) => $vil instanceof Village)); + + $villages->push(...$province->villages); + } + + return $villages; } } diff --git a/tests/Models/RegencyTest.php b/tests/Models/RegencyTest.php index 35bea82..70c007a 100644 --- a/tests/Models/RegencyTest.php +++ b/tests/Models/RegencyTest.php @@ -4,10 +4,11 @@ namespace Creasi\Tests\Models; -use Creasi\Nusa\Models\District; -use Creasi\Nusa\Models\Province; +use Creasi\Nusa\Contracts\District; +use Creasi\Nusa\Contracts\Province; +use Creasi\Nusa\Contracts\Regency as RegencyContract; use Creasi\Nusa\Models\Regency; -use Creasi\Nusa\Models\Village; +use Creasi\Nusa\Contracts\Village; use Creasi\Tests\TestCase; use Illuminate\Support\Collection; use PHPUnit\Framework\Attributes\Depends; @@ -19,47 +20,59 @@ #[Group('regencies')] class RegencyTest extends TestCase { + /** + * @param Collection $regencies + * @return Collection + */ #[Test] - #[DependsExternal(ProvinceTest::class, 'it_should_be_true')] - public function it_should_be_true() + #[DependsExternal(ProvinceTest::class, 'it_should_has_many_regencies')] + public function it_should_has_many_regencies(Collection $regencies) { - $this->assertTrue(\class_exists(Regency::class)); + $regencies->each(function (Regency $regency) { + $this->assertIsInt($regency->code, 'Code should be int'); + $this->assertIsFloat($regency->latitude, 'Latitude should be float'); + $this->assertIsFloat($regency->longitude, 'Longitude should be float'); + $this->assertIsArray($regency->coordinates, 'Coordinates should be array'); - return Regency::with([ - 'province', - 'districts' => function ($query) { - $query->take(10); - }, - 'villages' => function ($query) { - $query->take(10); - }, - ])->get(); + $this->assertInstanceOf(RegencyContract::class, $regency); + }); + + return $regencies; } + /** + * @param Collection $regencies + */ #[Test] - #[Depends('it_should_be_true')] + #[Depends('it_should_has_many_regencies')] public function it_should_belongs_to_province(Collection $regencies) { - $regencies->each(function (Regency $reg) { - $this->assertInstanceOf(Province::class, $reg->province); + $regencies->each(function (Regency $regency) { + $this->assertInstanceOf(Province::class, $regency->province); }); } + /** + * @param Collection $regencies + */ #[Test] - #[Depends('it_should_be_true')] + #[Depends('it_should_has_many_regencies')] public function it_should_has_many_districts(Collection $regencies) { - $regencies->each(function (Regency $reg) { - $this->assertTrue($reg->districts->every(fn ($dis) => $dis instanceof District)); + $regencies->each(function (Regency $regency) { + $this->assertTrue($regency->districts->every(fn ($dis) => $dis instanceof District)); }); } + /** + * @param Collection $regencies + */ #[Test] - #[Depends('it_should_be_true')] + #[Depends('it_should_has_many_regencies')] public function it_should_has_many_villages(Collection $regencies) { - $regencies->each(function (Regency $reg) { - $this->assertTrue($reg->villages->every(fn ($vil) => $vil instanceof Village)); + $regencies->each(function (Regency $regency) { + $this->assertTrue($regency->villages->every(fn ($vil) => $vil instanceof Village)); }); } } diff --git a/tests/Models/VillageTest.php b/tests/Models/VillageTest.php index 6090f35..b51579e 100644 --- a/tests/Models/VillageTest.php +++ b/tests/Models/VillageTest.php @@ -4,9 +4,10 @@ namespace Creasi\Tests\Models; -use Creasi\Nusa\Models\District; -use Creasi\Nusa\Models\Province; -use Creasi\Nusa\Models\Regency; +use Creasi\Nusa\Contracts\District; +use Creasi\Nusa\Contracts\Province; +use Creasi\Nusa\Contracts\Regency; +use Creasi\Nusa\Contracts\Village as VillageContract; use Creasi\Nusa\Models\Village; use Creasi\Tests\TestCase; use Illuminate\Support\Collection; @@ -19,39 +20,57 @@ #[Group('villages')] class VillageTest extends TestCase { + /** + * @param Collection $villages + * @return Collection + */ #[Test] - #[DependsExternal(DistrictTest::class, 'it_should_be_true')] - public function it_should_be_true() + #[DependsExternal(ProvinceTest::class, 'it_should_has_many_villages')] + public function it_should_has_many_villages(Collection $villages) { - $this->assertTrue(\class_exists(Village::class)); + $villages->each(function (Village $village) { + $this->assertIsInt($village->code, 'Code should be int'); + $this->assertIsInt($village->postal_code, 'Postal Code should be int'); - return Village::with('province', 'regency', 'district')->take(10)->get(); + $this->assertInstanceOf(VillageContract::class, $village); + }); + + return $villages; } + /** + * @param Collection $villages + */ #[Test] - #[Depends('it_should_be_true')] + #[Depends('it_should_has_many_villages')] public function it_should_belongs_to_province(Collection $villages) { - $villages->each(function (Village $vil) { - $this->assertInstanceOf(Province::class, $vil->province); + $villages->each(function (Village $village) { + $this->assertInstanceOf(Province::class, $village->province); }); } + /** + * @param Collection $villages + */ #[Test] - #[Depends('it_should_be_true')] + #[Depends('it_should_has_many_villages')] public function it_should_belongs_to_regency(Collection $villages) { - $villages->each(function (Village $vil) { - $this->assertInstanceOf(Regency::class, $vil->regency); + $villages->each(function (Village $village) { + $this->assertInstanceOf(Regency::class, $village->regency); }); } + /** + * @param Collection $villages + */ #[Test] - #[Depends('it_should_be_true')] + #[Depends('it_should_has_many_villages')] public function it_should_belongs_to_district(Collection $villages) { - $villages->each(function (Village $vil) { - $this->assertInstanceOf(District::class, $vil->district); + $villages->each(function (Village $village) { + $this->assertInstanceOf(District::class, $village->district); }); } } diff --git a/tests/NusaTest.php b/tests/NusaTest.php index e0b6fc9..b77870f 100644 --- a/tests/NusaTest.php +++ b/tests/NusaTest.php @@ -4,16 +4,38 @@ namespace Creasi\Tests; +use Creasi\Nusa\Models\Address; +use Creasi\Nusa\Models\Village; +use Creasi\Tests\Models\ProvinceTest; +use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Support\Collection; +use PHPUnit\Framework\Attributes\DependsExternal; use PHPUnit\Framework\Attributes\Test; class NusaTest extends TestCase { + use WithFaker; + + /** + * @param Collection $villages + * @return Collection + */ #[Test] - public function it_should_be_true() + #[DependsExternal(ProvinceTest::class, 'it_should_has_many_villages')] + public function it_may_accociate_with_address(Collection $villages) { - if (! env('GIT_BRANCH')) { - $this->artisan('nusa:sync'); - } + $village = $villages->random()->first(); + + $address = new Address([ + 'line' => $this->faker->streetAddress(), + ]); + + $address->save(); + + $address->province()->associate($village->province); + $address->regency()->associate($village->regency); + $address->district()->associate($village->district); + $address->village()->associate($village); $this->assertTrue(true); }