Apa itu MVC Software Design Pattern Pada Laravel

Ketika membangun website, kita tidak hanya memastikan fitur-fitur berfungsi dengan baik, tetapi juga harus memperhatikan agar website mudah diperbesar (scalable) dan dimaintenance (maintainable).

Bayangkan sebuah bangunan yang dirancang dengan baik; saat kita ingin menambah ruangan atau merenovasi, kita dapat melakukannya dengan mudah tanpa harus merobohkan seluruh bangunan. Begitu juga dengan website, struktur kode yang baik memungkinkan kita menambah fitur baru atau memperbaiki bug tanpa mengganggu sistem yang sudah ada.

Pentingnya Kerja Sama Programmer

Dalam tim pengembangan, kerja sama yang baik antara programmer sangat penting. Untuk mencapai hal ini, diperlukan pola kerja yang terstruktur dan terorganisir. Salah satu pola yang dapat membantu adalah Model-View-Controller (MVC) pattern.

MVC pattern membantu programmer bekerja sama lebih efisien dengan membagi tanggung jawab kode ke dalam tiga komponen utama: Model, View, dan Controller.

Apa itu MVC Pattern?

MVC adalah singkatan dari Model-View-Controller, sebuah pola arsitektur yang digunakan untuk mengorganisir kode dalam aplikasi. Untuk memahami konsep ini, kita bisa menggunakan analogi sederhana dari kehidupan sehari-hari.

Bayangkan sebuah restoran:

  • Model: Bagian dapur yang menyiapkan makanan (data).
  • View: Area tempat pelanggan duduk dan melihat menu (antarmuka pengguna).
  • Controller: Pelayan yang mengambil pesanan dari pelanggan dan menyampaikannya ke dapur serta mengembalikan makanan ke pelanggan (logika aplikasi).

Dengan menggunakan MVC, kita memisahkan data (Model), antarmuka pengguna (View), dan logika aplikasi (Controller) sehingga setiap komponen dapat dikembangkan dan diuji secara independen.

Laravel dan MVC Pattern

Laravel adalah salah satu framework PHP yang menggunakan MVC pattern. Berikut adalah tiga alasan mengapa Laravel menggunakan MVC pattern:

  1. Pemeliharaan Kode: Dengan memisahkan komponen-komponen aplikasi, Laravel membuat kode lebih mudah untuk dipelihara dan diperbaiki. Kita dapat memperbaiki bug atau menambah fitur baru tanpa mengganggu komponen lain.
  2. Kolaborasi Tim: MVC memungkinkan anggota tim bekerja pada bagian yang berbeda secara bersamaan. Seorang developer dapat bekerja pada Model, sementara yang lain mengerjakan View, dan yang lain lagi mengembangkan Controller.
  3. Pengujian: Dengan MVC, kita dapat menguji setiap komponen secara terpisah. Misalnya, kita dapat menguji logika aplikasi tanpa harus melibatkan antarmuka pengguna, yang mempermudah proses debugging dan pengembangan fitur baru.

Contoh Penerapan MVC dalam Proyek Pesan Tiket Hotel

Mari kita lihat bagaimana MVC pattern diterapkan dalam proyek pesan tiket hotel dengan fitur cari hotel, booking, dan pembayaran menggunakan Midtrans payment gateway.

1. Model

Model bertanggung jawab untuk mengelola data. Dalam contoh ini, kita memiliki model Hotel dan Booking.

// app/Models/Hotel.php
namespace App\\\\Models;

use Illuminate\\\\Database\\\\Eloquent\\\\Model;

class Hotel extends Model
{
    protected $fillable = ['name', 'location', 'price'];
}

// app/Models/Booking.php
namespace App\\\\Models;

use Illuminate\\\\Database\\\\Eloquent\\\\Model;

class Booking extends Model
{
    protected $fillable = ['hotel_id', 'user_id', 'check_in', 'check_out', 'payment_status'];
}

2. View

View bertanggung jawab untuk menampilkan data kepada pengguna. Dalam Laravel, kita menggunakan Blade templates.

<!-- resources/views/hotels/index.blade.php -->
@extends('layouts.app')

@section('content')
    <h1>Hotels</h1>
    <form method="GET" action="{{ route('hotels.index') }}">
        <input type="text" name="search" placeholder="Search hotels...">
        <button type="submit">Search</button>
    </form>

    @foreach($hotels as $hotel)
        <div>
            <h2>{{ $hotel->name }}</h2>
            <p>{{ $hotel->location }}</p>
            <p>${{ $hotel->price }}</p>
            <a href="{{ route('bookings.create', $hotel->id) }}">Book Now</a>
        </div>
    @endforeach
@endsection

3. Controller

Controller bertanggung jawab untuk logika aplikasi. Berikut adalah contoh controller untuk mencari hotel dan membuat booking.

// app/Http/Controllers/HotelController.php
namespace App\\\\Http\\\\Controllers;

use App\\\\Models\\\\Hotel;
use Illuminate\\\\Http\\\\Request;

class HotelController extends Controller
{
    public function index(Request $request)
    {
        $search = $request->input('search');
        $hotels = Hotel::where('name', 'LIKE', "%$search%")
                       ->orWhere('location', 'LIKE', "%$search%")
                       ->get();

        return view('hotels.index', compact('hotels'));
    }
}

// app/Http/Controllers/BookingController.php
namespace App\\\\Http\\\\Controllers;

use App\\\\Models\\\\Booking;
use App\\\\Models\\\\Hotel;
use Illuminate\\\\Http\\\\Request;
use Midtrans;

class BookingController extends Controller
{
    public function create($hotelId)
    {
        $hotel = Hotel::findOrFail($hotelId);
        return view('bookings.create', compact('hotel'));
    }

    public function store(Request $request)
    {
        $booking = Booking::create([
            'hotel_id' => $request->input('hotel_id'),
            'user_id' => auth()->id(),
            'check_in' => $request->input('check_in'),
            'check_out' => $request->input('check_out'),
            'payment_status' => 'pending',
        ]);

        // Proses pembayaran menggunakan Midtrans
        Midtrans::setConfig([
            'serverKey' => env('MIDTRANS_SERVER_KEY'),
            'isProduction' => false,
        ]);

        $params = [
            'transaction_details' => [
                'order_id' => $booking->id,
                'gross_amount' => $request->input('amount'),
            ],
            'customer_details' => [
                'first_name' => auth()->user()->name,
                'email' => auth()->user()->email,
            ],
        ];

        $snapToken = Midtrans::getSnapToken($params);
        return view('bookings.payment', compact('snapToken', 'booking'));
    }
}

Kesalahan Utama Programmer Pemula dalam Menerapkan MVC Pattern

1. Menggabungkan Logika dalam View

Programmer pemula sering kali memasukkan logika aplikasi dalam view. Seharusnya, logika ini ditempatkan di dalam controller. Hal ini membuat kode sulit dibaca dan sulit untuk di-maintenance. Berikut adalah contoh kesalahan umum:

Contoh Kesalahan:

<!-- resources/views/hotels/index.blade.php -->
@extends('layouts.app')

@section('content')
    <h1>Hotels</h1>
    <form method="GET" action="{{ route('hotels.index') }}">
        <input type="text" name="search" placeholder="Search hotels...">
        <button type="submit">Search</button>
    </form>

    @foreach($hotels as $hotel)
        <div>
            <h2>{{ $hotel->name }}</h2>
            <p>{{ $hotel->location }}</p>
            <p>${{ $hotel->price }}</p>
            @if($hotel->availability > 0)
                <p>Available</p>
            @else
                <p>Not Available</p>
            @endif
        </div>
    @endforeach
@endsection

Pada contoh di atas, logika pengecekan ketersediaan hotel dimasukkan langsung ke dalam view. Sebaiknya, logika ini ditempatkan di controller.

Contoh yang Benar:

// app/Http/Controllers/HotelController.php
namespace App\\\\Http\\\\Controllers;

use App\\\\Models\\\\Hotel;
use Illuminate\\\\Http\\\\Request;

class HotelController extends Controller
{
    public function index(Request $request)
    {
        $search = $request->input('search');
        $hotels = Hotel::where('name', 'LIKE', "%$search%")
                       ->orWhere('location', 'LIKE', "%$search%")
                       ->get()
                       ->map(function ($hotel) {
                           $hotel->availability_status = $hotel->availability > 0 ? 'Available' : 'Not Available';
                           return $hotel;
                       });

        return view('hotels.index', compact('hotels'));
    }
}

<!-- resources/views/hotels/index.blade.php -->
@extends('layouts.app')

@section('content')
    <h1>Hotels</h1>
    <form method="GET" action="{{ route('hotels.index') }}">
        <input type="text" name="search" placeholder="Search hotels...">
        <button type="submit">Search</button>
    </form>

    @foreach($hotels as $hotel)
        <div>
            <h2>{{ $hotel->name }}</h2>
            <p>{{ $hotel->location }}</p>
            <p>${{ $hotel->price }}</p>
            <p>{{ $hotel->availability_status }}</p>
        </div>
    @endforeach
@endsection

2. Model yang Tidak Berhubungan

Programmer pemula sering tidak mendefinisikan relasi antara model dengan benar. Misalnya, dalam proyek pesan tiket hotel, kita perlu mendefinisikan hubungan antara Hotel dan Booking. Relasi yang jelas membantu dalam mengelola data lebih efisien.

Contoh Kesalahan:

// app/Models/Hotel.php
namespace App\\\\Models;

use Illuminate\\\\Database\\\\Eloquent\\\\Model;

class Hotel extends Model
{
    protected $fillable = ['name', 'location', 'price'];
}

// app/Models/Booking.php
namespace App\\\\Models;

use Illuminate\\\\Database\\\\Eloquent\\\\Model;

class Booking extends Model
{
    protected $fillable = ['hotel_id', 'user_id', 'check_in', 'check_out', 'payment_status'];
}

Pada contoh di atas, relasi antara Hotel dan Booking tidak didefinisikan dengan jelas.

Contoh yang Benar:

// app/Models/Hotel.php
namespace App\\\\Models;

use Illuminate\\\\Database\\\\Eloquent\\\\Model;

class Hotel extends Model
{
    protected $fillable = ['name', 'location', 'price'];

    public function bookings()
    {
        return $this->hasMany(Booking::class);
    }
}

// app/Models/Booking.php
namespace App\\\\Models;

use Illuminate\\\\Database\\\\Eloquent\\\\Model;

class Booking extends Model
{
    protected $fillable = ['hotel_id', 'user_id', 'check_in', 'check_out', 'payment_status'];

    public function hotel()
    {
        return $this->belongsTo(Hotel::class);
    }
}

Dengan mendefinisikan relasi ini, kita dapat dengan mudah mengambil data booking terkait dengan sebuah hotel.

3. Tidak Memanfaatkan Middleware

Banyak pemula tidak memanfaatkan middleware untuk menangani logika umum seperti otentikasi dan otorisasi, yang seharusnya dikelola secara terpusat. Middleware membantu memastikan bahwa logika ini diterapkan secara konsisten di seluruh aplikasi.

Contoh Kesalahan:

// app/Http/Controllers/BookingController.php
namespace App\\\\Http\\\\Controllers;

use App\\\\Models\\\\Booking;
use Illuminate\\\\Http\\\\Request;

class BookingController extends Controller
{
    public function create()
    {
        if (!auth()->check()) {
            return redirect('login');
        }

        return view('bookings.create');
    }

    public function store(Request $request)
    {
        if (!auth()->check()) {
            return redirect('login');
        }

        Booking::create([
            'hotel_id' => $request->input('hotel_id'),
            'user_id' => auth()->id(),
            'check_in' => $request->input('check_in'),
            'check_out' => $request->input('check_out'),
            'payment_status' => 'pending',
        ]);

        return redirect()->route('bookings.index');
    }
}

Pada contoh di atas, pengecekan otentikasi dilakukan langsung di controller, yang seharusnya dikelola oleh middleware.

Contoh yang Benar:

// app/Http/Controllers/BookingController.php
namespace App\\\\Http\\\\Controllers;

use App\\\\Models\\\\Booking;
use Illuminate\\\\Http\\\\Request;

class BookingController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
    }

    public function create()
    {
        return view('bookings.create');
    }

    public function store(Request $request)
    {
        Booking::create([
            'hotel_id' => $request->input('hotel_id'),
            'user_id' => auth()->id(),
            'check_in' => $request->input('check_in'),
            'check_out' => $request->input('check_out'),
            'payment_status' => 'pending',
        ]);

        return redirect()->route('bookings.index');
    }
}

Dengan menggunakan middleware, kita memastikan bahwa hanya pengguna yang terotentikasi yang dapat mengakses metode create dan store pada BookingController.

Perbedaan antara MVC dan Service Repository Pattern dalam Framework Laravel

MVC (Model-View-Controller) dan Service Repository Pattern adalah dua pola arsitektur yang dapat digunakan dalam pengembangan aplikasi menggunakan Laravel. Berikut adalah lima perbedaan utama antara keduanya beserta contoh koding untuk memperjelas perbedaan tersebut.

1. Tanggung Jawab Kode

  • MVC: Memisahkan tanggung jawab antara logika aplikasi, tampilan, dan manajemen data.
  • Service Repository Pattern: Menambahkan lapisan tambahan untuk mengelola logika bisnis dan akses data, memisahkan lebih jauh tanggung jawab antara lapisan aplikasi.

Contoh MVC:

// app/Models/Hotel.php
namespace App\\\\Models;

use Illuminate\\\\Database\\\\Eloquent\\\\Model;

class Hotel extends Model
{
    protected $fillable = ['name', 'location', 'price'];
}

// app/Http/Controllers/HotelController.php
namespace App\\\\Http\\\\Controllers;

use App\\\\Models\\\\Hotel;
use Illuminate\\\\Http\\\\Request;

class HotelController extends Controller
{
    public function index()
    {
        $hotels = Hotel::all();
        return view('hotels.index', compact('hotels'));
    }
}

Contoh Service Repository Pattern:

// app/Repositories/HotelRepository.php
namespace App\\\\Repositories;

use App\\\\Models\\\\Hotel;

class HotelRepository
{
    public function getAllHotels()
    {
        return Hotel::all();
    }
}

// app/Services/HotelService.php
namespace App\\\\Services;

use App\\\\Repositories\\\\HotelRepository;

class HotelService
{
    protected $hotelRepository;

    public function __construct(HotelRepository $hotelRepository)
    {
        $this->hotelRepository = $hotelRepository;
    }

    public function listHotels()
    {
        return $this->hotelRepository->getAllHotels();
    }
}

// app/Http/Controllers/HotelController.php
namespace App\\\\Http\\\\Controllers;

use App\\\\Services\\\\HotelService;

class HotelController extends Controller
{
    protected $hotelService;

    public function __construct(HotelService $hotelService)
    {
        $this->hotelService = $hotelService;
    }

    public function index()
    {
        $hotels = $this->hotelService->listHotels();
        return view('hotels.index', compact('hotels'));
    }
}

2. Keterbacaan dan Pemeliharaan Kode

  • MVC: Struktur kode lebih sederhana dan langsung, cocok untuk aplikasi kecil hingga menengah.
  • Service Repository Pattern: Kode lebih terorganisir dan mudah dikelola untuk aplikasi skala besar dengan logika bisnis kompleks.

Contoh MVC:

// Logika bisnis langsung di dalam controller
public function bookHotel(Request $request, $hotelId)
{
    $hotel = Hotel::findOrFail($hotelId);
    $booking = new Booking();
    $booking->hotel_id = $hotel->id;
    $booking->user_id = auth()->id();
    $booking->check_in = $request->input('check_in');
    $booking->check_out = $request->input('check_out');
    $booking->save();
    return redirect()->route('bookings.index');
}

Contoh Service Repository Pattern:

// app/Services/BookingService.php
namespace App\\\\Services;

use App\\\\Repositories\\\\HotelRepository;
use App\\\\Repositories\\\\BookingRepository;
use Illuminate\\\\Support\\\\Facades\\\\Auth;

class BookingService
{
    protected $hotelRepository;
    protected $bookingRepository;

    public function __construct(HotelRepository $hotelRepository, BookingRepository $bookingRepository)
    {
        $this->hotelRepository = $hotelRepository;
        $this->bookingRepository = $bookingRepository;
    }

    public function bookHotel($hotelId, $data)
    {
        $hotel = $this->hotelRepository->find($hotelId);
        $booking = $this->bookingRepository->create([
            'hotel_id' => $hotel->id,
            'user_id' => Auth::id(),
            'check_in' => $data['check_in'],
            'check_out' => $data['check_out'],
        ]);
        return $booking;
    }
}

// app/Http/Controllers/BookingController.php
namespace App\\\\Http\\\\Controllers;

use App\\\\Services\\\\BookingService;
use Illuminate\\\\Http\\\\Request;

class BookingController extends Controller
{
    protected $bookingService;

    public function __construct(BookingService $bookingService)
    {
        $this->bookingService = $bookingService;
    }

    public function bookHotel(Request $request, $hotelId)
    {
        $this->bookingService->bookHotel($hotelId, $request->all());
        return redirect()->route('bookings.index');
    }
}

3. Pengujian

  • MVC: Pengujian unit bisa lebih sulit karena logika bisnis sering tercampur dalam controller dan model.
  • Service Repository Pattern: Memisahkan logika bisnis membuat pengujian lebih mudah dan lebih spesifik.

Contoh Pengujian pada MVC:

// tests/Feature/HotelControllerTest.php
public function testIndex()
{
    $response = $this->get('/hotels');
    $response->assertStatus(200);
    $response->assertViewHas('hotels');
}

Contoh Pengujian pada Service Repository Pattern:

// tests/Unit/HotelServiceTest.php
use App\\\\Services\\\\HotelService;
use App\\\\Repositories\\\\HotelRepository;
use PHPUnit\\\\Framework\\\\TestCase;

class HotelServiceTest extends TestCase
{
    public function testListHotels()
    {
        $hotelRepository = $this->createMock(HotelRepository::class);
        $hotelRepository->method('getAllHotels')->willReturn(collect([]));
        $hotelService = new HotelService($hotelRepository);
        $hotels = $hotelService->listHotels();
        $this->assertEmpty($hotels);
    }
}

4. Keterlibatan Komponen

  • MVC: Langsung melibatkan model, view, dan controller tanpa lapisan tambahan.
  • Service Repository Pattern: Menambah lapisan repository untuk manajemen data dan service untuk logika bisnis.

Contoh MVC:

// Menggunakan model langsung di controller
$hotel = Hotel::findOrFail($hotelId);

Contoh Service Repository Pattern:

// Menggunakan repository untuk akses data
$hotel = $this->hotelRepository->find($hotelId);

5. Scalability

  • MVC: Cocok untuk aplikasi kecil hingga menengah, bisa menjadi sulit dikelola seiring bertambahnya kompleksitas.
  • Service Repository Pattern: Lebih scalable dan maintainable untuk aplikasi besar dengan logika bisnis yang kompleks.

Contoh MVC:

// Struktur sederhana
public function index()
{
    $hotels = Hotel::all();
    return view('hotels.index', compact('hotels'));
}

Contoh Service Repository Pattern:

// Struktur lebih kompleks tapi scalable
public function index()
{
    $hotels = $this->hotelService->listHotels();
    return view('hotels.index', compact('hotels'));
}

10 Kesimpulan yang Dapat Dipelajari dari Artikel Ini

Pemahaman Tanggung Jawab Kode: MVC pattern memisahkan tanggung jawab kode menjadi tiga komponen utama, yaitu Model, View, dan Controller. Ini membantu menjaga keteraturan dan keterbacaan kode.

Struktur Kode yang Terorganisir: Service Repository Pattern menambahkan lapisan tambahan untuk mengelola logika bisnis dan akses data, membuat struktur kode lebih terorganisir dan scalable.

Kemudahan Pemeliharaan Kode: Dengan memisahkan logika aplikasi, tampilan, dan manajemen data, MVC pattern mempermudah pemeliharaan dan pengembangan aplikasi.

Kolaborasi Tim yang Lebih Baik: MVC pattern memungkinkan anggota tim untuk bekerja pada bagian yang berbeda secara bersamaan, meningkatkan efisiensi dan kolaborasi.

Pengujian yang Lebih Mudah: Service Repository Pattern membuat pengujian unit lebih mudah karena logika bisnis dipisahkan dalam service dan repository, memungkinkan pengujian yang lebih spesifik dan terfokus.

Skalabilitas: Service Repository Pattern lebih scalable dan maintainable untuk aplikasi besar dengan logika bisnis yang kompleks, karena pemisahan yang jelas antara logika bisnis dan akses data.

Manajemen Relasi Data: Mendefinisikan relasi antara model dengan benar, seperti hubungan antara Hotel dan Booking, membantu dalam mengelola data lebih efisien dan konsisten.

Pemanfaatan Middleware: Menggunakan middleware untuk logika umum seperti otentikasi dan otorisasi membantu memastikan bahwa logika ini diterapkan secara konsisten di seluruh aplikasi.

Pengurangan Bug: Dengan memisahkan logika bisnis dan manajemen data, MVC dan Service Repository Pattern dapat mengurangi bug dan memudahkan debugging serta pengembangan fitur baru.

Pembelajaran dan Implementasi yang Lebih Baik: Memahami dan mengimplementasikan MVC dan Service Repository Pattern dengan benar sangat penting untuk pengembangan aplikasi yang sukses, serta membantu programmer menjadi lebih terampil dan efisien dalam mengelola kode.

Dengan mempelajari kesimpulan ini, programmer dapat mengembangkan aplikasi yang lebih baik, terstruktur, dan mudah dikelola, serta meningkatkan kolaborasi tim dan efisiensi pengembangan.

Penutup dan Saran

Penerapan MVC pattern dan Service Repository Pattern sangat penting dalam pembangunan aplikasi yang scalable dan maintainable. Kedua pola ini menawarkan keuntungan dalam hal keterbacaan kode, pemeliharaan, dan pengujian, yang semuanya esensial untuk pengembangan aplikasi yang sukses.

Bagi programmer, memahami dan mengimplementasikan pola-pola ini dengan benar adalah langkah penting untuk meningkatkan kualitas dan efisiensi kode.

Saran untuk Programmer

Berikut adalah beberapa saran penting yang bisa diikuti oleh programmer dalam mengimplementasikan MVC dan Service Repository Pattern:

  • Pahami Dasar-Dasar MVC:
    • Pelajari komponen-komponen utama MVC (Model, View, Controller) dan bagaimana mereka bekerja bersama.
    • Cobalah menerapkan MVC pattern dalam proyek kecil untuk memahami cara kerjanya sebelum menggunakannya dalam proyek besar.
  • Implementasikan Service Repository Pattern untuk Proyek Besar:
    • Gunakan Service Repository Pattern untuk proyek dengan logika bisnis yang kompleks.
    • Pisahkan logika bisnis ke dalam service dan repository untuk meningkatkan keterbacaan dan pemeliharaan kode.
  • Manfaatkan Middleware:
    • Gunakan middleware untuk menangani logika umum seperti otentikasi dan otorisasi, sehingga controller tetap bersih dan fokus pada logika bisnis.
  • Fokus pada Keterbacaan Kode:
    • Tuliskan kode yang jelas dan mudah dimengerti oleh anggota tim lain.
    • Hindari menumpuk terlalu banyak logika dalam satu tempat, seperti di controller atau view.
  • Lakukan Pengujian secara Teratur:
    • Buat pengujian unit untuk setiap komponen aplikasi, termasuk model, service, dan repository.
    • Pastikan setiap fitur diuji dengan baik sebelum dirilis.
  • Manfaatkan Relasi Model dengan Benar:
    • Definisikan relasi antara model dengan benar untuk memudahkan pengelolaan data.
    • Gunakan Eloquent ORM Laravel untuk memanfaatkan fitur-fitur relasi yang disediakan.
  • Gunakan Tools dan Resource yang Tepat:
    • Manfaatkan tools dan resource yang tepat untuk belajar dan mengimplementasikan MVC dan Service Repository Pattern, seperti tutorial online, dokumentasi, dan komunitas programmer.
  • Belajar dari Mentor Expert:
    • Belajar dari mentor yang berpengalaman, seperti yang tersedia di BuildWithAngga, untuk mendapatkan bimbingan langsung dan tips praktis.
    • Manfaatkan benefit yang ditawarkan, seperti akses seumur hidup, konsultasi mentor, dan berbagai keuntungan lainnya.

Dengan mengikuti saran-saran ini, programmer dapat meningkatkan kemampuan mereka dalam mengembangkan aplikasi yang lebih baik dan terstruktur, serta memaksimalkan potensi kolaborasi dalam tim pengembangan. Implementasi yang baik dari MVC dan Service Repository Pattern akan membantu dalam menciptakan kode yang lebih rapi, mudah dikelola, dan scalable.