Browse Source
- Implement mappers for Academic (Faculty, StudyProgram, Term) and Admission (AcademicProgram, AdmissionTrack, AdmissionSchedule, FeeType) domains. - Add registry bindings in service providers for API synchronization. - Enhance models to use HasPublicUlid and clean up ULID-related traits. - Update DTOs and actions to accommodate integration features and improve mapping flexibility. - Standardize billing cycle parsing with PMB label support.master
51 changed files with 732 additions and 222 deletions
@ -0,0 +1,64 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Domains\Academic\Integration\Mappers; |
||||
|
|
||||
|
use App\Domains\Academic\Actions\Curriculum\EstablishFaculty; |
||||
|
use App\Domains\Academic\Actions\Curriculum\ModifyFacultyDetails; |
||||
|
use App\Domains\Academic\DTOs\Curriculum\EstablishFacultyDTO; |
||||
|
use App\Domains\Academic\DTOs\Curriculum\ModifyFacultyDetailsDTO; |
||||
|
use App\Domains\Academic\Models\Faculty; |
||||
|
use App\Domains\System\Support\Integration\DataPayloadMapper; |
||||
|
use Illuminate\Database\Eloquent\Model; |
||||
|
|
||||
|
class FacultyDataMapper implements DataPayloadMapper |
||||
|
{ |
||||
|
public function __construct( |
||||
|
protected EstablishFaculty $establishFaculty, |
||||
|
protected ModifyFacultyDetails $modifyFacultyDetails, |
||||
|
) |
||||
|
{ |
||||
|
// Inject required Domain and Cross-Domain Actions via constructor composition |
||||
|
} |
||||
|
|
||||
|
public function getLookupKey(): string |
||||
|
{ |
||||
|
// Return the unique string key used to identify existing records (e.g., 'email') |
||||
|
return 'external_id'; |
||||
|
} |
||||
|
|
||||
|
public function transform(array $rawData): array |
||||
|
{ |
||||
|
// Normalize incoming data array formats into an internal domain-safe layout |
||||
|
return [ |
||||
|
'name' => $rawData['nama_fakultas'], |
||||
|
'code' => $rawData['kode_fakultas'], |
||||
|
'external_id' => $rawData['id_fakultas'], |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
public function updateOrCreateDomainState(array $payload, ?Model $existingModel = null): void |
||||
|
{ |
||||
|
/** @var Faculty $faculty */ |
||||
|
$faculty = $existingModel; |
||||
|
if ($faculty) { |
||||
|
$this->modifyFacultyDetails->execute($faculty, new ModifyFacultyDetailsDTO( |
||||
|
name: $payload['name'], |
||||
|
code: $payload['code'], |
||||
|
)); |
||||
|
} else { |
||||
|
$this->establishFaculty->execute(dto: new EstablishFacultyDTO( |
||||
|
name: $payload['name'], |
||||
|
code: $payload['code'], |
||||
|
externalId: $payload['external_id'], |
||||
|
)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return class-string |
||||
|
*/ |
||||
|
public function getModelClass(): string |
||||
|
{ |
||||
|
return Faculty::class; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,82 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Domains\Academic\Integration\Mappers; |
||||
|
|
||||
|
use App\Domains\Academic\Actions\Curriculum\AdjustStudyProgramDetails; |
||||
|
use App\Domains\Academic\Actions\Curriculum\RegisterStudyProgram; |
||||
|
use App\Domains\Academic\DTOs\Curriculum\AdjustStudyProgramDTO; |
||||
|
use App\Domains\Academic\DTOs\Curriculum\RegisterStudyProgramDTO; |
||||
|
use App\Domains\Academic\Models\Faculty; |
||||
|
use App\Domains\Academic\Models\StudyProgram; |
||||
|
use App\Domains\System\Enums\LifecycleStatus; |
||||
|
use App\Domains\System\Support\Integration\DataPayloadMapper; |
||||
|
use Illuminate\Database\Eloquent\Model; |
||||
|
|
||||
|
class StudyProgramDataMapper implements DataPayloadMapper |
||||
|
{ |
||||
|
private static array $facultyCache = []; |
||||
|
|
||||
|
public function __construct( |
||||
|
protected RegisterStudyProgram $registerStudyProgram, |
||||
|
protected AdjustStudyProgramDetails $adjustStudyProgramDetails, |
||||
|
) |
||||
|
{ |
||||
|
// Inject required Domain and Cross-Domain Actions via constructor composition |
||||
|
} |
||||
|
|
||||
|
public function getLookupKey(): string |
||||
|
{ |
||||
|
// Return the unique string key used to identify existing records (e.g., 'email') |
||||
|
return 'external_id'; |
||||
|
} |
||||
|
|
||||
|
public function transform(array $rawData): array |
||||
|
{ |
||||
|
// Normalize incoming data array formats into an internal domain-safe layout |
||||
|
return [ |
||||
|
'name' => $rawData['nama_program_studi'], |
||||
|
'code' => $rawData['kode_program_studi'], |
||||
|
'level' => $rawData['nama_jenjang_pendidikan'], |
||||
|
'faculty_id' => self::resolveFacultyId($rawData['id_fakultas']), |
||||
|
'external_id' => $rawData['id_prodi'], |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
public function updateOrCreateDomainState(array $payload, ?Model $existingModel = null): void |
||||
|
{ |
||||
|
/** @var StudyProgram $studyProgram */ |
||||
|
$studyProgram = $existingModel; |
||||
|
|
||||
|
if ($studyProgram) { |
||||
|
$this->adjustStudyProgramDetails->execute($studyProgram, new AdjustStudyProgramDTO( |
||||
|
level: $payload['level'], |
||||
|
status: LifecycleStatus::ACTIVE, |
||||
|
)); |
||||
|
} else { |
||||
|
$this->registerStudyProgram->execute(new RegisterStudyProgramDTO( |
||||
|
name: $payload['name'], |
||||
|
code: $payload['code'], |
||||
|
level: $payload['level'], |
||||
|
status: LifecycleStatus::ACTIVE, |
||||
|
facultyId: $payload['faculty_id'], |
||||
|
externalId: $payload['external_id'], |
||||
|
)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return class-string |
||||
|
*/ |
||||
|
public function getModelClass(): string |
||||
|
{ |
||||
|
return StudyProgram::class; |
||||
|
} |
||||
|
|
||||
|
private function resolveFacultyId(string $externalId): int { |
||||
|
if (!isset(self::$facultyCache[$externalId])) { |
||||
|
self::$facultyCache[$externalId] = Faculty::where('external_id', $externalId)->first()->id; |
||||
|
} |
||||
|
|
||||
|
return self::$facultyCache[$externalId]; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,72 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Domains\Academic\Integration\Mappers; |
||||
|
|
||||
|
use App\Domains\Academic\Actions\Calendar\InitializeAcademicTerm; |
||||
|
use App\Domains\Academic\Actions\Calendar\ModifyTermDate; |
||||
|
use App\Domains\Academic\DTOs\Calendar\InitializeAcademicTermDTO; |
||||
|
use App\Domains\Academic\DTOs\Calendar\ModifyTermDateDTO; |
||||
|
use App\Domains\Academic\Models\Term; |
||||
|
use App\Domains\System\Support\Integration\DataPayloadMapper; |
||||
|
use Carbon\CarbonImmutable; |
||||
|
use Illuminate\Database\Eloquent\Model; |
||||
|
|
||||
|
class TermDataMapper implements DataPayloadMapper |
||||
|
{ |
||||
|
public function __construct( |
||||
|
protected InitializeAcademicTerm $initializeAcademicTerm, |
||||
|
protected ModifyTermDate $modifyTermDate, |
||||
|
) |
||||
|
{ |
||||
|
// Inject required Domain and Cross-Domain Actions via constructor composition |
||||
|
} |
||||
|
|
||||
|
public function getLookupKey(): string |
||||
|
{ |
||||
|
// Return the unique string key used to identify existing records (e.g., 'email') |
||||
|
return 'code'; |
||||
|
} |
||||
|
|
||||
|
public function transform(array $rawData): array |
||||
|
{ |
||||
|
// Normalize incoming data array formats into an internal domain-safe layout |
||||
|
return [ |
||||
|
'name' => $rawData['nama_semester'], |
||||
|
'code' => $rawData['id_semester'], |
||||
|
'year_study' => $rawData['id_tahun_ajaran'], |
||||
|
'semester' => $rawData['semester'], |
||||
|
'date_start' => $rawData['tanggal_mulai'], |
||||
|
'date_end' => $rawData['tanggal_selesai'], |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
public function updateOrCreateDomainState(array $payload, ?Model $existingModel = null): void |
||||
|
{ |
||||
|
/** @var Term $term */ |
||||
|
$term = $existingModel; |
||||
|
|
||||
|
if(isset($term)) { |
||||
|
$this->modifyTermDate->execute($term, new ModifyTermDateDTO( |
||||
|
startDate: CarbonImmutable::parse($payload['date_start']), |
||||
|
endDate: CarbonImmutable::parse($payload['date_end']), |
||||
|
)); |
||||
|
} else { |
||||
|
$this->initializeAcademicTerm->execute(new InitializeAcademicTermDTO( |
||||
|
name: $payload['name'], |
||||
|
code: $payload['code'], |
||||
|
yearStudy: $payload['year_study'], |
||||
|
semester: $payload['semester'], |
||||
|
startDate: CarbonImmutable::parse($payload['date_start']), |
||||
|
endDate: CarbonImmutable::parse($payload['date_end']), |
||||
|
)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return class-string |
||||
|
*/ |
||||
|
public function getModelClass(): string |
||||
|
{ |
||||
|
return Term::class; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Domains\Channel\Support\Mapper; |
||||
|
|
||||
|
use App\Domains\Channel\Enums\Api\ApiType; |
||||
|
use App\Domains\System\Support\Integration\DataPayloadMapper; |
||||
|
use InvalidArgumentException; |
||||
|
|
||||
|
class ApiMapperRegistry |
||||
|
{ |
||||
|
/** |
||||
|
* @var array<string, class-string<DataPayloadMapper>> |
||||
|
*/ |
||||
|
protected static array $mappers = []; |
||||
|
|
||||
|
/** |
||||
|
* Register mapper with type-safe using enum ApiType |
||||
|
*/ |
||||
|
public static function register(ApiType $type, string $mapperClass): void |
||||
|
{ |
||||
|
static::$mappers[$type->value] = $mapperClass; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param ApiType $type |
||||
|
* @throws InvalidArgumentException |
||||
|
* @return DataPayloadMapper |
||||
|
*/ |
||||
|
public static function resolve(ApiType $type): DataPayloadMapper |
||||
|
{ |
||||
|
$mapperClass = static::$mappers[$type->value] ?? null; |
||||
|
|
||||
|
if (! $mapperClass || ! class_exists($mapperClass)) { |
||||
|
throw new InvalidArgumentException("No DataMapper registered for ApiType: {$type->value}"); |
||||
|
} |
||||
|
|
||||
|
return app($mapperClass); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Domains\System\Traits\Model; |
||||
|
|
||||
|
use Illuminate\Support\Str; |
||||
|
|
||||
|
trait HasPublicUlid |
||||
|
{ |
||||
|
public static function bootHasPublicUlid(): void |
||||
|
{ |
||||
|
static::creating(function ($model) { |
||||
|
$model->ulid = Str::ulid(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public function getRouteKeyName(): string |
||||
|
{ |
||||
|
return 'ulid'; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return [ |
||||
|
'clients' => [ |
||||
|
'synced' => 'Clients synced successfully.', |
||||
|
'fail_to_sync' => 'Clients failed to sync: :error.' |
||||
|
] |
||||
|
]; |
||||
Loading…
Reference in new issue