Konsep Model-View-Controller (MVC) pada Laravel

Halo, Sob! Sudah tahu belum kalau Laravel menggunakan arsitektur MVC (Model-View-Controller) untuk membangun program dan aplikasi. Artikel ini akan menjelaskan konsep MVC Laravel dan memberikan contoh implementasi sederhana dalam sebuah proyek Laravel. Simak yuk, Sob!

Laravel menerapkan konsep MVC untuk memisahkan logika program aplikasi menjadi tiga komponen, yaitu:

  1. Model

Model adalah komponen yang menangani logika pengolahan data, seperti mengambil, menyimpan, mengubah, dan menghapus data. Laravel menyediakan query builder dan Eloquent ORM (Object-Relational Mapping) untuk mempermudah interaksi dengan database.

  1. View

Bagian view memiliki peran untuk menampilkan antarmuka aplikasi ke pengguna. View menampilkan data yang berupa HTML, JSON, XML. Laravel menggunakan Blade template engine untuk membuat tampilan, dengan format file namafile.blade.php.

  1. Controller

Controller menghubungkan Model dan View. Ketika pengguna mengirimkan input melalui HTTP request, Controller memprosesnya dengan berinteraksi langsung dengan Model, lalu mengirimkan hasilnya pada View untuk ditampilkan ke pengguna.

Dengan arsitektur MVC, pengembang dapat membangun dan memelihara aplikasi secara lebih terstruktur, efisien, dan mudah dikembangkan.

Penjelasan mengenai konsep MVC ini juga bisa kamu akses pada artikel Menjalankan Laravel di Hosting.

Selanjutnya, mari simak bagaimana konsep MVS bekerja di Laravel dengan mempraktikkan membuat aplikasi sederhana yang menampilkan data customer.

Catatan: Asumsi kamu sudah menginisiasi proyek Laravel di lokal.

Step 1: Membuat Database

Pertama, buat database melalui phpMyAdmin dengan nama db_laraveltutorial

1-buat-database-konsep-model-view-controller-laravel-mvc

Step 2: Isi Data di Database

Selanjutnya, isi database dengan tabel dan dummy data menggunakan query di bawah ini.

				
					USE db_laraveltutorial;
CREATE TABLE IF NOT EXISTS customers (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(150) NOT NULL,
address VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO customers (name, email, address) VALUES
('Andi Pratama', 'andi@example.com', 'Jl. Merdeka No.1'),
('Benny Santoso', 'benny@example.com', 'Jl. Kuningan No.5'),
('Cici Dewi', 'cici@example.com', 'Jl. Sukajadi No.12');
				
			
2-isi-database-konsep-model-view-controller-laravel-mvc

Step 3: Mengatur Konfigurasi

Sesuaikan konfigurasi di file .env

3-setting-file-env-laravel

Keterangan

  • DB_DATABASE : isi dengan nama database
  • DB_USERNAME : isi dengan nama user database
  • DB_PASSWORD : isi dengan password database

Supaya Laravel mencatat perubahan di .env jalankan perintah:

				
					php artisan config:clear
php artisan config:cache
				
			

Step 4: Membuat Model

Artikel ini mencontohkan membuat model Customer, yang mana model ini akan berinteraksi dengan tabel Customer yang ada pada database. Perintah membuat model:

				
					php artisan make:model Customer
				
			

Ubah model Customer.php yang berada di app/Models/Customer/php

				
					<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Customer extends Model {
    use HasFactory;
    protected $table = 'customers';
    protected $fillable = ['name','email','address'];
}
				
			

Tips: Gunakan huruf kapital (PascalCase) untuk nama model, misalnya Customer, Order, atau User. Ini mengikuti standar penulisan class di PHP.

Step 5: Membuat Controller

Untuk mengambil data di Model, buat Controller menggunakan perintah:

				
					php artisan make:controller CustomerController
				
			

Ubah file CustomerContoller.php yang berada di app/Http/Controllers/CustomerController

				
					<?php
namespace App\Http\Controllers;

use App\Models\Customer;

class CustomerController extends Controller {
    public function index() {
        $customers = Customer::all();
        return view('customers.index', compact('customers'));
    }
}
				
			

Step 6: Membuat View

Supaya bisa menampilkan data di browser, buat View dengan nama index.blade.php di dalam folder customers, file ini nantinya berada di resources/views/customers/index.blade.php


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Data Customer</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f9f9f9;
padding: 40px;
}
h1 {
text-align: center;
color: #333;
}
table {
border-collapse: collapse;
width: 100%;
background-color: #fff;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
th, td {
padding: 12px 15px;
border: 1px solid #ddd;
text-align: left;
}
th {
background-color: #007bff;
color: #fff;
}
tr:nth-child(even) {
background-color: #f2f2f2;
}
tr:hover {
background-color: #e9ecef;
}
</style>
</head>
<body>
<h1>Data Customer</h1>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Address</th>
</tr>
</thead>
<tbody>
@foreach($customers as $c)
<tr>
<td>{{ $c->id }}</td>
<td>{{ $c->name }}</td>
<td>{{ $c->email }}</td>
<td>{{ $c->address }}</td>
</tr>
@endforeach
</tbody>
</table>
<script id="perfmatters-delayed-scripts-js">(function(){window.pmDC=1;window.pmDT=10;if(window.pmDT){var e=setTimeout(d,window.pmDT*1e3)}const t=["keydown","mousedown","mousemove","wheel","touchmove","touchstart","touchend"];const n={normal:[],defer:[],async:[]};const o=[];const i=[];var r=false;var a="";window.pmIsClickPending=false;t.forEach(function(e){window.addEventListener(e,d,{passive:true})});if(window.pmDC){window.addEventListener("touchstart",b,{passive:true});window.addEventListener("mousedown",b)}function d(){if(typeof e!=="undefined"){clearTimeout(e)}t.forEach(function(e){window.removeEventListener(e,d,{passive:true})});if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",s)}else{s()}}async function s(){c();u();f();m();await w(n.normal);await w(n.defer);await w(n.async);await p();document.querySelectorAll("link[data-pmdelayedstyle]").forEach(function(e){e.setAttribute("href",e.getAttribute("data-pmdelayedstyle"))});window.dispatchEvent(new Event("perfmatters-allScriptsLoaded")),E().then(()=>{h()})}function c(){let o={};function e(t,e){function n(e){return o[t].delayedEvents.indexOf(e)>=0?"perfmatters-"+e:e}if(!o[t]){o[t]={originalFunctions:{add:t.addEventListener,remove:t.removeEventListener},delayedEvents:[]};t.addEventListener=function(){arguments[0]=n(arguments[0]);o[t].originalFunctions.add.apply(t,arguments)};t.removeEventListener=function(){arguments[0]=n(arguments[0]);o[t].originalFunctions.remove.apply(t,arguments)}}o[t].delayedEvents.push(e)}function t(t,n){const e=t[n];Object.defineProperty(t,n,{get:!e?function(){}:e,set:function(e){t["perfmatters"+n]=e}})}e(document,"DOMContentLoaded");e(window,"DOMContentLoaded");e(window,"load");e(document,"readystatechange");t(document,"onreadystatechange");t(window,"onload")}function u(){let n=window.jQuery;Object.defineProperty(window,"jQuery",{get(){return n},set(t){if(t&&t.fn&&!o.includes(t)){t.fn.ready=t.fn.init.prototype.ready=function(e){if(r){e.bind(document)(t)}else{document.addEventListener("perfmatters-DOMContentLoaded",function(){e.bind(document)(t)})}};const e=t.fn.on;t.fn.on=t.fn.init.prototype.on=function(){if(this[0]===window){function t(e){e=e.split(" ");e=e.map(function(e){if(e==="load"||e.indexOf("load.")===0){return"perfmatters-jquery-load"}else{return e}});e=e.join(" ");return e}if(typeof arguments[0]=="string"||arguments[0]instanceof String){arguments[0]=t(arguments[0])}else if(typeof arguments[0]=="object"){Object.keys(arguments[0]).forEach(function(e){delete Object.assign(arguments[0],{[t(e)]:arguments[0][e]})[e]})}}return e.apply(this,arguments),this};o.push(t)}n=t}})}function f(){document.querySelectorAll("script[type=pmdelayedscript]").forEach(function(e){if(e.hasAttribute("src")){if(e.hasAttribute("defer")&&e.defer!==false){n.defer.push(e)}else if(e.hasAttribute("async")&&e.async!==false){n.async.push(e)}else{n.normal.push(e)}}else{n.normal.push(e)}})}function m(){var o=document.createDocumentFragment();[...n.normal,...n.defer,...n.async].forEach(function(e){var t=e.getAttribute("src");if(t){var n=document.createElement("link");n.href=t;if(e.getAttribute("data-perfmatters-type")=="module"){n.rel="modulepreload"}else{n.rel="preload";n.as="script"}o.appendChild(n)}});document.head.appendChild(o)}async function w(e){var t=e.shift();if(t){await l(t);return w(e)}return Promise.resolve()}async function l(t){await v();return new Promise(function(e){const n=document.createElement("script");[...t.attributes].forEach(function(e){let t=e.nodeName;if(t!=="type"){if(t==="data-perfmatters-type"){t="type"}n.setAttribute(t,e.nodeValue)}});if(t.hasAttribute("src")){n.addEventListener("load",e);n.addEventListener("error",e)}else{n.text=t.text;e()}t.parentNode.replaceChild(n,t)})}async function p(){r=true;await v();document.dispatchEvent(new Event("perfmatters-DOMContentLoaded"));await v();window.dispatchEvent(new Event("perfmatters-DOMContentLoaded"));await v();document.dispatchEvent(new Event("perfmatters-readystatechange"));await v();if(document.perfmattersonreadystatechange){document.perfmattersonreadystatechange()}await v();window.dispatchEvent(new Event("perfmatters-load"));await v();if(window.perfmattersonload){window.perfmattersonload()}await v();o.forEach(function(e){e(window).trigger("perfmatters-jquery-load")})}async function v(){return new Promise(function(e){requestAnimationFrame(e)})}function h(){window.removeEventListener("touchstart",b,{passive:true});window.removeEventListener("mousedown",b);i.forEach(e=>{if(e.target.outerHTML===a){e.target.dispatchEvent(new MouseEvent("click",{view:e.view,bubbles:true,cancelable:true}))}})}function E(){return new Promise(e=>{window.pmIsClickPending?g=e:e()})}function y(){window.pmIsClickPending=true}function g(){window.pmIsClickPending=false}function L(e){e.target.removeEventListener("click",L);C(e.target,"pm-onclick","onclick");i.push(e),e.preventDefault();e.stopPropagation();e.stopImmediatePropagation();g()}function b(e){if(e.target.tagName!=="HTML"){if(!a){a=e.target.outerHTML}window.addEventListener("touchend",A);window.addEventListener("mouseup",A);window.addEventListener("touchmove",k,{passive:true});window.addEventListener("mousemove",k);e.target.addEventListener("click",L);C(e.target,"onclick","pm-onclick");y()}}function k(e){window.removeEventListener("touchend",A);window.removeEventListener("mouseup",A);window.removeEventListener("touchmove",k,{passive:true});window.removeEventListener("mousemove",k);e.target.removeEventListener("click",L);C(e.target,"pm-onclick","onclick");g()}function A(e){window.removeEventListener("touchend",A);window.removeEventListener("mouseup",A);window.removeEventListener("touchmove",k,{passive:true});window.removeEventListener("mousemove",k)}function C(e,t,n){if(e.hasAttribute&&e.hasAttribute(t)){event.target.setAttribute(n,event.target.getAttribute(t));event.target.removeAttribute(t)}}})();(function(){var e,a,s;function t(){(e=document.createElement("span")).id="elementor-device-mode",e.setAttribute("class","elementor-screen-only"),document.body.appendChild(e),requestAnimationFrame(n)}function n(){a=o(getComputedStyle(e,":after").content.replace(/"/g,"")),document.querySelectorAll(".elementor-invisible[data-settings]").forEach(e=>{let t=e.getBoundingClientRect();if(t.bottom>=0&&t.top<=window.innerHeight)try{i(e)}catch(e){}})}function i(e){let t=JSON.parse(e.dataset.settings),n=t._animation_delay||t.animation_delay||0,i=t[a.find(e=>t[e])];if("none"===i)return void e.classList.remove("elementor-invisible");e.classList.remove(i),s&&e.classList.remove(s),s=i;let o=setTimeout(()=>{e.classList.remove("elementor-invisible"),e.classList.add("animated",i),l(e,t)},n);window.addEventListener("perfmatters-startLoading",function(){clearTimeout(o)})}function o(e="mobile"){let n=[""];switch(e){case"mobile":n.unshift("_mobile");case"tablet":n.unshift("_tablet");case"desktop":n.unshift("_desktop")}let i=[];return["animation","_animation"].forEach(t=>{n.forEach(e=>{i.push(t+e)})}),i}function l(e,t){o().forEach(e=>delete t[e]),e.dataset.settings=JSON.stringify(t)}document.addEventListener("DOMContentLoaded",t)})();</script><script id="perfmatters-delayed-scripts-js">(function(){window.pmDC=1;window.pmDT=10;if(window.pmDT){var e=setTimeout(d,window.pmDT*1e3)}const t=["keydown","mousedown","mousemove","wheel","touchmove","touchstart","touchend"];const n={normal:[],defer:[],async:[]};const o=[];const i=[];var r=false;var a="";window.pmIsClickPending=false;t.forEach(function(e){window.addEventListener(e,d,{passive:true})});if(window.pmDC){window.addEventListener("touchstart",b,{passive:true});window.addEventListener("mousedown",b)}function d(){if(typeof e!=="undefined"){clearTimeout(e)}t.forEach(function(e){window.removeEventListener(e,d,{passive:true})});if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",s)}else{s()}}async function s(){c();u();f();m();await w(n.normal);await w(n.defer);await w(n.async);await p();document.querySelectorAll("link[data-pmdelayedstyle]").forEach(function(e){e.setAttribute("href",e.getAttribute("data-pmdelayedstyle"))});window.dispatchEvent(new Event("perfmatters-allScriptsLoaded")),E().then(()=>{h()})}function c(){let o={};function e(t,e){function n(e){return o[t].delayedEvents.indexOf(e)>=0?"perfmatters-"+e:e}if(!o[t]){o[t]={originalFunctions:{add:t.addEventListener,remove:t.removeEventListener},delayedEvents:[]};t.addEventListener=function(){arguments[0]=n(arguments[0]);o[t].originalFunctions.add.apply(t,arguments)};t.removeEventListener=function(){arguments[0]=n(arguments[0]);o[t].originalFunctions.remove.apply(t,arguments)}}o[t].delayedEvents.push(e)}function t(t,n){const e=t[n];Object.defineProperty(t,n,{get:!e?function(){}:e,set:function(e){t["perfmatters"+n]=e}})}e(document,"DOMContentLoaded");e(window,"DOMContentLoaded");e(window,"load");e(document,"readystatechange");t(document,"onreadystatechange");t(window,"onload")}function u(){let n=window.jQuery;Object.defineProperty(window,"jQuery",{get(){return n},set(t){if(t&&t.fn&&!o.includes(t)){t.fn.ready=t.fn.init.prototype.ready=function(e){if(r){e.bind(document)(t)}else{document.addEventListener("perfmatters-DOMContentLoaded",function(){e.bind(document)(t)})}};const e=t.fn.on;t.fn.on=t.fn.init.prototype.on=function(){if(this[0]===window){function t(e){e=e.split(" ");e=e.map(function(e){if(e==="load"||e.indexOf("load.")===0){return"perfmatters-jquery-load"}else{return e}});e=e.join(" ");return e}if(typeof arguments[0]=="string"||arguments[0]instanceof String){arguments[0]=t(arguments[0])}else if(typeof arguments[0]=="object"){Object.keys(arguments[0]).forEach(function(e){delete Object.assign(arguments[0],{[t(e)]:arguments[0][e]})[e]})}}return e.apply(this,arguments),this};o.push(t)}n=t}})}function f(){document.querySelectorAll("script[type=pmdelayedscript]").forEach(function(e){if(e.hasAttribute("src")){if(e.hasAttribute("defer")&&e.defer!==false){n.defer.push(e)}else if(e.hasAttribute("async")&&e.async!==false){n.async.push(e)}else{n.normal.push(e)}}else{n.normal.push(e)}})}function m(){var o=document.createDocumentFragment();[...n.normal,...n.defer,...n.async].forEach(function(e){var t=e.getAttribute("src");if(t){var n=document.createElement("link");n.href=t;if(e.getAttribute("data-perfmatters-type")=="module"){n.rel="modulepreload"}else{n.rel="preload";n.as="script"}o.appendChild(n)}});document.head.appendChild(o)}async function w(e){var t=e.shift();if(t){await l(t);return w(e)}return Promise.resolve()}async function l(t){await v();return new Promise(function(e){const n=document.createElement("script");[...t.attributes].forEach(function(e){let t=e.nodeName;if(t!=="type"){if(t==="data-perfmatters-type"){t="type"}n.setAttribute(t,e.nodeValue)}});if(t.hasAttribute("src")){n.addEventListener("load",e);n.addEventListener("error",e)}else{n.text=t.text;e()}t.parentNode.replaceChild(n,t)})}async function p(){r=true;await v();document.dispatchEvent(new Event("perfmatters-DOMContentLoaded"));await v();window.dispatchEvent(new Event("perfmatters-DOMContentLoaded"));await v();document.dispatchEvent(new Event("perfmatters-readystatechange"));await v();if(document.perfmattersonreadystatechange){document.perfmattersonreadystatechange()}await v();window.dispatchEvent(new Event("perfmatters-load"));await v();if(window.perfmattersonload){window.perfmattersonload()}await v();o.forEach(function(e){e(window).trigger("perfmatters-jquery-load")})}async function v(){return new Promise(function(e){requestAnimationFrame(e)})}function h(){window.removeEventListener("touchstart",b,{passive:true});window.removeEventListener("mousedown",b);i.forEach(e=>{if(e.target.outerHTML===a){e.target.dispatchEvent(new MouseEvent("click",{view:e.view,bubbles:true,cancelable:true}))}})}function E(){return new Promise(e=>{window.pmIsClickPending?g=e:e()})}function y(){window.pmIsClickPending=true}function g(){window.pmIsClickPending=false}function L(e){e.target.removeEventListener("click",L);C(e.target,"pm-onclick","onclick");i.push(e),e.preventDefault();e.stopPropagation();e.stopImmediatePropagation();g()}function b(e){if(e.target.tagName!=="HTML"){if(!a){a=e.target.outerHTML}window.addEventListener("touchend",A);window.addEventListener("mouseup",A);window.addEventListener("touchmove",k,{passive:true});window.addEventListener("mousemove",k);e.target.addEventListener("click",L);C(e.target,"onclick","pm-onclick");y()}}function k(e){window.removeEventListener("touchend",A);window.removeEventListener("mouseup",A);window.removeEventListener("touchmove",k,{passive:true});window.removeEventListener("mousemove",k);e.target.removeEventListener("click",L);C(e.target,"pm-onclick","onclick");g()}function A(e){window.removeEventListener("touchend",A);window.removeEventListener("mouseup",A);window.removeEventListener("touchmove",k,{passive:true});window.removeEventListener("mousemove",k)}function C(e,t,n){if(e.hasAttribute&&e.hasAttribute(t)){event.target.setAttribute(n,event.target.getAttribute(t));event.target.removeAttribute(t)}}})();(function(){var e,a,s;function t(){(e=document.createElement("span")).id="elementor-device-mode",e.setAttribute("class","elementor-screen-only"),document.body.appendChild(e),requestAnimationFrame(n)}function n(){a=o(getComputedStyle(e,":after").content.replace(/"/g,"")),document.querySelectorAll(".elementor-invisible[data-settings]").forEach(e=>{let t=e.getBoundingClientRect();if(t.bottom>=0&&t.top<=window.innerHeight)try{i(e)}catch(e){}})}function i(e){let t=JSON.parse(e.dataset.settings),n=t._animation_delay||t.animation_delay||0,i=t[a.find(e=>t[e])];if("none"===i)return void e.classList.remove("elementor-invisible");e.classList.remove(i),s&&e.classList.remove(s),s=i;let o=setTimeout(()=>{e.classList.remove("elementor-invisible"),e.classList.add("animated",i),l(e,t)},n);window.addEventListener("perfmatters-startLoading",function(){clearTimeout(o)})}function o(e="mobile"){let n=[""];switch(e){case"mobile":n.unshift("_mobile");case"tablet":n.unshift("_tablet");case"desktop":n.unshift("_desktop")}let i=[];return["animation","_animation"].forEach(t=>{n.forEach(e=>{i.push(t+e)})}),i}function l(e,t){o().forEach(e=>delete t[e]),e.dataset.settings=JSON.stringify(t)}document.addEventListener("DOMContentLoaded",t)})();</script></body>
</html

Step 7: Membuat Route

Selanjutnya, menentukan routing untuk halaman index.blade.php yang akan menampilkan data customer. Untuk mengatur route berada di routes/web.php


use App\Http\Controllers\CustomerController;
Route::get('/customers', [CustomerController::class, 'index']);

Step 8: Coba Aplikasi Laravel

Sekarang, periksa aplikasi di browser menggunakan perintah artisan di terminal.


php artisan serve

4-php-artisan-serve

Akses aplikasi menggunakan URL yang muncul dengan menekan Ctrl + klik URL dengan kursor.

4-tampil-data-konsep-model-view-controller-laravel-mvc

Setelah mempraktikkan ini kamu akan lebih paham mengenai konsep Model-View-Controller pada Laravel. Semangat, Sob!

Simak terus tips-tips seputar domain dan hosting dari Jagoan Hosting di laman Pusat Tutorial Hosting & Domain. Kalau ada artikel yang belum tersedia, silahkan menyampaikan kepada Tim Support via Live Chat atau Open Ticket, Sob!

Related Posts
Tips Mengatasi Error Undefined Index / Variable

Pengantar Halo sobat Jagoan! di tutorial kali ini kita bakalan membahas permasalahan Error Undefined Index/Variable. Pasti sekarang kamu lagi main-main Read more

Tutorial Mendeteksi Script Jahat pada Website Sebelum Publish

Hai, Sob kamu pasti tentu tidak ingin jika website kamu dibobol oleh hacker, terutama jika kamu seorang programmer dan webmaster. Read more

Tutorial Install Virus Scanner Clam AV pada cPanel

Kamu ngerasa insecure karena belum install antivirus? Install antivirus emang penting, Sob untuk menjaga performa server kamu. Nah, kamu bisa Read more

Tutorial mereset Password MySQL melalui akses Root

Lupa password root MySQL kamu? Gawat!, tapi kamu gak perlu panik, kita akan bahas bareng tutorialnya berikut ini. Eh, tapi Read more