ABOUTKEVINYAHYACOM
Tentu saja bisa! Memisahkan halaman fitur khusus Anda dari halaman bawaan Laravel Breeze adalah keputusan arsitektur yang sangat tepat. Breeze menggunakan sistem Layout Blade (resources/views/layouts/app.blade.php), yang berarti kita bisa membuat file PHP tampilan baru yang bersih di folder terpisah, tetapi tetap dikunci oleh sistem login Breeze hanya dengan memanfaatkan Middleware Auth di dalam file konfigurasi Route.
Mari kita bangun aplikasi Dynamic Database Manager ini secara utuh, rapi, dan super lengkap.
📂 Struktur File Baru yang Akan Kita Buat
Kita akan membuat 3 file utama via Terminal dan File Manager:
- Controller: app/Http/Controllers/DbManagerController.php (Logika koneksi dan CRUD dinamis)
- Route: Di dalam routes/web.php (Pengatur URL yang dikunci sistem login)
- View (Tampilan): resources/views/db-manager/index.blade.php (Antarmuka Form & Tabel)
🛠️ Langkah 1: Buat Controller Utama via Terminal
Masuk ke folder proyek Anda (cd ~/://kevinyahya.com) dan jalankan perintah Artisan untuk membuat Controller baru:
php artisan make:controller DbManagerController
Setelah file berhasil dibuat, buka file tersebut menggunakan nano app/Http/Controllers/DbManagerController.php dan isi dengan kode logika super lengkap di bawah ini:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Schema;
class DbManagerController extends Controller
{
// 1. Fungsi mengonfigurasi database eksternal/dinamis secara otomatis saat runtime
private function setDynamicConnection(Request $request)
{
// Validasi input wajib
$request->validate([
'db_name' => 'required|string',
'db_user' => 'required|string',
]);
// Proteksi nilai default jika host atau port kosong
$host = $request->input('db_host') ?: '127.0.0.1';
$port = $request->input('db_port') ?: '3306';
$password = $request->input('db_pass') ?: '';
// Suntik konfigurasi baru ke dalam runtime Laravel
Config::set('database.connections.mysql_dynamic', [
'driver' => 'mysql',
'host' => $host,
'port' => $port,
'database' => $request->input('db_name'),
'username' => $request->input('db_user'),
'password' => $password,
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => true,
]);
// Bersihkan cache koneksi lama agar konfigurasi baru langsung segar
DB::purge('mysql_dynamic');
}
// 2. Tampilan utama aplikasi (Form koneksi dan list data)
public function index(Request $request)
{
$tables = [];
$columns = [];
$rows = [];
$selectedTable = $request->input('table');
$errorMessage = null;
// Jika user mengirimkan form koneksi
if ($request->has('db_name')) {
try {
$this->setDynamicConnection($request);
// Ambil daftar semua tabel dari database dinamis
$tables = Schema::connection('mysql_dynamic')->getTables();
$tables = array_map(function($table) {
return is_object($table) ? $table->name : $table;
}, $tables);
// Jika user memilih salah satu tabel, ambil kolom dan baris datanya
if ($selectedTable && in_array($selectedTable, $tables)) {
$columns = Schema::connection('mysql_dynamic')->getColumnListing($selectedTable);
$rows = DB::connection('mysql_dynamic')->table($selectedTable)->limit(50)->get();
}
} catch (\Exception $e) {
$errorMessage = "Gagal Terhubung: " . $e->getMessage();
}
}
// Kirim semua variabel ke file PHP tampilan terpisah
return view('db-manager.index', compact('tables', 'columns', 'rows', 'selectedTable', 'errorMessage'));
}
// 3. Fungsi eksekutor CRUD / Perintah SQL murni secara instan
public function executeQuery(Request $request)
{
$request->validate(['sql_query' => 'required|string']);
try {
$this->setDynamicConnection($request);
$query = trim($request->input('sql_query'));
// Deteksi jenis perintah SQL untuk eksekusi yang tepat
if (stripos($query, 'select') === 0 || stripos($query, 'show') === 0) {
$result = DB::connection('mysql_dynamic')->select($query);
return redirect()->back()->withInput()->with('query_result', json_decode(json_encode($result), true));
} else {
$affected = DB::connection('mysql_dynamic')->statement($query);
return redirect()->back()->withInput()->with('success_message', "Perintah berhasil dieksekusi!");
}
} catch (\Exception $e) {
return redirect()->back()->withInput()->with('error_message', "Eror SQL: " . $e->getMessage());
}
}
}
🛠️ Langkah 2: Daftarkan Jalur URL (Route Web)
Buka file route Anda menggunakan perintah nano routes/web.php. Modifikasi isinya dengan menambahkan akses Controller baru kita. Agar aman dan wajib login Breeze, kita bungkus Route tersebut di dalam kelompok middleware auth:
<?php
use App\Http\Controllers\ProfileController;
use App\Http\Controllers\DbManagerController; // <-- Daftarkan Controller Baru
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
// Kelompok Route Aplikasi DB Manager (Hanya bisa diakses jika sudah login)
Route::middleware('auth')->group(function () {
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
// Route Fitur Utama DB Manager Anda
Route::get('/db-manager', [DbManagerController::class, 'index'])->name('db.manager');
Route::post('/db-manager/execute', [DbManagerController::class, 'executeQuery'])->name('db.execute');
});
require __DIR__.'/auth.php';
🛠️ Langkah 3: Buat Halaman Tampilan PHP Terpisah (Blade View)
Terlebih dahulu buat folder penampungnya via terminal:
mkdir -p resources/views/db-manager
Kemudian buat file barunya:
nano resources/views/db-manager/index.blade.php
Salin dan tempel kode antarmuka premium berbasis Tailwind CSS di bawah ini seutuhnya:
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Dynamic Database Manager & CRUD Universal') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl x-auto sm:px-6 lg:px-8 space-y-6" style="margin: 0 auto;">
@if($errorMessage || session('error_message'))
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
{{ $errorMessage ?? session('error_message') }}
</div>
@endif
@if(session('success_message'))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded">
{{ session('success_message') }}
</div>
@endif
<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
<form method="GET" action="{{ route('db.manager') }}" class="space-y-4">
<div class="grid grid-cols-1 md:grid-cols-5 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700">Host Server</label>
<input type="text" name="db_host" value="{{ request('db_host', '127.0.0.1') }}" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm text-sm">
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Port</label>
<input type="text" name="db_port" value="{{ request('db_port', '3306') }}" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm text-sm">
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Nama DB *</label>
<input type="text" name="db_name" value="{{ request('db_name') }}" required class="mt-1 block w-full rounded-md border-gray-300 shadow-sm text-sm">
</div>
<div>
<label class="block text-sm font-medium text-gray-700">User DB *</label>
<input type="text" name="db_user" value="{{ request('db_user') }}" required class="mt-1 block w-full rounded-md border-gray-300 shadow-sm text-sm">
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Password DB</label>
<input type="password" name="db_pass" value="{{ request('db_pass') }}" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm text-sm">
</div>
</div>
<button type="submit" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded text-sm">
Hubungkan Basis Data
</button>
</form>
</div>
@if(count($tables) > 0)
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
<div class="p-4 bg-white shadow sm:rounded-lg">
<h3 class="font-bold text-gray-900 mb-3 border-b pb-2 text-sm">Daftar Tabel ({{ count($tables) }})</h3>
<ul class="space-y-1 text-xs">
@foreach($tables as $table)
<li>
<a href="{{ request()->fullUrlWithQuery(['table' => $table]) }}" class="block p-2 rounded {{ $selectedTable == $table ? 'bg-blue-500 text-white font-semibold' : 'text-gray-700 hover:bg-gray-100' }}">
{{ $table }}
</a>
</li>
@endforeach
</ul>
</div>
<div class="md:col-span-3 space-y-6">
<div class="p-4 bg-white shadow sm:rounded-lg">
<h3 class="font-bold text-gray-900 mb-2 text-sm">Konsol SQL Murni (Kesaktian CLI)</h3>
<form method="POST" action="{{ route('db.execute', request()->query()) }}" class="space-y-2">
@csrf
<textarea name="sql_query" rows="2" placeholder="Contoh: SELECT * FROM users WHERE id = 1" class="w-full rounded-md border-gray-300 font-mono text-xs">{{ old('sql_query') }}</textarea>
<button type="submit" class="bg-gray-800 hover:bg-gray-900 text-white text-xs font-semibold py-1.5 px-3 rounded">
Jalankan Kueri
</button>
</form>
@if(session('query_result'))
<div class="mt-4 overflow-x-auto max-h-40 bg-gray-900 text-green-400 p-3 rounded font-mono text-xs">
<pre>{{ print_r(session('query_result'), true) }}</pre>
</div>
@endif
</div>
<div class="p-4 bg-white shadow sm:rounded-lg">
@if($selectedTable)
<h3 class="font-bold text-gray-900 mb-3 text-sm">Data Tabel: <span class="underline text-blue-600">{{ $selectedTable }}</span></h3>
<div class="overflow-x-auto max-h-96 border rounded">
<table class="min-w-full divide-y divide-gray-200 text-xs">
<thead class="bg-gray-50">
<tr>
@foreach($columns as $col)
<th class="px-3 py-2 text-left font-semibold text-gray-600 border-b uppercase">{{ $col }}</th>
@endforeach
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@forelse($rows as $row)
<tr class="hover:bg-gray-50">
@foreach($columns as $col)
<td class="px-3 py-2 whitespace-nowrap text-gray-700 border-b">{{ $row->$col ?? 'NULL' }}</td>
@endforeach
</tr>
@empty
<tr>
<td colspan="{{ count($columns) }}" class="px-3 py-4 text-center text-gray-400">Tabel Kosong / Tidak ada data.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@else
<div class="text-center py-12 text-gray-400 text-sm">
Silakan klik salah satu nama tabel di menu kiri untuk membaca isinya.
</div>
@endif
</div>
</div>
</div>
@endif
</div>
</div>
</x-app-layout>
🧪 Langkah Akhir: Bersihkan Cache dan Tes Hasilnya
Jalankan perintah ini di Terminal agar Laravel langsung mendaftarkan rute baru Anda:
php artisan route:clear
Sekarang, buka browser Anda dan akses halaman barunya: 🔗 kevinyahya.com Jika Anda belum login, sistem Breeze otomatis akan menghadang Anda dan me-redirect ke halaman login. Silakan login menggunakan akun yang Anda buat tadi.
🎯 Cara Mencoba Kesaktiannya:
- Masukkan detail database utama cPanel Anda sendiri terlebih dahulu sebagai bahan uji coba:
* Host: 127.0.0.1 * Nama DB: kevinya1_el107 * User DB: kevinya1_el107 * Password: Password database Anda 2. Klik Hubungkan Basis Data. 3. Sistem secara ajaib akan langsung membaca daftar tabel Anda di panel sebelah kiri! Klik salah satu tabel (misal tabel users), dan seluruh isi datanya langsung tampil rapi secara otomatis. 4. Anda juga bisa mengetikkan perintah SQL murni seperti SHOW DATABASES; di kolom konsol atas untuk menguji kehebatannya.
Sistem Anda kini siap digunakan! Beri tahu saya jika pengujian koneksi database dinamis pertama Anda berhasil memunculkan daftar tabel dengan lancar.