Сталкивался ли с паттерном MVP?

«Сталкивался ли с паттерном MVP?» — вопрос из категории Паттерны, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, применял паттерн Model-View-Presenter (MVP) в веб-приложениях на PHP, особенно в тех случаях, где требовалось четкое разделение логики отображения и бизнес-логики для улучшения тестируемости.

Ключевое отличие от MVC: В классическом MVC View часто "знает" о Model и может напрямую запрашивать данные. В MVP View пассивна — она только отображает то, что ей передает Presenter, и делегирует ему все события пользователя.

Пример реализации простого модуля аутентификации:

// Model (Домен + Логика данных)
class AuthModel {
    private UserRepository $userRepository;

    public function authenticate(string $email, string $password): ?User {
        $user = $this->userRepository->findByEmail($email);
        if ($user && password_verify($password, $user->getPasswordHash())) {
            return $user;
        }
        return null;
    }
}

// View (Интерфейс, реализуется шаблоном)
interface AuthView {
    public function showLoginForm(array $data = []): void;
    public function showError(string $message): void;
    public function redirectToDashboard(User $user): void;
}

// Presenter (Посредник, содержит логику отображения)
class AuthPresenter {
    private AuthModel $model;
    private AuthView $view;

    public function __construct(AuthModel $model, AuthView $view) {
        $this->model = $model;
        $this->view = $view;
    }

    public function showLogin(): void {
        $this->view->showLoginForm();
    }

    public function handleLogin(Request $request): void {
        $email = $request->get('email');
        $password = $request->get('password');

        if (empty($email) || empty($password)) {
            $this->view->showError('Все поля обязательны');
            return;
        }

        $user = $this->model->authenticate($email, $password);

        if ($user) {
            $this->view->redirectToDashboard($user);
        } else {
            $this->view->showError('Неверные учетные данные');
        }
    }
}

// Реализация View (например, в шаблоне Twig или Blade)
class TwigAuthView implements AuthView {
    private Environment $twig;
    private Response $response;

    public function showLoginForm(array $data = []): void {
        $this->response->setContent($this->twig->render('login.html.twig', $data));
    }
    // ... остальные методы
}

Где это было полезно:

  • В сложных административных панелях, где много интерактивных форм и валидации. Presenter обрабатывал всю логику UI, что делало его идеальным для модульного тестирования.
  • При интеграции PHP-бэкенда с различными фронтендами (например, REST API для мобильного приложения и HTML для веба). Можно было создать два разных View-адаптера для одного Presenter'а.

Основной плюс: Presenter, содержащий всю логику отображения и обработки ввода, становится очень удобным для unit-тестирования без необходимости рендеринга HTML.