MVC Lesson 7: Core principles of custom MVC in modern PHP

Here’s a concise guide covering the core principles, design patterns, and process to get you started.

๐ŸŒ What is MVC?

MVC stands for Model-View-Controller:

  • Model: Handles business logic and database interaction.
  • View: Manages the presentation layer (HTML, templates).
  • Controller: Handles user input, communicates between Model and View.

๐Ÿ”‘ Core Principles

  1. Separation of Concerns: Keep logic, presentation, and data access separate.
  2. Routing: Map URLs to controller actions.
  3. Autoloading (PSR-4): Load classes automatically using namespaces.
  4. Dependency Injection: Avoid hard-coded dependencies; use constructor or container.
  5. Single Responsibility Principle (SOLID): Each class has one job.
  6. Security: Sanitize inputs, use prepared statements, CSRF protection, etc.
  7. Templating: Avoid mixing PHP logic with HTML (use template engines like Twig or Blade).
  8. Modern PHP Standards: Use PHP 8+, type hints, and attributes.

๐Ÿ—๏ธ Basic Folder Structure

/your-app
โ”‚
โ”œโ”€โ”€ /app
โ”‚   โ”œโ”€โ”€ /Controllers
โ”‚   โ”œโ”€โ”€ /Models
โ”‚   โ”œโ”€โ”€ /Views
โ”‚   โ”œโ”€โ”€ /Core         <- Router, Base Controller, etc.
โ”‚
โ”œโ”€โ”€ /public
โ”‚   โ””โ”€โ”€ index.php     <- Entry point (Front Controller)
โ”‚
โ”œโ”€โ”€ /config
โ”œโ”€โ”€ /routes
โ””โ”€โ”€ composer.json

๐Ÿ› ๏ธ Step-by-Step Setup

1. Set Up Entry Point (public/index.php)

require_once __DIR__ . '/../vendor/autoload.php';

use App\Core\Router;

$router = new Router();
$router->dispatch($_SERVER['REQUEST_URI']);

2. Router (app/Core/Router.php)

  • Parse URL
  • Match with defined routes
  • Load controller & call method
namespace App\Core;

class Router {
    public function dispatch($uri) {
        // parse URI, match routes, call controller
    }
}

3. Controller Example

namespace App\Controllers;

use App\Core\Controller;

class HomeController extends Controller {
    public function index() {
        $this->view('home/index', ['title' => 'Home']);
    }
}

4. View Loader (Controller.php Base Class)

public function view($view, $data = []) {
    extract($data);
    require "../app/Views/$view.php";
}

5. Model Example

namespace App\Models;

use PDO;

class User {
    public static function all() {
        // return all users using PDO
    }
}

6. Autoloading with Composer

  • composer.json
{
  "autoload": {
    "psr-4": {
      "App\\": "app/"
    }
  }
}

Then run:

composer dump-autoload

โœ… Modern Enhancements

  • Use dotenv for config (vlucas/phpdotenv)
  • Use a DI container (like PHP-DI)
  • Implement Middleware for auth, logging
  • Use PDO with prepared statements or ORM like Eloquent
  • Add unit testing (PHPUnit)
  • Use Twig or Blade templating
  • Add error handling and logging

๐Ÿงญ Next Steps

  1. Set up a basic working app with routes, controller, and view.
  2. Expand the router to handle parameters and method-based routing.
  3. Connect models with a database using PDO or ORM.
  4. Add templating, error handling, and helpers.
  5. Gradually modularize (middlewares, services, auth, etc.)