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.)