Bagian 3 dari seri
Perbandingan Kriteria dan Perhitungan Bobot AHP Laravel
Diterbitkan 18 September 2026
Buat Service AHP
Buat folder:
app/Services
Lalu buat file:
app/Services/AhpService.php
Isi app/Services/AhpService.php:
<?php
namespace App\Services;
class AhpService
{
public function matrix(array $ids, $records, string $left, string $right): array
{
$matrix = [];
foreach ($ids as $i) {
foreach ($ids as $j) {
if ($i === $j) {
$matrix[$i][$j] = 1.0;
continue;
}
$direct = $records->first(
fn ($row) => $row->{$left} == $i && $row->{$right} == $j
);
if ($direct) {
$matrix[$i][$j] = (float) $direct->nilai;
continue;
}
$reverse = $records->first(
fn ($row) => $row->{$left} == $j && $row->{$right} == $i
);
$matrix[$i][$j] = $reverse
? 1 / (float) $reverse->nilai
: 1.0;
}
}
return $matrix;
}
public function priority(array $matrix, array $ids): array
{
$columnTotals = [];
foreach ($ids as $j) {
$columnTotals[$j] = array_sum(
array_map(fn ($i) => $matrix[$i][$j], $ids)
);
}
$normalized = [];
$weights = [];
foreach ($ids as $i) {
foreach ($ids as $j) {
$normalized[$i][$j] =
$matrix[$i][$j] / $columnTotals[$j];
}
$weights[$i] =
array_sum($normalized[$i]) / count($ids);
}
return compact('columnTotals', 'normalized', 'weights');
}
public function consistency(array $matrix, array $weights, array $ids): array
{
$n = count($ids);
if ($n < 3) {
return [
'lambdaMax' => $n,
'ci' => 0,
'ri' => 0,
'cr' => 0,
];
}
$weightedSum = [];
foreach ($ids as $i) {
$weightedSum[$i] = 0;
foreach ($ids as $j) {
$weightedSum[$i] += $matrix[$i][$j] * $weights[$j];
}
}
$lambda = [];
foreach ($ids as $i) {
$lambda[$i] = $weightedSum[$i] / $weights[$i];
}
$lambdaMax = array_sum($lambda) / $n;
$ci = ($lambdaMax - $n) / ($n - 1);
$riTable = [
1 => 0.00,
2 => 0.00,
3 => 0.58,
4 => 0.90,
5 => 1.12,
6 => 1.24,
7 => 1.32,
8 => 1.41,
9 => 1.45,
10 => 1.49,
];
$ri = $riTable[$n] ?? 1.49;
$cr = $ri > 0 ? $ci / $ri : 0;
return compact('lambdaMax', 'ci', 'ri', 'cr');
}
}
Buat Controller
php artisan make:controller PerbandinganKriteriaController
app/Http/Controllers/PerbandinganKriteriaController.php:
<?php
namespace App\Http\Controllers;
use App\Models\Kriteria;
use App\Models\PerbandinganKriteria;
use App\Services\AhpService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
class PerbandinganKriteriaController extends Controller
{
private array $skalaAhp = [
0.111111,
0.125,
0.142857,
0.166667,
0.2,
0.25,
0.333333,
0.5,
1,
2,
3,
4,
5,
6,
7,
8,
9,
];
public function index(AhpService $ahp)
{
$kriteria = Kriteria::orderBy('kode')->get();
$records = PerbandinganKriteria::all();
$ids = $kriteria->pluck('id')->all();
$matrix = $ids
? $ahp->matrix($ids, $records, 'kriteria_1_id', 'kriteria_2_id')
: [];
$hasil = count($ids) >= 2
? $ahp->priority($matrix, $ids)
: null;
$konsistensi = $hasil
? $ahp->consistency($matrix, $hasil['weights'], $ids)
: null;
return view('perbandingan-kriteria.index', compact(
'kriteria',
'matrix',
'hasil',
'konsistensi'
));
}
public function store(Request $request)
{
$kriteria = Kriteria::orderBy('kode')->get();
$ids = $kriteria->pluck('id')->all();
$request->validate([
'nilai' => ['required', 'array'],
'nilai.*.*' => [
'required',
'numeric',
Rule::in($this->skalaAhp),
],
]);
DB::transaction(function () use ($request, $ids) {
PerbandinganKriteria::query()->delete();
for ($i = 0; $i < count($ids); $i++) {
for ($j = $i + 1; $j < count($ids); $j++) {
$a = $ids[$i];
$b = $ids[$j];
PerbandinganKriteria::create([
'kriteria_1_id' => $a,
'kriteria_2_id' => $b,
'nilai' => (float) $request->input("nilai.$a.$b"),
]);
}
}
});
return redirect()
->route('perbandingan-kriteria.index')
->with('success', 'Perbandingan kriteria berhasil disimpan.');
}
}
Route
Tambahkan pada grup auth:
use App\Http\Controllers\PerbandinganKriteriaController;
Route::get(
'/perbandingan-kriteria',
[PerbandinganKriteriaController::class, 'index']
)->name('perbandingan-kriteria.index');
Route::post(
'/perbandingan-kriteria',
[PerbandinganKriteriaController::class, 'store']
)->name('perbandingan-kriteria.store');
Tambahkan Menu Perbandingan Kriteria
Buka resources/views/layouts/app.blade.php, lalu tambahkan menu berikut pada navbar:
<a class="nav-link text-white" href="{{ route('kriteria.index') }}">
Kriteria
</a>
<a class="nav-link text-white" href="{{ route('alternatif.index') }}">
Alternatif
</a>
<a class="nav-link text-white" href="{{ route('perbandingan-kriteria.index') }}">
Perbandingan Kriteria
</a>
View Matriks Kriteria
Buat resources/views/perbandingan-kriteria/index.blade.php:
@extends('layouts.app')
@section('title', 'Perbandingan Kriteria')
@section('content')
<h1 class="h4 mb-3">Perbandingan Kriteria</h1>
<div class="alert alert-info">
Pilih nilai berdasarkan skala perbandingan AHP.
</div>
@php
$skalaAhp = [
'0.111111' => '1/9',
'0.125' => '1/8',
'0.142857' => '1/7',
'0.166667' => '1/6',
'0.2' => '1/5',
'0.25' => '1/4',
'0.333333' => '1/3',
'0.5' => '1/2',
'1' => '1',
'2' => '2',
'3' => '3',
'4' => '4',
'5' => '5',
'6' => '6',
'7' => '7',
'8' => '8',
'9' => '9',
];
@endphp
<form method="POST" action="{{ route('perbandingan-kriteria.store') }}">
@csrf
<div class="card border-0 shadow-sm mb-4">
<div class="card-body table-responsive">
<table class="table table-bordered text-center align-middle">
<thead>
<tr>
<th>Kriteria</th>
@foreach($kriteria as $kolom)
<th>{{ $kolom->kode }}</th>
@endforeach
</tr>
</thead>
<tbody>
@foreach($kriteria as $i => $baris)
<tr>
<th>{{ $baris->kode }}</th>
@foreach($kriteria as $j => $kolom)
<td>
@if($i === $j)
1
@elseif($i < $j)
@php
$nilaiAktif = old(
"nilai.{$baris->id}.{$kolom->id}",
$matrix[$baris->id][$kolom->id] ?? 1
);
@endphp
<select
name="nilai[{{ $baris->id }}][{{ $kolom->id }}]"
class="form-select"
required>
@foreach($skalaAhp as $value => $label)
<option
value="{{ $value }}"
@selected(abs((float) $nilaiAktif - (float) $value) < 0.000001)>
{{ $label }}
</option>
@endforeach
</select>
@else
{{ number_format(
$matrix[$baris->id][$kolom->id] ?? 1,
4
) }}
@endif
</td>
@endforeach
</tr>
@endforeach
</tbody>
</table>
<button class="btn btn-primary">Simpan dan Hitung</button>
</div>
</div>
</form>
@if($hasil)
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<h2 class="h5">Bobot Prioritas</h2>
<table class="table table-bordered">
<thead>
<tr>
<th>Kriteria</th>
<th>Bobot</th>
</tr>
</thead>
<tbody>
@foreach($kriteria as $item)
<tr>
<td>{{ $item->kode }} - {{ $item->nama }}</td>
<td>{{ number_format($hasil['weights'][$item->id], 6) }}</td>
</tr>
@endforeach
</tbody>
</table>
<p>Lambda Maksimum:
<strong>{{ number_format($konsistensi['lambdaMax'], 6) }}</strong>
</p>
<p>CI:
<strong>{{ number_format($konsistensi['ci'], 6) }}</strong>
</p>
<p>CR:
<strong>{{ number_format($konsistensi['cr'], 6) }}</strong>
</p>
@if($konsistensi['cr'] <= 0.1)
<div class="alert alert-success mb-0">
Matriks konsisten (CR ≤ 0,1).
</div>
@else
<div class="alert alert-danger mb-0">
Matriks belum konsisten. Perbaiki nilai perbandingan.
</div>
@endif
</div>
</div>
@endif
@endsection

Checkpoint
Pastikan:
- Nilai diagonal selalu 1.
- Input menggunakan dropdown skala AHP.
- Nilai seperti
1/3dapat dipilih langsung. - Nilai di bawah diagonal otomatis merupakan kebalikan.
- Bobot kriteria berjumlah mendekati 1.
- CR ditampilkan.
- Matriks diterima jika CR ≤ 0,1.




