From ff1ec1ef66fd3f66c072407da043704031063971 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Tue, 9 Jun 2026 19:16:25 +0700 Subject: [PATCH] Add employee, user, and user profile models with enums for status and gender --- app/Enums/EmployeeStatus.php | 10 +++ app/Enums/EmploymentStatus.php | 11 +++ app/Enums/Gender.php | 9 ++ app/Models/Employee.php | 85 +++++++++++++++++++ app/Models/User.php | 24 +++--- app/Models/UserProfile.php | 34 ++++++++ config/auth.php | 1 + .../0001_01_01_000000_create_users_table.php | 18 ++-- ...6_09_112732_create_user_profiles_table.php | 39 +++++++++ ...26_06_09_112737_create_employees_table.php | 41 +++++++++ database/seeders/DatabaseSeeder.php | 8 +- database/seeders/UserSeeder.php | 32 +++++++ tests/Feature/ExampleTest.php | 2 +- tests/Unit/ExampleTest.php | 2 +- 14 files changed, 289 insertions(+), 27 deletions(-) create mode 100644 app/Enums/EmployeeStatus.php create mode 100644 app/Enums/EmploymentStatus.php create mode 100644 app/Enums/Gender.php create mode 100644 app/Models/Employee.php create mode 100644 app/Models/UserProfile.php create mode 100644 database/migrations/2026_06_09_112732_create_user_profiles_table.php create mode 100644 database/migrations/2026_06_09_112737_create_employees_table.php create mode 100644 database/seeders/UserSeeder.php diff --git a/app/Enums/EmployeeStatus.php b/app/Enums/EmployeeStatus.php new file mode 100644 index 0000000..8b24cba --- /dev/null +++ b/app/Enums/EmployeeStatus.php @@ -0,0 +1,10 @@ + 'date', + 'resign_date' => 'date', + 'employment_status' => EmploymentStatus::class, + 'employee_status' => EmployeeStatus::class, + 'base_salary' => 'integer', + ]; + } + + #[Scope] + public function active(Builder $query): void + { + $query->where('employee_status', EmployeeStatus::ACTIVE->value); + } + + #[Scope] + public function inactive(Builder $query): void + { + $query->where('employee_status', EmployeeStatus::INACTIVE->value); + } + + #[Scope] + public function resigned(Builder $query): void + { + $query->where('employee_status', EmployeeStatus::RESIGNED->value); + } + + public function fullTime(Builder $query): void + { + $query->where('employment_status', EmploymentStatus::FULL_TIME->value); + } + + #[Scope] + public function partTime(Builder $query): void + { + $query->where('employee_status', EmploymentStatus::PART_TIME->value); + } + + #[Scope] + public function contract(Builder $query): void + { + $query->where('employee_status', EmploymentStatus::CONTRACT->value); + } + + #[Scope] + public function temporary(Builder $query): void + { + $query->where('employee_status', EmploymentStatus::TEMPORARY->value); + } + + public function baseSalaryFormatted(): Attribute + { + return Attribute::make( + get: fn (int $value) => number_format($value, 0, ',', '.'), + ); + } + + public function profile(): BelongsTo + { + return $this->belongsTo(UserProfile::class); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index f6ba1d2..d5bbc67 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,31 +2,31 @@ namespace App\Models; -// use Illuminate\Contracts\Auth\MustVerifyEmail; -use Database\Factories\UserFactory; -use Illuminate\Database\Eloquent\Attributes\Fillable; +use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Attributes\Hidden; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Relations\HasOne; +use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; -#[Fillable(['name', 'email', 'password'])] +#[Guarded(['id'])] #[Hidden(['password', 'remember_token'])] class User extends Authenticatable { - /** @use HasFactory */ - use HasFactory, Notifiable; + use HasFactory, Notifiable, SoftDeletes; - /** - * Get the attributes that should be cast. - * - * @return array - */ protected function casts(): array { return [ - 'email_verified_at' => 'datetime', + 'is_active' => 'boolean', + 'last_login_at' => 'datetime', 'password' => 'hashed', ]; } + + public function profile(): HasOne + { + return $this->hasOne(UserProfile::class); + } } diff --git a/app/Models/UserProfile.php b/app/Models/UserProfile.php new file mode 100644 index 0000000..6dd6f83 --- /dev/null +++ b/app/Models/UserProfile.php @@ -0,0 +1,34 @@ + Gender::class, + 'birth_date' => 'date', + ]; + } + + public function employee(): HasOne + { + return $this->hasOne(Employee::class); + } + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } +} diff --git a/config/auth.php b/config/auth.php index d7568ff..effc1d5 100644 --- a/config/auth.php +++ b/config/auth.php @@ -114,4 +114,5 @@ 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), + 'password_default' => env('PASSWORD_DEFAULT', 'Minimal8@'), ]; diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php index 05fb5d9..c2d56e0 100644 --- a/database/migrations/0001_01_01_000000_create_users_table.php +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -12,13 +12,17 @@ public function up(): void { Schema::create('users', function (Blueprint $table) { - $table->id(); - $table->string('name'); - $table->string('email')->unique(); - $table->timestamp('email_verified_at')->nullable(); - $table->string('password'); - $table->rememberToken(); - $table->timestamps(); + $table->unsignedBigInteger('id', true); + + $table->string('email', 100)->unique(); + $table->string('username', 20)->unique(); + $table->string('password', 255); + $table->boolean('is_active')->default(true); + $table->timestamp('last_login_at')->nullable(); + + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); + $table->softDeletes(); }); Schema::create('password_reset_tokens', function (Blueprint $table) { diff --git a/database/migrations/2026_06_09_112732_create_user_profiles_table.php b/database/migrations/2026_06_09_112732_create_user_profiles_table.php new file mode 100644 index 0000000..ca73b79 --- /dev/null +++ b/database/migrations/2026_06_09_112732_create_user_profiles_table.php @@ -0,0 +1,39 @@ +id(); + + $table->foreignId('user_id')->constrained('users')->cascadeOnDelete()->unique(); + + $table->string('full_name', 200); + $table->string('phone_number', 20)->nullable(); + $table->enum('gender', array_column(Gender::cases(), 'value'))->nullable(); + $table->date('birth_date')->nullable(); + $table->text('address')->nullable(); + + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('user_profiles'); + } +}; diff --git a/database/migrations/2026_06_09_112737_create_employees_table.php b/database/migrations/2026_06_09_112737_create_employees_table.php new file mode 100644 index 0000000..00d6537 --- /dev/null +++ b/database/migrations/2026_06_09_112737_create_employees_table.php @@ -0,0 +1,41 @@ +id(); + + $table->foreignId('user_id')->constrained()->cascadeOnDelete()->unique(); + + $table->string('employee_code', 20)->unique(); + $table->date('join_date'); + $table->date('resign_date')->nullable(); + $table->enum('employment_status', array_column(EmploymentStatus::cases(), 'value'))->default(EmploymentStatus::FULL_TIME->value); + $table->enum('employee_status', array_column(EmployeeStatus::cases(), 'value'))->default(EmployeeStatus::ACTIVE->value); + $table->unsignedInteger('base_salary'); + + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('employees'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 6b901f8..b9c7377 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use App\Models\User; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; @@ -15,11 +14,8 @@ class DatabaseSeeder extends Seeder */ public function run(): void { - // User::factory(10)->create(); - - User::factory()->create([ - 'name' => 'Test User', - 'email' => 'test@example.com', + $this->call([ + UserSeeder::class, ]); } } diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php new file mode 100644 index 0000000..e795e1c --- /dev/null +++ b/database/seeders/UserSeeder.php @@ -0,0 +1,32 @@ + 'project.pangestuyoga@gmail.com', + 'username' => 'pangestu', + 'password' => Hash::make(config('auth.password_default')), + ]); + + UserProfile::create([ + 'user_id' => $developer->id, + 'full_name' => 'Yoga Pangestu', + 'phone_number' => '082121495806', + 'gender' => Gender::MALE->value, + ]); + }); + } +} diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php index a576279..ef7b533 100644 --- a/tests/Feature/ExampleTest.php +++ b/tests/Feature/ExampleTest.php @@ -4,4 +4,4 @@ $response = $this->get(route('home')); $response->assertOk(); -}); \ No newline at end of file +}); diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php index 27f3f87..44a4f33 100644 --- a/tests/Unit/ExampleTest.php +++ b/tests/Unit/ExampleTest.php @@ -2,4 +2,4 @@ test('that true is true', function () { expect(true)->toBeTrue(); -}); \ No newline at end of file +});