diff --git a/app/Models/Guru.php b/app/Models/Guru.php
index 046252b..e191e66 100644
--- a/app/Models/Guru.php
+++ b/app/Models/Guru.php
@@ -10,9 +10,9 @@ class Guru extends Authenticatable
use HasFactory;
protected $table = 'gurus';
- protected $primaryKey = 'nip';
- public $incrementing = false;
- protected $keyType = 'string';
+ protected $primaryKey = 'id_guru';
+ public $incrementing = true;
+ protected $keyType = 'int';
protected $fillable = [
'nip',
diff --git a/app/Models/Kelas.php b/app/Models/Kelas.php
index b310993..ec1ce22 100644
--- a/app/Models/Kelas.php
+++ b/app/Models/Kelas.php
@@ -11,8 +11,8 @@ class Kelas extends Model
protected $table = 'kelas';
protected $primaryKey = 'id_kelas';
- public $incrementing = false;
- protected $keyType = 'string';
+ public $incrementing = true;
+ protected $keyType = 'int';
protected $fillable = [
'id_kelas',
diff --git a/app/Models/Leaderboard.php b/app/Models/Leaderboard.php
index d99c558..255f155 100644
--- a/app/Models/Leaderboard.php
+++ b/app/Models/Leaderboard.php
@@ -14,7 +14,7 @@ class Leaderboard extends Model
protected $primaryKey = 'id_history';
protected $fillable = [
- 'nisn',
+ 'id_siswa',
'id_kelas',
'total_exp',
'ranking',
@@ -22,4 +22,14 @@ class Leaderboard extends Model
'tahun_ajaran',
'tanggal_snapshot',
];
+
+ public function siswa()
+ {
+ return $this->belongsTo(Siswa::class, 'id_siswa', 'id_siswa');
+ }
+
+ public function kelas()
+ {
+ return $this->belongsTo(Kelas::class, 'id_kelas', 'id_kelas');
+ }
}
\ No newline at end of file
diff --git a/app/Models/Mapel.php b/app/Models/Mapel.php
index a3b2336..db20e5f 100644
--- a/app/Models/Mapel.php
+++ b/app/Models/Mapel.php
@@ -11,8 +11,8 @@ class Mapel extends Model
protected $table = 'mapels';
protected $primaryKey = 'id_mapel';
- public $incrementing = false;
- protected $keyType = 'string';
+ public $incrementing = true;
+ protected $keyType = 'int';
protected $fillable = [
'id_mapel',
diff --git a/app/Models/Mengajar.php b/app/Models/Mengajar.php
index f643a39..2062ea9 100644
--- a/app/Models/Mengajar.php
+++ b/app/Models/Mengajar.php
@@ -13,7 +13,7 @@ class Mengajar extends Model
protected $primaryKey = 'id_mengajar';
protected $fillable = [
- 'nip',
+ 'id_guru',
'id_mapel',
'id_kelas',
];
@@ -21,7 +21,7 @@ class Mengajar extends Model
// Relasi ke Guru
public function guru()
{
- return $this->belongsTo(Guru::class, 'nip', 'nip');
+ return $this->belongsTo(Guru::class, 'id_guru', 'id_guru');
}
// Relasi ke Mapel
diff --git a/app/Models/PengumpulanTugas.php b/app/Models/PengumpulanTugas.php
index f540c40..dcb3703 100644
--- a/app/Models/PengumpulanTugas.php
+++ b/app/Models/PengumpulanTugas.php
@@ -15,10 +15,20 @@ class PengumpulanTugas extends Model
protected $fillable = [
'id_tugas',
- 'nisn',
+ 'is_siswa',
'lampiran_tugas',
'tanggal_submit',
'exp',
'status',
];
+
+ public function siswa()
+ {
+ return $this->belongsTo(Siswa::class, 'id_siswa', 'id_siswa');
+ }
+
+ public function tugas()
+ {
+ return $this->belongsTo(Tugas::class, 'id_tugas', 'id_tugas');
+ }
}
\ No newline at end of file
diff --git a/app/Models/PesertaChallenge.php b/app/Models/PesertaChallenge.php
index 0f07eea..feb764a 100644
--- a/app/Models/PesertaChallenge.php
+++ b/app/Models/PesertaChallenge.php
@@ -15,10 +15,20 @@ class PesertaChallenge extends Model
protected $fillable = [
'id_challenge',
- 'nisn',
+ 'id_siswa',
'jawaban',
'waktu_submit',
'exp',
'status',
];
+
+ public function siswa()
+ {
+ return $this->belongsTo(Siswa::class, 'id_siswa', 'id_siswa');
+ }
+
+ public function challenge()
+ {
+ return $this->belongsTo(Challenge::class, 'id_challenge', 'id_challenge');
+ }
}
\ No newline at end of file
diff --git a/app/Models/Siswa.php b/app/Models/Siswa.php
index e008dff..d413490 100644
--- a/app/Models/Siswa.php
+++ b/app/Models/Siswa.php
@@ -11,11 +11,11 @@ class Siswa extends Model
protected $table = 'siswas';
- protected $primaryKey = 'nisn';
+ protected $primaryKey = 'id_siswa';
- public $incrementing = false;
+ public $incrementing = true;
- protected $keyType = 'string';
+ protected $keyType = 'int';
protected $fillable = [
'nisn',
diff --git a/app/Models/SiswaBadge.php b/app/Models/SiswaBadge.php
index 8d8698b..a281e5f 100644
--- a/app/Models/SiswaBadge.php
+++ b/app/Models/SiswaBadge.php
@@ -14,8 +14,18 @@ class SiswaBadge extends Model
protected $primaryKey = 'id_siswa_badge';
protected $fillable = [
- 'nisn',
+ 'id_siswa',
'id_badge',
'tanggal_diberikan',
];
+
+ public function siswa()
+ {
+ return $this->belongsTo(Siswa::class, 'id_siswa', 'id_siswa');
+ }
+
+ public function badge()
+ {
+ return $this->belongsTo(Badge::class, 'id_badge', 'id_badge');
+ }
}
\ No newline at end of file
diff --git a/composer.json b/composer.json
index da9c932..38e8622 100644
--- a/composer.json
+++ b/composer.json
@@ -7,6 +7,7 @@
"license": "MIT",
"require": {
"php": "^8.2",
+ "doctrine/dbal": "^4.4",
"laravel/framework": "^12.0",
"laravel/tinker": "^2.10.1"
},
diff --git a/composer.lock b/composer.lock
index ed14c47..e8dd399 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "bdc6d92507d397adf1c269ed49ec424d",
+ "content-hash": "b431c9cde1a46957fa68826ee65622af",
"packages": [
{
"name": "brick/math",
@@ -210,6 +210,160 @@
},
"time": "2024-07-08T12:26:09+00:00"
},
+ {
+ "name": "doctrine/dbal",
+ "version": "4.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/dbal.git",
+ "reference": "3d544473fb93f5c25b483ea4f4ce99f8c4d9d44c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/dbal/zipball/3d544473fb93f5c25b483ea4f4ce99f8c4d9d44c",
+ "reference": "3d544473fb93f5c25b483ea4f4ce99f8c4d9d44c",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/deprecations": "^1.1.5",
+ "php": "^8.2",
+ "psr/cache": "^1|^2|^3",
+ "psr/log": "^1|^2|^3"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "14.0.0",
+ "fig/log-test": "^1",
+ "jetbrains/phpstorm-stubs": "2023.2",
+ "phpstan/phpstan": "2.1.30",
+ "phpstan/phpstan-phpunit": "2.0.7",
+ "phpstan/phpstan-strict-rules": "^2",
+ "phpunit/phpunit": "11.5.23",
+ "slevomat/coding-standard": "8.24.0",
+ "squizlabs/php_codesniffer": "4.0.0",
+ "symfony/cache": "^6.3.8|^7.0|^8.0",
+ "symfony/console": "^5.4|^6.3|^7.0|^8.0"
+ },
+ "suggest": {
+ "symfony/console": "For helpful console commands such as SQL execution and import of files."
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\DBAL\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ }
+ ],
+ "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.",
+ "homepage": "https://www.doctrine-project.org/projects/dbal.html",
+ "keywords": [
+ "abstraction",
+ "database",
+ "db2",
+ "dbal",
+ "mariadb",
+ "mssql",
+ "mysql",
+ "oci8",
+ "oracle",
+ "pdo",
+ "pgsql",
+ "postgresql",
+ "queryobject",
+ "sasql",
+ "sql",
+ "sqlite",
+ "sqlserver",
+ "sqlsrv"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/dbal/issues",
+ "source": "https://github.com/doctrine/dbal/tree/4.4.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-12-04T10:11:03+00:00"
+ },
+ {
+ "name": "doctrine/deprecations",
+ "version": "1.1.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/deprecations.git",
+ "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca",
+ "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<=7.5 || >=14"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^9 || ^12 || ^14",
+ "phpstan/phpstan": "1.4.10 || 2.1.30",
+ "phpstan/phpstan-phpunit": "^1.0 || ^2",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0",
+ "psr/log": "^1 || ^2 || ^3"
+ },
+ "suggest": {
+ "psr/log": "Allows logging deprecations via PSR-3 logger implementation"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Deprecations\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
+ "homepage": "https://www.doctrine-project.org/",
+ "support": {
+ "issues": "https://github.com/doctrine/deprecations/issues",
+ "source": "https://github.com/doctrine/deprecations/tree/1.1.6"
+ },
+ "time": "2026-02-07T07:09:04+00:00"
+ },
{
"name": "doctrine/inflector",
"version": "2.1.0",
@@ -2588,6 +2742,55 @@
],
"time": "2025-08-21T11:53:16+00:00"
},
+ {
+ "name": "psr/cache",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/cache.git",
+ "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
+ "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Cache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for caching libraries",
+ "keywords": [
+ "cache",
+ "psr",
+ "psr-6"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/cache/tree/3.0.0"
+ },
+ "time": "2021-02-03T23:26:27+00:00"
+ },
{
"name": "psr/clock",
"version": "1.0.0",
diff --git a/database/migrations/2026_02_18_022905_create_challenge_kelas_table.php b/database/migrations/2026_02_18_022905_create_challenge_kelas_table.php
deleted file mode 100644
index c21b261..0000000
--- a/database/migrations/2026_02_18_022905_create_challenge_kelas_table.php
+++ /dev/null
@@ -1,40 +0,0 @@
-id();
- $table->unsignedBigInteger('id_challenge');
- $table->unsignedBigInteger('id_kelas');
- $table->timestamps();
-
- $table->foreign('id_challenge')
- ->references('id_challenge')
- ->on('challenges')
- ->onDelete('cascade');
-
- $table->foreign('id_kelas')
- ->references('id_kelas')
- ->on('kelas')
- ->onDelete('cascade');
-});
-
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- Schema::dropIfExists('challenge_kelas');
- }
-};
diff --git a/database/migrations/2026_02_21_140654_step1_refactor_kelas_pk.php b/database/migrations/2026_02_21_140654_step1_refactor_kelas_pk.php
new file mode 100644
index 0000000..e2f0580
--- /dev/null
+++ b/database/migrations/2026_02_21_140654_step1_refactor_kelas_pk.php
@@ -0,0 +1,38 @@
+unsignedBigInteger('id_kelas_baru')->nullable()->after('id_kelas');
+ });
+
+ // 2. Isi dengan nomor urut
+ DB::statement('SET @n = 0');
+ DB::statement('UPDATE kelas SET id_kelas_baru = (@n := @n + 1) ORDER BY id_kelas');
+
+ // 3. Hapus PK lama (aman karena FK sudah tidak ada)
+ DB::statement('ALTER TABLE kelas DROP PRIMARY KEY');
+
+ // 4. Jadikan AUTO_INCREMENT PK
+ DB::statement('ALTER TABLE kelas MODIFY id_kelas_baru BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, ADD PRIMARY KEY (id_kelas_baru)');
+
+ // 5. Rename kolom
+ DB::statement('ALTER TABLE kelas CHANGE id_kelas id_kelas_old VARCHAR(20)');
+ DB::statement('ALTER TABLE kelas CHANGE id_kelas_baru id_kelas BIGINT UNSIGNED NOT NULL AUTO_INCREMENT');
+ }
+
+ public function down(): void
+ {
+ throw new \Exception('Rollback step ini tidak didukung. Restore dari backup.');
+ }
+};
\ No newline at end of file
diff --git a/database/migrations/2026_02_21_140707_step2_refactor_mapels_pk.php b/database/migrations/2026_02_21_140707_step2_refactor_mapels_pk.php
new file mode 100644
index 0000000..bc9be97
--- /dev/null
+++ b/database/migrations/2026_02_21_140707_step2_refactor_mapels_pk.php
@@ -0,0 +1,38 @@
+unsignedBigInteger('id_mapel_baru')->nullable()->after('id_mapel');
+ });
+
+ // 2. Isi dengan nomor urut
+ DB::statement('SET @n = 0');
+ DB::statement('UPDATE mapels SET id_mapel_baru = (@n := @n + 1) ORDER BY id_mapel');
+
+ // 3. Hapus PK lama
+ DB::statement('ALTER TABLE mapels DROP PRIMARY KEY');
+
+ // 4. Jadikan AUTO_INCREMENT PK
+ DB::statement('ALTER TABLE mapels MODIFY id_mapel_baru BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, ADD PRIMARY KEY (id_mapel_baru)');
+
+ // 5. Rename kolom
+ DB::statement('ALTER TABLE mapels CHANGE id_mapel id_mapel_old VARCHAR(20)');
+ DB::statement('ALTER TABLE mapels CHANGE id_mapel_baru id_mapel BIGINT UNSIGNED NOT NULL AUTO_INCREMENT');
+ }
+
+ public function down(): void
+ {
+ throw new \Exception('Rollback step ini tidak didukung. Restore dari backup.');
+ }
+};
\ No newline at end of file
diff --git a/database/migrations/2026_02_21_140714_step3_refactor_siswas_pk.php b/database/migrations/2026_02_21_140714_step3_refactor_siswas_pk.php
new file mode 100644
index 0000000..96e215f
--- /dev/null
+++ b/database/migrations/2026_02_21_140714_step3_refactor_siswas_pk.php
@@ -0,0 +1,54 @@
+dropForeign('leaderboards_nisn_foreign');
+ });
+ Schema::table('pengumpulan_tugas', function (Blueprint $table) {
+ $table->dropForeign('pengumpulan_tugas_nisn_foreign');
+ });
+ Schema::table('peserta_challenges', function (Blueprint $table) {
+ $table->dropForeign('peserta_challenges_nisn_foreign');
+ });
+ Schema::table('siswa_badges', function (Blueprint $table) {
+ $table->dropForeign('siswa_badges_nisn_foreign');
+ });
+
+ // Tambah kolom id_siswa
+ Schema::table('siswas', function (Blueprint $table) {
+ $table->unsignedBigInteger('id_siswa')->nullable()->first();
+ });
+
+ // Isi nomor urut
+ DB::statement('SET @n = 0');
+ DB::statement('UPDATE siswas SET id_siswa = (@n := @n + 1) ORDER BY nisn');
+
+ // Hapus PK nisn
+ DB::statement('ALTER TABLE siswas DROP PRIMARY KEY');
+
+ // Jadikan id_siswa sebagai PK AUTO_INCREMENT
+ DB::statement('ALTER TABLE siswas MODIFY id_siswa BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, ADD PRIMARY KEY (id_siswa)');
+
+ // Jadikan nisn UNIQUE
+ Schema::table('siswas', function (Blueprint $table) {
+ $table->unique('nisn', 'siswas_nisn_unique');
+ });
+ }
+
+ public function down(): void
+ {
+ throw new \Exception('Rollback tidak didukung. Restore dari backup.');
+ }
+};
\ No newline at end of file
diff --git a/database/migrations/2026_02_21_140726_step4_refactor_gurus_pk.php b/database/migrations/2026_02_21_140726_step4_refactor_gurus_pk.php
new file mode 100644
index 0000000..ba82e85
--- /dev/null
+++ b/database/migrations/2026_02_21_140726_step4_refactor_gurus_pk.php
@@ -0,0 +1,42 @@
+dropForeign('mengajars_nip_foreign');
+ });
+
+ // Tambah kolom id_guru
+ Schema::table('gurus', function (Blueprint $table) {
+ $table->unsignedBigInteger('id_guru')->nullable()->first();
+ });
+
+ // Isi nomor urut
+ DB::statement('SET @n = 0');
+ DB::statement('UPDATE gurus SET id_guru = (@n := @n + 1) ORDER BY nip');
+
+ // Hapus PK nip
+ DB::statement('ALTER TABLE gurus DROP PRIMARY KEY');
+
+ // Jadikan id_guru sebagai PK AUTO_INCREMENT
+ DB::statement('ALTER TABLE gurus MODIFY id_guru BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, ADD PRIMARY KEY (id_guru)');
+
+ // Jadikan nip UNIQUE
+ Schema::table('gurus', function (Blueprint $table) {
+ $table->unique('nip', 'gurus_nip_unique');
+ });
+ }
+
+ public function down(): void
+ {
+ throw new \Exception('Rollback tidak didukung. Restore dari backup.');
+ }
+};
\ No newline at end of file
diff --git a/database/migrations/2026_02_21_140736_step5_migrate_fk_kelas.php b/database/migrations/2026_02_21_140736_step5_migrate_fk_kelas.php
new file mode 100644
index 0000000..66bb8ae
--- /dev/null
+++ b/database/migrations/2026_02_21_140736_step5_migrate_fk_kelas.php
@@ -0,0 +1,64 @@
+unsignedBigInteger('id_kelas_baru')->nullable()->after('id_kelas');
+ });
+ DB::statement("UPDATE siswas s JOIN kelas k ON s.id_kelas = k.id_kelas_old SET s.id_kelas_baru = k.id_kelas");
+ Schema::table('siswas', function (Blueprint $table) {
+ $table->unsignedBigInteger('id_kelas_baru')->nullable(false)->change();
+ $table->foreign('id_kelas_baru', 'siswas_id_kelas_new_foreign')->references('id_kelas')->on('kelas')->onDelete('cascade');
+ });
+ Schema::table('siswas', function (Blueprint $table) {
+ $table->dropColumn('id_kelas');
+ });
+ DB::statement('ALTER TABLE siswas CHANGE id_kelas_baru id_kelas BIGINT UNSIGNED NOT NULL');
+
+ // === MENGAJARS — id_kelas varchar → bigint ===
+ Schema::table('mengajars', function (Blueprint $table) {
+ $table->unsignedBigInteger('id_kelas_baru')->nullable()->after('id_kelas');
+ });
+ DB::statement("UPDATE mengajars m JOIN kelas k ON m.id_kelas = k.id_kelas_old SET m.id_kelas_baru = k.id_kelas");
+ Schema::table('mengajars', function (Blueprint $table) {
+ $table->unsignedBigInteger('id_kelas_baru')->nullable(false)->change();
+ $table->foreign('id_kelas_baru', 'mengajars_id_kelas_new_foreign')->references('id_kelas')->on('kelas')->onDelete('cascade');
+ });
+ Schema::table('mengajars', function (Blueprint $table) {
+ $table->dropColumn('id_kelas');
+ });
+ DB::statement('ALTER TABLE mengajars CHANGE id_kelas_baru id_kelas BIGINT UNSIGNED NOT NULL');
+
+ // === LEADERBOARDS — id_kelas varchar → bigint ===
+ Schema::table('leaderboards', function (Blueprint $table) {
+ $table->unsignedBigInteger('id_kelas_baru')->nullable()->after('id_kelas');
+ });
+ DB::statement("UPDATE leaderboards l JOIN kelas k ON l.id_kelas = k.id_kelas_old SET l.id_kelas_baru = k.id_kelas");
+ Schema::table('leaderboards', function (Blueprint $table) {
+ $table->unsignedBigInteger('id_kelas_baru')->nullable(false)->change();
+ $table->foreign('id_kelas_baru', 'leaderboards_id_kelas_new_foreign')->references('id_kelas')->on('kelas')->onDelete('cascade');
+ });
+ Schema::table('leaderboards', function (Blueprint $table) {
+ $table->dropColumn('id_kelas');
+ });
+ DB::statement('ALTER TABLE leaderboards CHANGE id_kelas_baru id_kelas BIGINT UNSIGNED NOT NULL');
+
+ // === Hapus kolom id_kelas_old dari kelas ===
+ Schema::table('kelas', function (Blueprint $table) {
+ $table->dropColumn('id_kelas_old');
+ });
+ }
+
+ public function down(): void
+ {
+ throw new \Exception('Rollback tidak didukung. Restore dari backup.');
+ }
+};
\ No newline at end of file
diff --git a/database/migrations/2026_02_21_140744_step6_migrate_fk_mapel.php b/database/migrations/2026_02_21_140744_step6_migrate_fk_mapel.php
new file mode 100644
index 0000000..b840279
--- /dev/null
+++ b/database/migrations/2026_02_21_140744_step6_migrate_fk_mapel.php
@@ -0,0 +1,35 @@
+unsignedBigInteger('id_mapel_baru')->nullable()->after('id_mapel');
+ });
+ DB::statement("UPDATE mengajars m JOIN mapels mp ON m.id_mapel = mp.id_mapel_old SET m.id_mapel_baru = mp.id_mapel");
+ Schema::table('mengajars', function (Blueprint $table) {
+ $table->unsignedBigInteger('id_mapel_baru')->nullable(false)->change();
+ $table->foreign('id_mapel_baru', 'mengajars_id_mapel_new_foreign')
+ ->references('id_mapel')->on('mapels')->onDelete('cascade');
+ });
+ Schema::table('mengajars', function (Blueprint $table) {
+ $table->dropColumn('id_mapel');
+ });
+ DB::statement('ALTER TABLE mengajars CHANGE id_mapel_baru id_mapel BIGINT UNSIGNED NOT NULL');
+
+ Schema::table('mapels', function (Blueprint $table) {
+ $table->dropColumn('id_mapel_old');
+ });
+ }
+
+ public function down(): void
+ {
+ throw new \Exception('Rollback tidak didukung. Restore dari backup.');
+ }
+};
\ No newline at end of file
diff --git a/database/migrations/2026_02_21_140842_step7_migrate_fk_nisn.php b/database/migrations/2026_02_21_140842_step7_migrate_fk_nisn.php
new file mode 100644
index 0000000..80dfb44
--- /dev/null
+++ b/database/migrations/2026_02_21_140842_step7_migrate_fk_nisn.php
@@ -0,0 +1,42 @@
+unsignedBigInteger('id_siswa')->nullable()->after('nisn');
+ });
+
+ DB::statement("UPDATE {$tabel} t JOIN siswas s ON t.nisn = s.nisn SET t.id_siswa = s.id_siswa");
+
+ Schema::table($tabel, function (Blueprint $table) use ($tabel) {
+ $table->unsignedBigInteger('id_siswa')->nullable(false)->change();
+ $table->foreign('id_siswa', "{$tabel}_id_siswa_new_foreign")
+ ->references('id_siswa')->on('siswas')->onDelete('cascade');
+ });
+
+ Schema::table($tabel, function (Blueprint $table) {
+ $table->dropColumn('nisn');
+ });
+ }
+ }
+
+ public function down(): void
+ {
+ throw new \Exception('Rollback tidak didukung. Restore dari backup.');
+ }
+};
\ No newline at end of file
diff --git a/database/migrations/2026_02_21_140857_step8_migrate_fk_nip.php b/database/migrations/2026_02_21_140857_step8_migrate_fk_nip.php
new file mode 100644
index 0000000..8eb61b6
--- /dev/null
+++ b/database/migrations/2026_02_21_140857_step8_migrate_fk_nip.php
@@ -0,0 +1,33 @@
+unsignedBigInteger('id_guru')->nullable()->after('nip');
+ });
+
+ DB::statement("UPDATE mengajars m JOIN gurus g ON m.nip = g.nip SET m.id_guru = g.id_guru");
+
+ Schema::table('mengajars', function (Blueprint $table) {
+ $table->unsignedBigInteger('id_guru')->nullable(false)->change();
+ $table->foreign('id_guru', 'mengajars_id_guru_new_foreign')
+ ->references('id_guru')->on('gurus')->onDelete('cascade');
+ });
+
+ Schema::table('mengajars', function (Blueprint $table) {
+ $table->dropColumn('nip');
+ });
+ }
+
+ public function down(): void
+ {
+ throw new \Exception('Rollback tidak didukung. Restore dari backup.');
+ }
+};
\ No newline at end of file
diff --git a/database/migrations/2026_02_21_140910_step9_cleanup_old_varchar_columns.php b/database/migrations/2026_02_21_140910_step9_cleanup_old_varchar_columns.php
new file mode 100644
index 0000000..dde3be4
--- /dev/null
+++ b/database/migrations/2026_02_21_140910_step9_cleanup_old_varchar_columns.php
@@ -0,0 +1,32 @@
+index('id_kelas', 'siswas_id_kelas_index');
+ });
+ Schema::table('mengajars', function (Blueprint $table) {
+ $table->index('id_kelas', 'mengajars_id_kelas_index');
+ $table->index('id_mapel', 'mengajars_id_mapel_index');
+ $table->index('id_guru', 'mengajars_id_guru_index');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('siswas', function (Blueprint $table) {
+ $table->dropIndex('siswas_id_kelas_index');
+ });
+ Schema::table('mengajars', function (Blueprint $table) {
+ $table->dropIndex('mengajars_id_kelas_index');
+ $table->dropIndex('mengajars_id_mapel_index');
+ $table->dropIndex('mengajars_id_guru_index');
+ });
+ }
+};
\ No newline at end of file
diff --git a/database/migrations/2026_02_21_141000_create_challenge_kelas_table.php b/database/migrations/2026_02_21_141000_create_challenge_kelas_table.php
new file mode 100644
index 0000000..4b09610
--- /dev/null
+++ b/database/migrations/2026_02_21_141000_create_challenge_kelas_table.php
@@ -0,0 +1,39 @@
+id();
+ $table->unsignedBigInteger('id_challenge');
+ $table->unsignedBigInteger('id_kelas');
+ $table->timestamps();
+ $table->foreign('id_challenge')
+ ->references('id_challenge')
+ ->on('challenges')
+ ->onDelete('cascade');
+ $table->foreign('id_kelas')
+ ->references('id_kelas')
+ ->on('kelas')
+ ->onDelete('cascade');
+ });
+ }
+}
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('challenge_kelas');
+ }
+};
diff --git a/resources/views/admin/guru/index.blade.php b/resources/views/admin/guru/index.blade.php
index 771fd01..15104a0 100644
--- a/resources/views/admin/guru/index.blade.php
+++ b/resources/views/admin/guru/index.blade.php
@@ -181,7 +181,7 @@
-