Bagian 3 dari seri

Input Nilai Alternatif pada Setiap Kriteria TOPSIS Laravel

Diterbitkan 21 September 2026

Pada bagian ini buat form untuk mengisi nilai setiap alternatif pada setiap kriteria.

Contoh Matriks Keputusan

Misalnya tersedia:

C1 - Harga
C2 - Kualitas
C3 - Pelayanan

dan:

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

Matriks keputusan:

Alternatif C1 C2 C3
A1 80 8 9
A2 70 9 8
A3 75 7 9

Setiap sel disimpan pada tabel penilaian.

Buat Controller

php artisan make:controller PenilaianController

PenilaianController

app/Http/Controllers/PenilaianController.php:

<?php

namespace App\Http\Controllers;

use App\Models\Alternatif;
use App\Models\Kriteria;
use App\Models\Penilaian;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class PenilaianController extends Controller
{
    public function index()
    {
        $kriteria = Kriteria::orderBy('kode')->get();
        $alternatif = Alternatif::orderBy('kode')->get();

        $records = Penilaian::all()
            ->keyBy(function ($item) {
                return $item->alternatif_id
                    . '-'
                    . $item->kriteria_id;
            });

        return view('penilaian.index', compact(
            'kriteria',
            'alternatif',
            'records'
        ));
    }

    public function store(Request $request)
    {
        $kriteria = Kriteria::orderBy('kode')->get();
        $alternatif = Alternatif::orderBy('kode')->get();

        if ($kriteria->isEmpty() || $alternatif->isEmpty()) {
            return back()->withErrors(
                'Kriteria dan alternatif harus tersedia.'
            );
        }

        $request->validate([
            'nilai' => ['required', 'array'],
            'nilai.*' => ['required', 'array'],
            'nilai.*.*' => [
                'required',
                'numeric',
                'gte:0',
            ],
        ]);

        DB::transaction(function () use (
            $request,
            $kriteria,
            $alternatif
        ) {
            foreach ($alternatif as $itemAlternatif) {
                foreach ($kriteria as $itemKriteria) {
                    $nilai = $request->input(
                        "nilai.{$itemAlternatif->id}.{$itemKriteria->id}"
                    );

                    Penilaian::updateOrCreate(
                        [
                            'alternatif_id' => $itemAlternatif->id,
                            'kriteria_id' => $itemKriteria->id,
                        ],
                        [
                            'nilai' => (float) $nilai,
                        ]
                    );
                }
            }

            Alternatif::query()->update([
                'nilai_preferensi' => null,
                'ranking' => null,
            ]);
        });

        return redirect()
            ->route('penilaian.index')
            ->with(
                'success',
                'Nilai alternatif berhasil disimpan.'
            );
    }
}

Route

Tambahkan pada grup auth:

use App\Http\Controllers\PenilaianController;

Route::get(
    '/penilaian',
    [PenilaianController::class, 'index']
)->name('penilaian.index');

Route::post(
    '/penilaian',
    [PenilaianController::class, 'store']
)->name('penilaian.store');

Tambahkan Menu Penilaian

Pada navbar:

<a
    class="nav-link text-white"
    href="{{ route('penilaian.index') }}">
    Penilaian
</a>

Buat View Matriks Penilaian

Buat:

resources/views/penilaian/index.blade.php

Isi:

@extends('layouts.app')

@section('title', 'Penilaian')

@section('content')
<div class="d-flex justify-content-between align-items-center mb-3">
    <div>
        <h1 class="h4 mb-1">Penilaian Alternatif</h1>

        <p class="text-muted mb-0">
            Masukkan nilai setiap alternatif pada setiap kriteria.
        </p>
    </div>
</div>

@if($kriteria->isEmpty())
    <div class="alert alert-warning">
        Belum ada kriteria.
    </div>
@elseif($alternatif->isEmpty())
    <div class="alert alert-warning">
        Belum ada alternatif.
    </div>
@else
    <form
        method="POST"
        action="{{ route('penilaian.store') }}">

        @csrf

        <div class="card border-0 shadow-sm">
            <div class="card-body">
                <div class="table-responsive">
                    <table
                        class="table table-bordered align-middle text-center">

                        <thead>
                        <tr>
                            <th class="text-start">Alternatif</th>

                            @foreach($kriteria as $itemKriteria)
                                <th>
                                    {{ $itemKriteria->kode }}

                                    <div class="small fw-normal">
                                        {{ $itemKriteria->nama }}
                                    </div>

                                    <div class="small fw-normal text-muted">
                                        {{ ucfirst($itemKriteria->atribut) }}
                                        |
                                        Bobot: {{ $itemKriteria->bobot }}
                                    </div>
                                </th>
                            @endforeach
                        </tr>
                        </thead>

                        <tbody>
                        @foreach($alternatif as $itemAlternatif)
                            <tr>
                                <th class="text-start">
                                    {{ $itemAlternatif->kode }}
                                    -
                                    {{ $itemAlternatif->nama }}
                                </th>

                                @foreach($kriteria as $itemKriteria)
                                    @php
                                        $key =
                                            $itemAlternatif->id
                                            . '-'
                                            . $itemKriteria->id;

                                        $record =
                                            $records->get($key);

                                        $nilaiLama = old(
                                            "nilai.{$itemAlternatif->id}.{$itemKriteria->id}",
                                            $record?->nilai
                                        );
                                    @endphp

                                    <td style="min-width:150px">
                                        <input
                                            type="number"
                                            name="nilai[{{ $itemAlternatif->id }}][{{ $itemKriteria->id }}]"
                                            value="{{ $nilaiLama }}"
                                            class="form-control text-end"
                                            step="0.000001"
                                            min="0"
                                            required>
                                    </td>
                                @endforeach
                            </tr>
                        @endforeach
                        </tbody>
                    </table>
                </div>

                <button class="btn btn-primary">
                    Simpan Penilaian
                </button>
            </div>
        </div>
    </form>
@endif
@endsection

Contoh Data Tutorial

Masukkan:

A1:
C1 = 80
C2 = 8
C3 = 9

A2:
C1 = 70
C2 = 9
C3 = 8

A3:
C1 = 75
C2 = 7
C3 = 9

Dengan kriteria:

C1 = Harga       | Cost    | Bobot 30
C2 = Kualitas    | Benefit | Bobot 40
C3 = Pelayanan   | Benefit | Bobot 30

Data ini digunakan pada bagian berikutnya untuk melihat proses normalisasi dan ranking.

Tutorial Topsis Laravel Penilaian

Checkpoint

Pastikan:

  • seluruh alternatif muncul sebagai baris;
  • seluruh kriteria muncul sebagai kolom;
  • atribut dan bobot kriteria terlihat;
  • nilai dapat disimpan;
  • nilai lama tampil kembali saat halaman dibuka;
  • kombinasi alternatif-kriteria tidak terduplikasi;
  • hasil ranking lama dikosongkan setelah nilai berubah;
  • nilai negatif ditolak;
  • semua sel wajib diisi.

Lanjut: Perhitungan TOPSIS dengan Laravel