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

Ответ

Да, применял паттерн 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.

Ответ 18+ 🔞

А, MVP, говоришь? Ну да, применял, конечно. Это ж классика, ёпта. Особенно когда проект начинает разрастаться, как опухоль, и в твоём контроллере уже не бизнес-логика, а пиздопроебина полная — вот тут и вспоминаешь про чёткое разделение обязанностей.

Смотри, в чём главный прикол, чтобы не охуеть сразу. В том же MVC вьюха частенько лезет в модель сама, как голодный в холодильник ночью. А в MVP — нет, блядь. Вьюха тут — пассивная мартышлюшка. Она просто показывает то, что ей 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('Неверные учетные данные');
        }
    }
}

// А это уже реализация вьюхи, например, на Twig
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));
    }
    // ... остальное
}

Где эта штука реально выручала? Ну, например, в этих ебуч