Bagian 1 dari seri

Persiapan Project TOPSIS Laravel, Bootstrap, Login, dan Database

Diterbitkan 21 September 2026

Ikuti langkah berikut secara berurutan. Setelah selesai, project harus bisa login dan menampilkan dashboard.

Buat Project Laravel

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

Konfigurasi Database

Buat database:

topsis_laravel

Atur .env:

APP_NAME="TOPSIS Laravel"
APP_URL=http://127.0.0.1:8000

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=topsis_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 Penilaian -m

php artisan make:controller AuthController
php artisan make:controller DashboardController

Migration Kriteria

Buka migration tabel kriteria:

Schema::create('kriteria', function (Blueprint $table) {
    $table->id();
    $table->string('kode', 10)->unique();
    $table->string('nama');
    $table->decimal('bobot', 12, 6);
    $table->enum('atribut', ['benefit', 'cost']);
    $table->timestamps();
});

Migration Alternatif

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

Migration Penilaian

Schema::create('penilaian', function (Blueprint $table) {
    $table->id();

    $table->foreignId('alternatif_id')
        ->constrained('alternatif')
        ->cascadeOnDelete();

    $table->foreignId('kriteria_id')
        ->constrained('kriteria')
        ->cascadeOnDelete();

    $table->decimal('nilai', 14, 6);
    $table->timestamps();

    $table->unique(
        ['alternatif_id', 'kriteria_id'],
        'penilaian_alternatif_kriteria_unique'
    );
});

Model Kriteria

app/Models/Kriteria.php:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Kriteria extends Model
{
    protected $table = 'kriteria';

    protected $fillable = [
        'kode',
        'nama',
        'bobot',
        'atribut',
    ];

    protected $casts = [
        'bobot' => 'float',
    ];

    public function penilaian()
    {
        return $this->hasMany(Penilaian::class);
    }
}

Model Alternatif

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_preferensi',
        'ranking',
    ];

    protected $casts = [
        'nilai_preferensi' => 'float',
    ];

    public function penilaian()
    {
        return $this->hasMany(Penilaian::class);
    }
}

Model Penilaian

app/Models/Penilaian.php:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Penilaian extends Model
{
    protected $table = 'penilaian';

    protected $fillable = [
        'alternatif_id',
        'kriteria_id',
        'nilai',
    ];

    protected $casts = [
        'nilai' => 'float',
    ];

    public function alternatif()
    {
        return $this->belongsTo(Alternatif::class);
    }

    public function kriteria()
    {
        return $this->belongsTo(Kriteria::class);
    }
}

Buat Seeder

php artisan make:seeder TopsisSeeder

Isi database/seeders/TopsisSeeder.php:

<?php

namespace Database\Seeders;

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

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

        $dataKriteria = [
            [
                'kode' => 'C1',
                'nama' => 'Harga',
                'bobot' => 30,
                'atribut' => 'cost',
            ],
            [
                'kode' => 'C2',
                'nama' => 'Kualitas',
                'bobot' => 40,
                'atribut' => 'benefit',
            ],
            [
                'kode' => 'C3',
                'nama' => 'Pelayanan',
                'bobot' => 30,
                'atribut' => 'benefit',
            ],
        ];

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

        $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_preferensi' => null,
                    'ranking' => null,
                ]
            );
        }

        $nilaiAwal = [
            'A1' => ['C1' => 80, 'C2' => 8, 'C3' => 9],
            'A2' => ['C1' => 70, 'C2' => 9, 'C3' => 8],
            'A3' => ['C1' => 75, 'C2' => 7, 'C3' => 9],
        ];

        $kriteria = Kriteria::all()->keyBy('kode');
        $alternatif = Alternatif::all()->keyBy('kode');

        foreach ($nilaiAwal as $kodeAlternatif => $nilaiKriteria) {
            foreach ($nilaiKriteria as $kodeKriteria => $nilai) {
                Penilaian::updateOrCreate(
                    [
                        'alternatif_id' => $alternatif[$kodeAlternatif]->id,
                        'kriteria_id' => $kriteria[$kodeKriteria]->id,
                    ],
                    [
                        'nilai' => $nilai,
                    ]
                );
            }
        }
    }
}

Daftarkan pada database/seeders/DatabaseSeeder.php:

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

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

Jalankan:

php artisan migrate:fresh --seed

Data Login

Email    : admin@example.com
Password : admin12345

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(route('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');
    }
}

DashboardController

app/Http/Controllers/DashboardController.php:

<?php

namespace App\Http\Controllers;

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

Route Dasar

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');
});

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', 'TOPSIS 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') }}">
            TOPSIS 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

Buat 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 TOPSIS 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 TOPSIS 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

Buat 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 TOPSIS</h1>

        <p class="mb-0">
            Project Laravel dan autentikasi berhasil dibuat.
        </p>
    </div>
</div>
@endsection

Menjalankan Project

Pastikan MySQL sudah aktif.

Jalankan:

php artisan serve

Buka:

http://127.0.0.1:8000

Halaman login:

http://127.0.0.1:8000/login
Tutorial Topsis Laravel Login
Tutorial Topsis Laravel Dashboard

File yang Harus Sudah Ada

app/Models/Kriteria.php
app/Models/Alternatif.php
app/Models/Penilaian.php
app/Http/Controllers/AuthController.php
app/Http/Controllers/DashboardController.php
database/seeders/TopsisSeeder.php
resources/views/layouts/app.blade.php
resources/views/auth/login.blade.php
resources/views/dashboard.blade.php
routes/web.php

Checkpoint

Pastikan:

  • project Laravel berjalan melalui php artisan serve;
  • koneksi database berhasil;
  • migration berhasil;
  • seeder berhasil;
  • login berhasil;
  • logout berhasil;
  • pengunjung yang belum login diarahkan ke halaman login;
  • Bootstrap tampil normal;
  • tabel kriteria, alternatif, dan penilaian tersedia.

Lanjut: CRUD Kriteria dan Alternatif TOPSIS