[MVC] Полная миграция на MVC архитектуру

- Создано ядро MVC: App, Router, Controller, Model, View, Database
- Созданы модели: User, Product, Category, Cart, Order
- Созданы контроллеры: Home, Auth, Product, Cart, Order, Page, Admin
- Созданы layouts и partials для представлений
- Добавлены все views для страниц
- Настроена маршрутизация с чистыми URL
- Обновлена конфигурация Docker и Apache для mod_rewrite
- Добавлена единая точка входа public/index.php
This commit is contained in:
kirill.khorkov
2026-01-03 11:48:14 +03:00
parent 3f257120fa
commit d2c15ec37f
53 changed files with 8650 additions and 30 deletions

163
app/Core/App.php Normal file
View File

@@ -0,0 +1,163 @@
<?php
namespace App\Core;
/**
* App - главный класс приложения
*/
class App
{
private Router $router;
private static ?App $instance = null;
public function __construct()
{
self::$instance = $this;
$this->router = new Router();
}
/**
* Получить экземпляр приложения
*/
public static function getInstance(): ?self
{
return self::$instance;
}
/**
* Получить роутер
*/
public function getRouter(): Router
{
return $this->router;
}
/**
* Инициализация приложения
*/
public function init(): self
{
// Запускаем сессию
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// Регистрируем автозагрузчик
$this->registerAutoloader();
// Настраиваем обработку ошибок
$this->setupErrorHandling();
// Загружаем маршруты
$this->loadRoutes();
return $this;
}
/**
* Регистрация автозагрузчика классов
*/
private function registerAutoloader(): void
{
spl_autoload_register(function ($class) {
// Преобразуем namespace в путь к файлу
$prefix = 'App\\';
$baseDir = dirname(__DIR__) . '/';
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
return;
}
$relativeClass = substr($class, $len);
$file = $baseDir . str_replace('\\', '/', $relativeClass) . '.php';
if (file_exists($file)) {
require $file;
}
});
}
/**
* Настройка обработки ошибок
*/
private function setupErrorHandling(): void
{
$config = require dirname(__DIR__, 2) . '/config/app.php';
if ($config['debug'] ?? false) {
error_reporting(E_ALL);
ini_set('display_errors', '1');
} else {
error_reporting(0);
ini_set('display_errors', '0');
}
set_exception_handler(function (\Throwable $e) {
$this->handleException($e);
});
set_error_handler(function ($severity, $message, $file, $line) {
throw new \ErrorException($message, 0, $severity, $file, $line);
});
}
/**
* Обработка исключений
*/
private function handleException(\Throwable $e): void
{
$config = require dirname(__DIR__, 2) . '/config/app.php';
http_response_code(500);
if ($config['debug'] ?? false) {
echo "<h1>Ошибка приложения</h1>";
echo "<p><strong>Сообщение:</strong> " . htmlspecialchars($e->getMessage()) . "</p>";
echo "<p><strong>Файл:</strong> " . htmlspecialchars($e->getFile()) . ":" . $e->getLine() . "</p>";
echo "<pre>" . htmlspecialchars($e->getTraceAsString()) . "</pre>";
} else {
echo View::render('errors/500', [], 'main');
}
}
/**
* Загрузка маршрутов
*/
private function loadRoutes(): void
{
$routesFile = dirname(__DIR__, 2) . '/config/routes.php';
if (file_exists($routesFile)) {
$router = $this->router;
require $routesFile;
}
}
/**
* Запуск приложения
*/
public function run(): void
{
$uri = $_SERVER['REQUEST_URI'] ?? '/';
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
// Удаляем базовый путь, если он есть
$basePath = '/cite_practica';
if (strpos($uri, $basePath) === 0) {
$uri = substr($uri, strlen($basePath));
}
// Если URI пустой, делаем его корневым
if (empty($uri) || $uri === false) {
$uri = '/';
}
try {
$this->router->dispatch($uri, $method);
} catch (\Exception $e) {
$this->handleException($e);
}
}
}