Контроллер форума в PHP: основы MVC и варианты реализации
Архитектура контроллера форума в PHP MVC
Как реализовать базовый контроллер для форума с методами просмотра разделов и тем?
В классической MVC контроллер принимает запрос, взаимодействует с моделью и передает данные представлению. Для форума контроллер может содержать методы: index (список разделов), viewSection (темы раздела), viewTopic (сообщения темы), createTopic (создание новой темы) и другие.
class ForumController {
private $db;
public function __construct($db) {
$this->db = $db;
}
public function index() {
$sections = $this->db->query("SELECT * FROM sections");
require 'views/forum/index.php';
}
public function viewSection($id) {
$section = $this->db->query("SELECT * FROM sections WHERE id = " . intval($id));
$topics = $this->db->query("SELECT * FROM topics WHERE section_id = " . intval($id));
require 'views/forum/section.php';
}
public function viewTopic($id) {
$topic = $this->db->query("SELECT * FROM topics WHERE id = " . intval($id));
$posts = $this->db->query("SELECT * FROM posts WHERE topic_id = " . intval($id));
require 'views/forum/topic.php';
}
}Index php app forums controller (контроллер форума в php)
Типичные проблемы: прямое использование $this->db без защиты от инъекций (в примере применяется intval, но не всегда). Отсутствие обработки случаев, когда раздел или тема не найдены. Нет проверки прав доступа. Жесткая привязка к представлению.
Как организовать гибкую маршрутизацию для URL форума?
Использование маршрутизатора (роутера) позволяет отвязать URL от файловой структуры. Пример роутера:
class Router {
private $routes = [];
public function add($pattern, $handler) {
$this->routes[$pattern] = $handler;
}
public function dispatch($uri) {
foreach ($this->routes as $pattern => $handler) {
if (preg_match($pattern, $uri, $matches)) {
return call_user_func_array($handler, array_slice($matches, 1));
}
}
throw new Exception('Route not found');
}
}
// Использование:
$router = new Router();
$router->add('#^/forum/section/(\d+)$#', function($id) use ($controller) {
$controller->viewSection($id);
});
$router->add('#^/forum/topic/(\d+)$#', function($id) use ($controller) {
$controller->viewTopic($id);
});Model index php (модель в php (mvc))
Возможные ошибки: порядок маршрутов важен (первое совпадение). Некорректные регулярные выражения приводят к сбоям. Отсутствие валидации параметров.
Как вынести бизнес-логику из контроллера форума?
Применение сервисного слоя (Service Layer). Контроллер делегирует операции сервисам, а сервисы работают с моделями.
class ForumService {
private $db;
public function __construct($db) { $this->db = $db; }
public function getSections() { /* логика */ }
public function createTopic($userId, $sectionId, $title, $content) { /* логика */ }
}
// В контроллере:
$forumService = new ForumService($db);
$this->forumService = $forumService;
public function createTopic($request) {
$result = $this->forumService->createTopic($request->userId, $request->sectionId, ...);
// редирект или ответ
}Php controllers index (индексный контроллер php)
Проблема: увеличение числа классов, усложнение тестирования, если не использовать внедрение зависимостей.
Как добавить аутентификацию и права доступа к действиям форума?
Middleware фильтры выполняются до контроллера. Например, проверка, залогинен ли пользователь.
class AuthMiddleware {
public function handle($request, $next) {
if (!isset($_SESSION['user'])) {
header('Location: /login');
exit;
}
return $next($request);
}
}
// Применение в роутере:
$router->add('#^/forum/create-topic$#', [new AuthMiddleware(), 'handle'], function() use ($controller) {
$controller->createTopic();
});Ошибки: неправильная передача управления между middleware, проблемы с сессиями на REST API. Рекомендуется использовать готовые библиотеки.
Расширенные примеры реализации контроллера форума
Как построить полный каркас приложения форума с использованием фронт-контроллера, роутера, контроллера и модели?
Рассмотрим полный пример, когда запросы обрабатываются единой точкой входа index.php, маршруты настраиваются в роутере, контроллер использует модель для работы с БД, а представления подключаются для вывода HTML.
// index.php
spl_autoload_register(function ($class) {
include 'classes/' . $class . '.php';
});
$router = new Router();
$model = new ForumModel($pdo);
$controller = new ForumController($model);
$router->add('#^/forum$#', [$controller, 'index']);
$router->add('#^/forum/section/(\d+)$#', [$controller, 'viewSection']);
$router->add('#^/forum/topic/(\d+)$#', [$controller, 'viewTopic']);
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
try {
$router->dispatch($uri);
} catch (Exception $e) {
http_response_code(404);
echo 'Страница не найдена';
}// ForumController.php
class ForumController {
private $model;
public function __construct($model) {
$this->model = $model;
}
public function index() {
$sections = $this->model->getAllSections();
include 'views/forum/index.php';
}
public function viewSection($id) {
$section = $this->model->getSectionById($id);
if (!$section) {
http_response_code(404);
echo 'Раздел не найден';
return;
}
$topics = $this->model->getTopicsBySection($id);
include 'views/forum/section.php';
}
public function viewTopic($id) {
$topic = $this->model->getTopicById($id);
if (!$topic) {
http_response_code(404);
echo 'Тема не найдена';
return;
}
$posts = $this->model->getPostsByTopic($id);
include 'views/forum/topic.php';
}
}// ForumModel.php
class ForumModel {
private $pdo;
public function __construct($pdo) {
$this->pdo = $pdo;
}
public function getAllSections() {
$stmt = $this->pdo->query('SELECT * FROM sections');
return $stmt->fetchAll();
}
public function getSectionById($id) {
$stmt = $this->pdo->prepare('SELECT * FROM sections WHERE id = ?');
$stmt->execute([$id]);
return $stmt->fetch();
}
public function getTopicsBySection($sectionId) {
$stmt = $this->pdo->prepare('SELECT * FROM topics WHERE section_id = ?');
$stmt->execute([$sectionId]);
return $stmt->fetchAll();
}
public function getTopicById($id) {
$stmt = $this->pdo->prepare('SELECT * FROM topics WHERE id = ?');
$stmt->execute([$id]);
return $stmt->fetch();
}
public function getPostsByTopic($topicId) {
$stmt = $this->pdo->prepare('SELECT * FROM posts WHERE topic_id = ?');
$stmt->execute([$topicId]);
return $stmt->fetchAll();
}
}
Разделы форума
<? foreach ($sections as $section): ?>
- <?= htmlspecialchars($section['title']) ?>
<? endforeach; ?>
Результат работы: при переходе по адресу /forum отображается список разделов, каждый раздел является ссылкой на /forum/section/ID, где выводятся темы раздела. Модель использует подготовленные запросы PDO, что защищает от SQL-инъекций.
Список разделов форума: - Общий раздел - Техническая поддержка - Предложения
Как реализовать REST API для форума с выводом данных в JSON?
Для API контроллер возвращает JSON вместо HTML, часто используется отдельный namespace или префикс URL.
class ApiForumController {
private $model;
public function __construct($model) { $this->model = $model; }
public function getSections() {
header('Content-Type: application/json');
$sections = $this->model->getAllSections();
echo json_encode($sections);
}
public function getTopicsBySection($id) {
header('Content-Type: application/json');
$topics = $this->model->getTopicsBySection($id);
echo json_encode($topics);
}
}
// В роутере добавляем:
$apiController = new ApiForumController($model);
$router->add('#^/api/forum/sections$#', [$apiController, 'getSections']);
$router->add('#^/api/forum/section/(\d+)/topics$#', [$apiController, 'getTopicsBySection']);{"getSections": [{"id":1,"title":"Общий раздел"},{"id":2,"title":"Техническая поддержка"}]}При обращении к /api/forum/sections возвращается JSON со списком разделов. Это позволяет строить SPA или мобильные приложения. Для защиты API можно добавить аутентификацию через токены.