Bagian 1 dari seri

Persiapan Project AHP Laravel, Bootstrap, Login, dan Database

Diterbitkan 18 September 2026

Buat Project Laravel

composer create-project laravel/laravel ahp-laravel
cd ahp-laravel
php artisan key:generate

Atur .env:

APP_NAME="AHP Laravel"
APP_URL=http://localhost/ahp-laravel/public

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=ahp_laravel
DB_USERNAME=root
DB_PASSWORD=

Buat Model dan Migration

php artisan make:model Kriteria -m
php artisan make:model Alternatif -m
php artisan make:model PerbandinganKriteria -m
php artisan make:model PerbandinganAlternatif -m
php artisan make:controller AuthController
php artisan make:controller DashboardController

Migration kriteria:

Schema::create('kriteria', function (Blueprint $table) {
    $table->id();
    $table->string('kode', 10)->unique();
    $table->string('nama');
    $table->timestamps();
});

Migration alternatif:

Schema::create('alternatif', function (Blueprint $table) {
    $table->id();
    $table->string('kode', 10)->unique();
    $table->string('nama');
    $table->decimal('nilai_akhir', 12, 8)->nullable();
    $table->unsignedInteger('ranking')->nullable();
    $table->timestamps();
});

Migration perbandingan_kriteria:

Schema::create('perbandingan_kriteria', function (Blueprint $table) {
    $table->id();
    $table->foreignId('kriteria_1_id')->constrained('kriteria')->cascadeOnDelete();
    $table->foreignId('kriteria_2_id')->constrained('kriteria')->cascadeOnDelete();
    $table->decimal('nilai', 12, 6);
    $table->timestamps();

    $table->unique(['kriteria_1_id', 'kriteria_2_id']);
});

Migration perbandingan_alternatif:

Schema::create('perbandingan_alternatif', function (Blueprint $table) {
    $table->id();
    $table->foreignId('kriteria_id')->constrained('kriteria')->cascadeOnDelete();
    $table->foreignId('alternatif_1_id')->constrained('alternatif')->cascadeOnDelete();
    $table->foreignId('alternatif_2_id')->constrained('alternatif')->cascadeOnDelete();
    $table->decimal('nilai', 12, 6);
    $table->timestamps();

    $table->unique(['kriteria_id', 'alternatif_1_id', 'alternatif_2_id']);
});

Jalankan:

php artisan migrate

Model

app/Models/Kriteria.php:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Kriteria extends Model
{
    protected $table = 'kriteria';
    protected $fillable = ['kode', 'nama'];
}

app/Models/Alternatif.php:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Alternatif extends Model
{
    protected $table = 'alternatif';

    protected $fillable = [
        'kode', 'nama', 'nilai_akhir', 'ranking'
    ];
}

app/Models/PerbandinganKriteria.php:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class PerbandinganKriteria extends Model
{
    protected $table = 'perbandingan_kriteria';

    protected $fillable = [
        'kriteria_1_id', 'kriteria_2_id', 'nilai'
    ];
}

app/Models/PerbandinganAlternatif.php:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class PerbandinganAlternatif extends Model
{
    protected $table = 'perbandingan_alternatif';

    protected $fillable = [
        'kriteria_id', 'alternatif_1_id', 'alternatif_2_id', 'nilai'
    ];
}

Seeder Data Awal

Buat satu seeder untuk seluruh data awal aplikasi:

php artisan make:seeder AhpSeeder

Buka database/seeders/AhpSeeder.php:

<?php

namespace Database\Seeders;

use App\Models\Alternatif;
use App\Models\Kriteria;
use App\Models\PerbandinganAlternatif;
use App\Models\PerbandinganKriteria;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;

class AhpSeeder extends Seeder
{
    public function run(): void
    {
        User::updateOrCreate(
            ['email' => 'admin@example.com'],
            [
                'name' => 'Administrator',
                'password' => Hash::make('admin12345'),
            ]
        );

        $dataKriteria = [
            ['kode' => 'C1', 'nama' => 'Harga'],
            ['kode' => 'C2', 'nama' => 'Kualitas'],
            ['kode' => 'C3', 'nama' => 'Pelayanan'],
        ];

        foreach ($dataKriteria as $item) {
            Kriteria::updateOrCreate(
                ['kode' => $item['kode']],
                ['nama' => $item['nama']]
            );
        }

        $dataAlternatif = [
            ['kode' => 'A1', 'nama' => 'Supplier A'],
            ['kode' => 'A2', 'nama' => 'Supplier B'],
            ['kode' => 'A3', 'nama' => 'Supplier C'],
        ];

        foreach ($dataAlternatif as $item) {
            Alternatif::updateOrCreate(
                ['kode' => $item['kode']],
                [
                    'nama' => $item['nama'],
                    'nilai_akhir' => null,
                    'ranking' => null,
                ]
            );
        }

        $kriteria = Kriteria::orderBy('kode')->get();
        $alternatif = Alternatif::orderBy('kode')->get();

        // Nilai awal perbandingan kriteria = 1
        for ($i = 0; $i < $kriteria->count(); $i++) {
            for ($j = $i + 1; $j < $kriteria->count(); $j++) {
                PerbandinganKriteria::updateOrCreate(
                    [
                        'kriteria_1_id' => $kriteria[$i]->id,
                        'kriteria_2_id' => $kriteria[$j]->id,
                    ],
                    [
                        'nilai' => 1,
                    ]
                );
            }
        }

        // Nilai awal perbandingan alternatif pada setiap kriteria = 1
        foreach ($kriteria as $itemKriteria) {
            for ($i = 0; $i < $alternatif->count(); $i++) {
                for ($j = $i + 1; $j < $alternatif->count(); $j++) {
                    PerbandinganAlternatif::updateOrCreate(
                        [
                            'kriteria_id' => $itemKriteria->id,
                            'alternatif_1_id' => $alternatif[$i]->id,
                            'alternatif_2_id' => $alternatif[$j]->id,
                        ],
                        [
                            'nilai' => 1,
                        ]
                    );
                }
            }
        }
    }
}

Daftarkan pada database/seeders/DatabaseSeeder.php:

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        $this->call(AhpSeeder::class);
    }
}

Jalankan migration sekaligus seeder:

php artisan migrate:fresh --seed

Data awal yang tersedia:

User
admin@example.com
Password: admin12345

Kriteria
C1 - Harga
C2 - Kualitas
C3 - Pelayanan

Alternatif
A1 - Supplier A
A2 - Supplier B
A3 - Supplier C

Seeder juga membuat seluruh pasangan perbandingan dengan nilai awal 1.

Contoh perbandingan kriteria:

C1 vs C2 = 1
C1 vs C3 = 1
C2 vs C3 = 1

Pada setiap kriteria, pasangan alternatif juga dibuat dengan nilai awal 1.

Nilai 1 berarti kedua elemen dianggap sama penting. Nilai tersebut nantinya dapat diubah melalui form perbandingan pada aplikasi.

AuthController

app/Http/Controllers/AuthController.php:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class AuthController extends Controller
{
    public function create()
    {
        return view('auth.login');
    }

    public function store(Request $request)
    {
        $credentials = $request->validate([
            'email' => ['required', 'email'],
            'password' => ['required'],
        ]);

        if (Auth::attempt($credentials, $request->boolean('remember'))) {
            $request->session()->regenerate();
            return redirect()->intended('/dashboard');
        }

        return back()->withErrors([
            'email' => 'Email atau password salah.',
        ])->onlyInput('email');
    }

    public function destroy(Request $request)
    {
        Auth::logout();
        $request->session()->invalidate();
        $request->session()->regenerateToken();

        return redirect()->route('login');
    }
}

Route

routes/web.php:

<?php

use App\Http\Controllers\AuthController;
use App\Http\Controllers\DashboardController;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return redirect()->route('dashboard');
});

Route::middleware('guest')->group(function () {
    Route::get('/login', [AuthController::class, 'create'])->name('login');
    Route::post('/login', [AuthController::class, 'store'])
        ->middleware('throttle:5,1')
        ->name('login.store');
});

Route::middleware('auth')->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index'])
        ->name('dashboard');

    Route::post('/logout', [AuthController::class, 'destroy'])
        ->name('logout');
});

app/Http/Controllers/DashboardController.php:

<?php

namespace App\Http\Controllers;

class DashboardController extends Controller
{
    public function index()
    {
        return view('dashboard');
    }
}

Layout Bootstrap

Buat resources/views/layouts/app.blade.php:

<!doctype html>
<html lang="id">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <title>@yield('title', 'AHP Laravel')</title>

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">

<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
    <div class="container">
        <a class="navbar-brand fw-bold" href="{{ route('dashboard') }}">
            AHP Laravel
        </a>

        @auth
            <div class="d-flex align-items-center gap-3 text-white">
                <span>{{ auth()->user()->name }}</span>

                <form method="POST" action="{{ route('logout') }}">
                    @csrf
                    <button class="btn btn-outline-light btn-sm">Logout</button>
                </form>
            </div>
        @endauth
    </div>
</nav>

<main class="container py-4">
    @if(session('success'))
        <div class="alert alert-success">{{ session('success') }}</div>
    @endif

    @if($errors->any())
        <div class="alert alert-danger">
            <ul class="mb-0">
                @foreach($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    @endif

    @yield('content')
</main>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Halaman Login

resources/views/auth/login.blade.php:

<!doctype html>
<html lang="id">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Login AHP Laravel</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">

<div class="container min-vh-100 d-flex align-items-center justify-content-center">
    <div class="card border-0 shadow-sm" style="width:100%;max-width:420px">
        <div class="card-body p-4">
            <h1 class="h4 mb-4">Login AHP Laravel</h1>

            <form method="POST" action="{{ route('login.store') }}">
                @csrf

                <div class="mb-3">
                    <label class="form-label">Email</label>
                    <input type="email" name="email"
                           value="{{ old('email') }}"
                           class="form-control" required autofocus>
                </div>

                <div class="mb-3">
                    <label class="form-label">Password</label>
                    <input type="password" name="password"
                           class="form-control" required>
                </div>

                <div class="form-check mb-3">
                    <input class="form-check-input"
                           type="checkbox"
                           name="remember"
                           value="1"
                           id="remember">
                    <label class="form-check-label" for="remember">
                        Ingat saya
                    </label>
                </div>

                <button class="btn btn-primary w-100">Login</button>
            </form>
        </div>
    </div>
</div>

</body>
</html>

Dashboard

resources/views/dashboard.blade.php:

@extends('layouts.app')

@section('title', 'Dashboard')

@section('content')
<div class="card border-0 shadow-sm">
    <div class="card-body">
        <h1 class="h4">Dashboard AHP</h1>
        <p class="mb-0">
            Project Laravel dan autentikasi berhasil dibuat.
        </p>
    </div>
</div>
@endsection

Karena project dijalankan melalui XAMPP, simpan project di folder htdocs dan akses melalui:

http://localhost/ahp-laravel/public/

Halaman login:

http://localhost/ahp-laravel/public/login

Jika sebelumnya konfigurasi route atau .env sudah pernah di-cache, jalankan:

php artisan optimize:clear
Tutorial AHP Laravel Login
Tutorial AHP Laravel Dashboard

Checkpoint

Pastikan:

  • Login berhasil.
  • Pengunjung yang belum login diarahkan ke halaman login.
  • Logout berhasil.
  • Semua migration berhasil.
  • Bootstrap tampil normal.

Lanjut: CRUD Kriteria dan Alternatif