Паттерн MVC и жизненный цикл запроса
MVC — это способ разделить приложение на три слоя: данные, логику и внешний вид — чтобы код оставался понятным даже когда проект разрастается.
Суть: Model отвечает за данные и базу, View — за то, что видит пользователь, Controller связывает их вместе и принимает решения. Запрос идёт по строго заданному пути.
Когда приложение маленькое, можно свалить весь код в один файл. Но как только страниц становится десятки, такой проект превращается в кашу: SQL-запросы перемешаны с HTML, бизнес-логика — с версткой, и любая правка грозит всё сломать. Паттерн MVC (Model-View-Controller) решает эту проблему, раскладывая код по трём чётким полкам.
Идея проста. Контроллер — это диспетчер: он получает запрос, решает, что делать, обращается к моделям за данными и выбирает, какую страницу показать. Модель знает всё о данных: как достать товары из базы, как их сохранить, какие у них правила. View (представление) — это шаблон, который превращает данные в готовый HTML. Каждый слой занимается своим делом и не лезет в чужой.
Как работает под капотом: путь запроса
Любой запрос проходит один и тот же конвейер. Понимание этого пути — фундамент для отладки и осмысленной работы с фреймворком.
ЖИЗНЕННЫЙ ЦИКЛ ЗАПРОСА В LARAVEL
HTTP-запрос
|
v
public/index.php (единая точка входа)
|
v
bootstrap/app.php (сборка приложения)
|
v
middleware (вход) (сессии, CSRF, auth)
|
v
Router (какой маршрут совпал?)
|
v
Controller (выполняет логику)
|
+--> Model --> База данных
|
v
View (Blade) (данные -> HTML)
|
v
middleware (выход) (правка ответа)
|
v
HTTP-ответ -> Браузер
Обратите внимание: middleware пройдёт дважды — на входе (до контроллера) и на выходе (после). Это «слои фильтров», через которые проходит каждый запрос. О них будет отдельный урок.
Разбор на примере
Допустим, пользователь открывает страницу профиля. Маршрут передаёт запрос в контроллер, контроллер просит модель достать пользователя, а потом отдаёт его в шаблон:
<?php
// Controller: диспетчер
class ProfileController extends Controller
{
public function show(int $id)
{
// 1. Просим Model достать данные
$user = User::findOrFail($id);
// 2. Передаём данные во View
return view('profile.show', ['user' => $user]);
}
}Здесь видно разделение: контроллер не пишет SQL (это дело модели User) и не пишет HTML (это дело шаблона profile.show). Он только координирует. Смоделируем сам конвейер MVC на Python — как запрос последовательно проходит слои.
Попробуй сам ▶
# Мини-модель MVC: запрос проходит через слои
def model(user_id): # MODEL: данные
db = {1: 'Анна', 2: 'Борис'}
return {'id': user_id, 'name': db.get(user_id, 'неизвестно')}
def view(data): # VIEW: внешний вид
return f"<h1>Профиль: {data['name']}</h1>"
def controller(user_id): # CONTROLLER: координатор
user = model(user_id) # просим данные у модели
return view(user) # отдаём их во view
print('Запрос /profile/1')
print('Ответ:', controller(1))
print('Ответ:', controller(2))
Частые ошибки
- «Толстые» контроллеры. Когда вся логика и SQL свалены в контроллер — это нарушение MVC. Бизнес-логика принадлежит моделям и сервисам.
- SQL в шаблоне. View должен только отображать готовые данные, а не лезть в базу.
- HTML в контроллере. Верстку собирает Blade, контроллер только передаёт данные.
Best practices
- Держите контроллеры тонкими: получил запрос, вызвал модель/сервис, вернул view.
- Сложную бизнес-логику выносите в отдельные классы-сервисы или методы моделей.
- Думайте о каждом слое отдельно — это упрощает тесты и поддержку.
Полезно понимать роль сервис-провайдеров в жизненном цикле — это «настройщики» приложения. На этапе загрузки, ещё до маршрутизации, Laravel проходит по всем зарегистрированным провайдерам и вызывает их методы register() (привязка сервисов в контейнер) и boot() (финальная настройка). Именно здесь подключаются ваши сервисы, наблюдатели событий, политики. Сердце этого механизма — контейнер зависимостей (service container): когда контроллеру в конструкторе или методе нужен какой-то класс, контейнер сам его создаёт и подставляет. Это и есть внедрение зависимостей (dependency injection), благодаря которому код слабо связан и легко тестируется. Понимание того, что приложение сначала собирается провайдерами, а уже потом обрабатывает запрос, помогает осознанно расширять Laravel и не дублировать инициализацию в каждом контроллере.
Итог: MVC раскладывает приложение на данные, логику и вид, а жизненный цикл запроса проводит каждый HTTP-запрос по предсказуемому конвейеру. Зная этот путь, вы понимаете, где искать любую часть кода.