首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 實作 MVC 架構:路由分發、模板渲染、資料存取層與現代化改良

     編輯:PHP 相關知識

在 PHP 中落地 MVC,核心在於建立清晰的請求處理管線:前端控制器 → 路由分發 → 控制器動作 → 服務層/資料層 → 視圖渲染 → 回應。本文從零構建一個極簡 MVC 骨架,展示核心組件實作。

目錄結構

public/
  index.php          # 前端控制器 (唯一入口)
  .htaccess          # URL 重寫
src/
  Core/
    Application.php  # 應用核心、服務容器
    Router.php       # 路由分發
    Controller.php   # 基礎控制器
    View.php         # 視圖渲染
    Database.php     # PDO 封裝
  Controllers/
    HomeController.php
    UserController.php
  Models/
    User.php         # 領域模型/實體
  Repositories/
    UserRepository.php
  Views/
    home.php
    user/
      show.php
      index.php
config/
  routes.php         # 路由定義
  database.php       # DB 配置

前端控制器 (public/index.php)

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

use App\Core\Application;

$app = new Application([
    'routes' => require __DIR__ . '/../config/routes.php',
    'db' => require __DIR__ . '/../config/database.php',
]);

$app->run();
?>

路由分發器 (Core/Router.php)

<?php
namespace App\Core;

class Router {
    private array $routes = [];
    
    public function __construct(array $routes) { $this->routes = $routes; }
    
    public function dispatch(string $method, string $uri): void {
        $uri = parse_url($uri, PHP_URL_PATH);
        $routeKey = "$method:$uri";
        
        // 靜態路由優先
        if (isset($this->routes[$routeKey])) {
            $this->execute($this->routes[$routeKey]);
            return;
        }
        
        // 動態路由匹配 (簡易版)
        foreach ($this->routes as $pattern => $handler) {
            if ($this->matchRoute($pattern, $uri, $method)) {
                $this->execute($handler, $this->extractParams($pattern, $uri));
                return;
            }
        }
        
        http_response_code(404);
        echo "404 Not Found";
    }
    
    private function matchRoute(string $pattern, string $uri, string $method): bool {
        $pattern = preg_replace('/\{(\w+)\}/', '([^/]+)', $pattern);
        $pattern = "^" . $pattern . "$";
        return (bool) preg_match("#$pattern#", $uri) && str_starts_with($pattern, $method . ':');
    }
    
    private function extractParams(string $pattern, string $uri): array {
        preg_match_all('/\{(\w+)\}/', $pattern, $keys);
        $pattern = preg_replace('/\{(\w+)\}/', '([^/]+)', $pattern);
        preg_match("#^$pattern$#", $uri, $matches);
        array_shift($matches);
        return array_combine($keys[1], $matches);
    }
    
    private function execute(array $handler, array $params = []): void {
        [$controllerClass, $method] = $handler;
        $controller = new $controllerClass();
        call_user_func_array([$controller, $method], $params);
    }
}
?>

基礎控制器與視圖 (Core/Controller.php, Core/View.php)

<?php
// Core/Controller.php
namespace App\Core;

class Controller {
    protected function view(string $template, array $data = []): void {
        View::render($template, $data);
    }
    
    protected function json(array $data, int $status = 200): void {
        http_response_code($status);
        header('Content-Type: application/json; charset=UTF-8');
        echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    }
}

// Core/View.php
namespace App\Core;

class View {
    public static function render(string $template, array $data = []): void {
        extract($data); // 變數解壓到當前作用域
        $file = __DIR__ . "/../../Views/$template.php";
        if (!is_file($file)) throw new Exception("View not found: $template");
        include $file;
    }
}
?>

資料存取層 (Repositories/UserRepository.php)

<?php
namespace App\Repositories;

use App\Core\Database;
use App\Models\User;

class UserRepository {
    public function __construct(private Database $db) {}
    
    public function findById(int $id): ?User {
        $row = $db->fetchOne('SELECT * FROM users WHERE id = ?', [$id]);
        return $row ? $this->hydrate($row) : null;
    }
    
    public function findAll(int $limit = 20, int $offset = 0): array {
        $rows = $db->fetchAll('SELECT * FROM users ORDER BY created_at DESC LIMIT ? OFFSET ?', [$limit, $offset]);
        return array_map([$this, 'hydrate'], $rows);
    }
    
    public function create(array $data): int {
        return $db->insert('users', $data);
    }
    
    private function hydrate(array $row): User {
        return new User(
            $row['id'],
            $row['name'],
            $row['email'],
            new DateTime($row['created_at'])
        );
    }
}
?>

控制器實作 (Controllers/UserController.php)

<?php
namespace App\Controllers;

use App\Core\Controller;
use App\Repositories\UserRepository;

class UserController extends Controller {
    public function index(UserRepository $users): void {
        $page = max(1, (int)($_GET['page'] ?? 1));
        $userList = $users->findAll(15, ($page - 1) * 15);
        $this->view('user/index', ['users' => $userList, 'page' => $page]);
    }
    
    public function show(UserRepository $users, int $id): void {
        $user = $users->findById($id);
        if (!$user) { http_response_code(404); echo 'User not found'; return; }
        $this->view('user/show', ['user' => $user]);
    }
    
    public function store(UserRepository $users): void {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); return; }
        $name = trim($_POST['name'] ?? '');
        $email = trim($_POST['email'] ?? '');
        if ($name && filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $id = $users->create(['name' => $name, 'email' => $email]);
            header("Location: /users/$id");
        } else {
            $this->view('user/create', ['errors' => ['name' => '必填', 'email' => '格式錯誤']]);
        }
    }
}
?>

路由定義 (config/routes.php)

<?php
return [
    'GET:/' => [\App\Controllers\HomeController::class, 'index'],
    'GET:/users' => [\App\Controllers\UserController::class, 'index'],
    'GET:/users/{id:\d+}' => [\App\Controllers\UserController::class, 'show'],
    'GET:/users/create' => [\App\Controllers\UserController::class, 'create'],
    'POST:/users' => [\App\Controllers\UserController::class, 'store'],
];
?>

現代化改良方向

  1. 依賴注入容器:自動解析控制器建構子依賴 (PHP-DI/Symfony DI)
  2. 中介軟體管線:認證、CSRF、速率限制、日誌 (PSR-15)
  3. 表單請求驗證:專用 FormRequest 類別、自動重導向帶錯誤
  4. 資源轉換器:API 統一序列化 (Fractal/League/Spatiie)
  5. 異常處理器:統一錯誤頁面/JSON 回應、日誌記錄
  6. 模板引擎:Twig/Blade (繼承、區塊、沙箱、快取)

這個骨架雖簡陋,但包含 MVC 核心循環。理解其運作,才能駕馭 Laravel/Symfony 等全功能框架。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved