# ER Diagram Database - SPK Kontrakan & Laundry ## 1. Diagram Entity Relationship (Mermaid) - Core Tables ```mermaid erDiagram USERS ||--o{ BOOKINGS : "1:M" USERS ||--o{ REVIEWS : "1:M" USERS ||--o{ FAVORITES : "1:M" KONTRAKANS ||--o{ BOOKINGS : "1:M" KONTRAKANS ||--o{ REVIEWS : "1:M" KONTRAKANS ||--o{ FAVORITES : "1:M" KONTRAKANS ||--o{ GALERI : "1:M" LAUNDRY ||--o{ LAYANAN_LAUNDRY : "1:M" LAUNDRY ||--o{ REVIEWS : "1:M" LAUNDRY ||--o{ FAVORITES : "1:M" LAUNDRY ||--o{ GALERI : "1:M" BOOKINGS ||--|| USERS : "M:1" BOOKINGS ||--|| KONTRAKANS : "M:1" USERS : PK id USERS : string name USERS : string email USERS : enum role KONTRAKANS : PK id KONTRAKANS : string nama KONTRAKANS : string alamat KONTRAKANS : int harga KONTRAKANS : decimal jarak KONTRAKANS : enum status BOOKINGS : PK id BOOKINGS : FK kontrakan_id BOOKINGS : FK user_id BOOKINGS : date start_date BOOKINGS : date end_date BOOKINGS : enum status LAUNDRY : PK id LAUNDRY : string nama LAUNDRY : string alamat LAUNDRY : int harga LAYANAN_LAUNDRY : PK id LAYANAN_LAUNDRY : FK laundry_id LAYANAN_LAUNDRY : string nama_paket REVIEWS : PK id REVIEWS : FK user_id REVIEWS : string type REVIEWS : int item_id REVIEWS : int rating FAVORITES : PK id FAVORITES : FK user_id FAVORITES : string type FAVORITES : int item_id GALERI : PK id GALERI : string type GALERI : int item_id GALERI : string foto ``` --- ## 2. Daftar Tabel & Penjelasan ### **USERS** - Tabel Pengguna | Field | Tipe | Keterangan | |-------|------|-----------| | id | bigint (PK) | ID pengguna unik | | name | varchar(255) | Nama lengkap | | email | varchar(255) | Email (unique) | | password | varchar(255) | Password terenkripsi | | phone | varchar(20) | Nomor telepon | | role | enum | super_admin, admin, user | | email_verified_at | timestamp | Verifikasi email | | deleted_at | timestamp | Soft delete | | created_at, updated_at | timestamp | Audit trail | **Relasi:** - `1:M` → BOOKINGS (seorang user bisa booking multiple kontrakan) - `1:M` → REVIEWS (seorang user bisa review multiple item) - `1:M` → FAVORITES (seorang user bisa favorite multiple item) - `1:M` → ACTIVITY_LOGS (log semua aksi user) - `1:M` → PERSONAL_ACCESS_TOKENS (API auth tokens) --- ### **KONTRAKANS** - Tabel Kontrakan | Field | Tipe | Keterangan | |-------|------|-----------| | id | bigint (PK) | ID kontrakan unik | | nama | varchar(255) | Nama/jenis kontrakan | | alamat | text | Alamat lengkap | | no_whatsapp | varchar(20) | WhatsApp owner | | latitude, longitude | decimal(10,8) | Koordinat GPS | | harga | int | Harga per bulan (Rp) | | jarak | decimal(5,2) | Jarak dari kampus (km) | | fasilitas | text | Daftar fasilitas | | jumlah_kamar | int | Jumlah kamar | | bathroom_count | int | Jumlah kamar mandi | | foto | varchar(255) | Foto primary | | status | enum | available, booked, occupied | | occupied_until | date | Tanggal kontrakan berakhir | | created_at, updated_at | timestamp | Audit trail | **Relasi:** - `1:M` → BOOKINGS (1 kontrakan bisa di-booking multiple times) - `1:M` → REVIEWS (banyak review untuk 1 kontrakan) - `1:M` → FAVORITES (banyak user bisa favorite 1 kontrakan) - `1:M` → GALERI (banyak foto untuk 1 kontrakan) - `1:M` → KRITERIA (difilter berdasarkan kriteria) **Status Flow:** ``` available → booked (ada booking confirmed/pending) → occupied (ada booking checked_in) → available (booking dibatalkan/selesai) ``` --- ### **BOOKINGS** - Tabel Pemesanan (Core Business Logic) | Field | Tipe | Keterangan | |-------|------|-----------| | id | bigint (PK) | ID booking unik | | kontrakan_id | bigint (FK) | Referensi kontrakan | | user_id | bigint (FK) | Referensi penyewa | | start_date | date | Tanggal check-in | | end_date | date | Tanggal check-out | | status | enum | pending, confirmed, checked_in, completed, cancelled | | amount | decimal(12,2) | Total harga booking (Rp) | | payment_status | enum | unpaid, paid, refunded | | payment_method | varchar(50) | transfer, cod, dll | | payment_proof | varchar(255) | Bukti transfer (file path) | | paid_at | timestamp | Kapan pembayaran | | notes | text | Catatan khusus | | booking_source | varchar(50) | api, web, mobile | | tenant_name | varchar(255) | Nama penyewa (dari API) | | tenant_phone | varchar(20) | Telepon penyewa | | confirmed_at, checked_in_at, checked_out_at | timestamp | Audit timestamps | | cancelled_at | timestamp | Kapan dibatalkan | | cancellation_reason | text | Alasan pembatalan | | created_at, updated_at | timestamp | Audit trail | **Relasi:** - `M:1` → USERS (banyak booking dari 1 user) - `M:1` → KONTRAKANS (banyak booking untuk 1 kontrakan) **Auto-Sync Status Kontrakan:** ``` Saat booking dihapus/dibatalkan: ↓ Cek booking lain untuk kontrakan yang sama ↓ - Ada booking checked_in? → status = "occupied" - Ada booking confirmed/pending? → status = "booked" - Tidak ada? → status = "available" + clear occupied_until ``` --- ### **REVIEWS** (Polymorphic) - Tabel Review/Rating | Field | Tipe | Keterangan | |-------|------|-----------| | id | bigint (PK) | ID review unik | | type | varchar(50) | "kontrakan" atau "laundry" | | item_id | int | ID kontrakan atau laundry | | user_id | bigint (FK) | Siapa yang review | | rating | int | 1-5 stars | | review | text | Isi review | | created_at, updated_at | timestamp | Audit trail | **Relasi:** - `M:1` → USERS (banyak review dari 1 user) - `M:1→` KONTRAKANS (via polymorphic: type='kontrakan' + item_id) - `M:1→` LAUNDRY (via polymorphic: type='laundry' + item_id) **Contoh Data:** ``` | id | type | item_id | user_id | rating | review | | 1 | kontrakan | 5 | 3 | 5 | Bagus banget! | | 2 | laundry | 2 | 4 | 4 | Cepat tapi mahal | ``` --- ### **FAVORITES** (Polymorphic) - Tabel Favorit | Field | Tipe | Keterangan | |-------|------|-----------| | id | bigint (PK) | ID favorite unik | | type | varchar(50) | "kontrakan" atau "laundry" | | item_id | int | ID kontrakan atau laundry | | user_id | bigint (FK) | User yang favorite | | created_at, updated_at | timestamp | Audit trail | **Relasi:** - `M:1` → USERS (banyak favorite dari 1 user) - `M:1→` KONTRAKANS (via polymorphic) - `M:1→` LAUNDRY (via polymorphic) **Constraint:** UNIQUE(user_id, type, item_id) - 1 user tidak bisa favorite item yang sama 2x --- ### **GALERI** (Polymorphic) - Tabel Foto/Gallery | Field | Tipe | Keterangan | |-------|------|-----------| | id | bigint (PK) | ID foto unik | | type | varchar(50) | "kontrakan" atau "laundry" | | item_id | int | ID kontrakan atau laundry | | foto | varchar(255) | File path foto | | urutan | int | Urutan display (1, 2, 3, ...) | | is_primary | boolean | Apakah foto utama | | caption | text | Deskripsi foto | | created_at, updated_at | timestamp | Audit trail | **Relasi:** - `M:1→` KONTRAKANS (via polymorphic) - `M:1→` LAUNDRY (via polymorphic) --- ### **LAUNDRY** - Tabel Laundry | Field | Tipe | Keterangan | |-------|------|-----------| | id | bigint (PK) | ID laundry unik | | nama | varchar(255) | Nama laundry | | alamat | text | Alamat | | no_whatsapp | varchar(20) | WhatsApp laundry | | latitude, longitude | decimal(10,8) | Koordinat GPS | | jarak | decimal(5,2) | Jarak dari kampus (km) | | fasilitas | text | Daftar fasilitas | | jam_buka | time | Jam buka (HH:mm) | | jam_tutup | time | Jam tutup (HH:mm) | | status | enum | active, inactive | | foto | varchar(255) | Foto primary | | created_at, updated_at | timestamp | Audit trail | **Relasi:** - `1:M` → LAYANAN_LAUNDRY (1 laundry punya banyak layanan) - `1:M` → REVIEWS (banyak review) - `1:M` → FAVORITES (banyak user favorite) - `1:M` → GALERI (banyak foto) --- ### **LAYANAN_LAUNDRY** - Tabel Jenis/Paket Layanan | Field | Tipe | Keterangan | |-------|------|-----------| | id | bigint (PK) | ID layanan unik | | laundry_id | bigint (FK) | Laundry mana | | jenis_layanan | enum | regular, express, kilat | | nama_paket | varchar(255) | Nama paket (e.g., "Reguler 5kg") | | harga | decimal(10,2) | Harga layanan (Rp) | | estimasi_selesai | varchar(50) | Estimasi waktu ("2 hari", "24 jam") | | deskripsi | text | Detail layanan | | status | enum | active, inactive | | rating | decimal(3,2) | Rating layanan | | waktu_proses | int | Waktu dalam jam | | created_at, updated_at | timestamp | Audit trail | **Relasi:** - `M:1` → LAUNDRY (banyak layanan dari 1 laundry) --- ### **KRITERIA** - Tabel Filter/SPK Kriteria | Field | Tipe | Keterangan | |-------|------|-----------| | id | bigint (PK) | ID kriteria unik | | tipe_bisnis | enum | "kontrakan", "laundry" | | nama_kriteria | varchar(255) | Nama kriteria filter | | bobot | int | Bobot/priority (untuk SPK) | | tipe | enum | numeric, categorical, boolean | | keterangan | text | Penjelasan kriteria | | created_at, updated_at | timestamp | Audit trail | **Contoh untuk Kontrakan:** ``` | id | tipe_bisnis | nama_kriteria | bobot | tipe | keterangan | | 1 | kontrakan | Harga | 30 | numeric | Harga sewa per bulan | | 2 | kontrakan | Jarak Kampus | 25 | numeric | Jarak dalam KM | | 3 | kontrakan | Jumlah Kamar | 20 | numeric | Banyak kamar | | 4 | kontrakan | Fasilitas | 25 | categorical | Kelengkapan fasilitas | ``` --- ### **ACTIVITY_LOGS** - Tabel Audit Log | Field | Tipe | Keterangan | |-------|------|-----------| | id | bigint (PK) | ID log unik | | user_id | bigint (FK) | User yang melakukan aksi | | action | varchar(50) | create, update, delete, login, dll | | description | text | Deskripsi aksi | | model_type | varchar(50) | Model apa yang di-akses (User, Booking, dll) | | model_id | int | ID model yang di-akses | | old_values | json | Nilai lama (untuk update/delete) | | new_values | json | Nilai baru (untuk create/update) | | ip_address | varchar(45) | IP address user | | user_agent | text | Browser info | | created_at | timestamp | Kapan terjadi | **Relasi:** - `M:1` → USERS (audit log user) - `Polymorphic` → Berbagai models (via model_type + model_id) **Contoh Data:** ``` { "action": "update", "description": "Update harga kontrakan", "model_type": "Kontrakan", "model_id": 5, "old_values": {"harga": 2000000}, "new_values": {"harga": 2500000} } ``` --- ### **PERSONAL_ACCESS_TOKENS** - Tabel API Tokens | Field | Tipe | Keterangan | |-------|------|-----------| | id | bigint (PK) | ID token unik | | user_id | bigint (FK) | User pemilik token | | name | varchar(255) | Nama token (e.g., "Mobile App") | | token | varchar(80) | Hash token | | abilities | json | Izin apa saja (e.g., ["*"]) | | last_used_at | timestamp | Terakhir dipakai | | expires_at | timestamp | Kapan expired | | created_at, updated_at | timestamp | Audit trail | **Relasi:** - `M:1` → USERS (banyak token per user) **Gunanya:** - Mobile app authentication - Third-party API access - Stateless authentication dengan Laravel Sanctum --- ## 3. Summary Relasi | Relasi | From | To | Tipe | Keterangan | |--------|------|-----|------|-----------| | User → Booking | 1 | M | Parent | Seorang user bisa booking multiple kontrakan | | Kontrakan → Booking | 1 | M | Parent | 1 kontrakan bisa di-booking multiple times | | User → Review | 1 | M | Parent | Seorang user bisa review multiple item | | Review → Kontrakan/Laundry | M | 1 | Polymorphic | Review bisa untuk kontrakan atau laundry | | User → Favorite | 1 | M | Parent | Seorang user bisa favorite multiple item | | Favorite → Kontrakan/Laundry | M | 1 | Polymorphic | Favorite bisa untuk kontrakan atau laundry | | Kontrakan → Galeri | 1 | M | Parent | 1 kontrakan punya banyak foto | | Laundry → Galeri | 1 | M | Parent | 1 laundry punya banyak foto | | Galeri → Kontrakan/Laundry | M | 1 | Polymorphic | Foto bisa untuk kontrakan atau laundry | | Laundry → Layanan | 1 | M | Parent | 1 laundry punya banyak jenis layanan | | User → ActivityLog | 1 | M | Parent | Setiap aksi user dicatat | | ActivityLog → Models | M | 1 | Polymorphic | Log bisa untuk berbagai model | | User → PersonalAccessToken | 1 | M | Parent | User bisa punya multiple API tokens | --- ## 4. Key Features ### **Auto-Sync Booking → Kontrakan Status** ```php // Saat booking dihapus/dibatalkan: $booking = Booking::find(1); $kontrakanId = $booking->kontrakan_id; $booking->delete(); // Trigger: Booking::syncKontrakanStatus($kontrakanId) // Otomatis mengupdate status kontrakan berdasarkan booking aktif ``` ### **Polymorphic Relationships** ```php // Review bisa untuk kontrakan atau laundry: $review = Review::create([ 'type' => 'kontrakan', // atau 'laundry' 'item_id' => 5, 'user_id' => 1, 'rating' => 5, 'review' => 'Bagus!' ]); // Query dengan polymorphic: $reviews = Review::forItem('kontrakan', 5)->get(); ``` ### **GPS Distance Calculation** ```php // Hitung jarak dari koordinat user menggunakan Haversine formula $kontrakan->calculateDistance($userLat, $userLng); // Return: 2.35 (km) ``` ### **Activity Logging** ```php // Semua aksi user terekam di database untuk audit trail ActivityLog::log( action: 'update', description: 'Update booking status', modelType: 'Booking', modelId: 1, oldValues: ['status' => 'pending'], newValues: ['status' => 'confirmed'] ); ``` --- ## 5. Constraints & Rules ### **Primary Keys** - Semua tabel memiliki `id` bigint unsigned sebagai PK - Auto-increment ### **Foreign Keys** - BOOKINGS.kontrakan_id → KONTRAKANS.id - BOOKINGS.user_id → USERS.id - REVIEWS.user_id → USERS.id - FAVORITES.user_id → USERS.id - LAYANAN_LAUNDRY.laundry_id → LAUNDRY.id - ACTIVITY_LOGS.user_id → USERS.id (nullable) - PERSONAL_ACCESS_TOKENS.user_id → USERS.id ### **Unique Constraints** - USERS.email - unique - FAVORITES(user_id, type, item_id) - unique (1 user tidak bisa favorite item sama 2x) ### **Enums** - USERS.role: super_admin, admin, user - BOOKINGS.status: pending, confirmed, checked_in, completed, cancelled - BOOKINGS.payment_status: unpaid, paid, refunded - KONTRAKANS.status: available, booked, occupied - REVIEWS.type, FAVORITES.type, GALERI.type: kontrakan, laundry - LAUNDRY.status: active, inactive - KRITERIA.tipe_bisnis: kontrakan, laundry - LAYANAN_LAUNDRY.jenis_layanan: regular, express, kilat ### **Soft Deletes** - USERS - menggunakan soft delete (deleted_at) ### **Timestamps** - created_at, updated_at otomatis di semua tabel --- ## 6. Normalisasi Database ✅ **Fully Normalized to 3NF:** - Tidak ada redundansi data - Setiap atribut non-key bergantung pada key - Tidak ada transitive dependency ✅ **Efficient Indexing:** - Foreign keys sudah di-index - search columns (nama, alamat) direkomendasikan di-index --- Dokumentasi ini dapat digunakan langsung untuk laporan sidang! 📊