app/Controllers/Models/Views/public/index.phproutes/web.phpMVC memisahkan penerimaan request, aturan aplikasi, dan tampilan. Tujuannya bukan membuat folder terlihat rapi, tetapi mencegah SQL, HTML, dan logika bisnis bercampur dalam satu file.
Front controller sebagai pintu masuk
<?php
// public/index.php
require dirname(__DIR__).'/vendor/autoload.php';
$container = require dirname(__DIR__).'/bootstrap.php';
$router = $container->get(Router::class);
$response = $router->dispatch($_SERVER['REQUEST_METHOD'], parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
$response->send();Router memetakan request ke controller
$router->get('/products', [ProductController::class, 'index']);
$router->post('/products', [ProductController::class, 'store']);
$router->get('/products/{id}', [ProductController::class, 'show']);Controller tetap tipis
final class ProductController
{
public function __construct(private ProductService $products) {}
public function store(Request $request): Response
{
$input = CreateProductData::fromArray($request->all());
$product = $this->products->create($input);
return Response::redirect('/products/'.$product->id);
}
}Validasi bentuk input dapat dilakukan oleh data object, sedangkan aturan bisnis berada di service/domain. Controller hanya menerjemahkan HTTP ke operasi aplikasi.
Model bukan sinonim tabel
Model mewakili data dan aturan domain. Akses database dapat dipisahkan ke repository agar model tidak bergantung langsung pada PDO.
final class PdoProductRepository implements ProductRepository
{
public function find(int $id): ?Product
{
$stmt = $this->pdo->prepare('SELECT * FROM products WHERE id = :id');
$stmt->execute(['id' => $id]);
$row = $stmt->fetch();
return $row ? Product::fromRow($row) : null;
}
}Urutan implementasi yang efektif
- 1Bangun Request dan Response kecil.
- 2Tambahkan router dengan parameter URL.
- 3Buat satu feature end-to-end: route, controller, service, repository, view.
- 4Tambahkan error handler dan middleware.
- 5Tulis integration test sebelum menambah feature berikutnya.
