onPHP / onphp-framework

onPHP is the mature GPL'ed multi-purpose object-oriented PHP framework.
85 stars 52 forks source link

ModelAndView as HttpResponse #194

Open DanielPlainview opened 11 years ago

DanielPlainview commented 11 years ago

Позволяет делать редирект, выставлять заголовки/cookies более православно.

class HelloWorldController extends HttpController
{
    public function handleRequest(HttpRequest $request)
    {
        // ...

        if ($user->isLocked())
            return $this->createForbiddenResponse();

        $response =
            RawResponse::create()->
                setContent(sprintf('Hello, %s!', $user->getName()))->
        ;

        $response->getHeaderCollection()->set('Content-Type', 'text/plain');

        return $response;
    }
}

Во front controller будет что-то типа

try {
    $response = $controller->handleRequest($request);
} catch (Exception $e) {
    $logger->logException($e);
    $response = new ...;
}

$response->render();
AlexeyDsov commented 11 years ago

Я уже написал комментарий в коде что сейчас ModelAndView это просто контейнер. Ну можно добавить в него ещё переменных которые будут возвращаться из контроллера и это будет дельно. Но заставлять его render'ить уже лишнее. Подобную логику стоит держать в отдельном классе обработавающим ModelAndView.

DanielPlainview commented 11 years ago

Но заставлять его render'ить уже лишнее.

Спорно. ModelAndView вполне может делать render сам, чтобы соблюдать tell don't ask. В OnPHP вообще есть что-то типа FrontController'а? Иначе получается, что у многих будет повторение вида

$view = $mav->getView();
$headerCollection = $mav->getHeaderCollection();
$cookieCollection = $mav->getCookieCollection();
$status = $mav->getStatus();

if (is_string($view)) {
    $view = ...;
}

header($status->toString());
// send $headerCollection
$cookieCollection->httpSetAll();
echo $view->render($mav->getModel());
AlexeyDsov commented 11 years ago

Мне кажется странным использовать ModelAndView для писем.

Ну MaV сейчас ни для чего не используется кроме передачи model и view. Странным может казаться любое его использование.

DanielPlainview commented 11 years ago

Так ведь BC это не ломает, а странность его использования только подтолкнёт к рефакторингу.

Надо подумать по поводу FrontController.

dovg commented 11 years ago

Я положительно отношусь к необходимости где-то (не во вью) аккумулировать заголовки и все остальное, что связано с http, но ИМХО ModelAndView не совсем удачное место для этого.

Вот например, у меня сейчас в проекте почти везде два представления - html или json поэтому заголовки в MaV мне не нужны, ибо они разные будут, и только фронт-контроллер знает какие отдавать. А контроллеру приложения пофиг, он свою работу выполнил - данные собрал, объекты обновил.

Более того, есть случаи, когда контекст http вообще отсутствует, при этом стандартная MVC вполне может быть.

DanielPlainview commented 11 years ago

Вот например, у меня сейчас в проекте почти везде два представления - html или json поэтому заголовки в MaV мне не нужны

А откуда заголовки будут браться?

Более того, есть случаи, когда контекст http вообще отсутствует, при этом стандартная MVC вполне может быть.

Я не вижу реального кейса. Сейчас у нас есть HttpRequest, но внезапно нет HttpResponse. Я могу сделать отдельный класс, но это потребует существенной переработки существующего кода в проекте. Везде ожидается ModelAndView и с этим надо как-то жить.

фронт-контроллер знает какие отдавать

Не во FrontController, допустим. А в каком-то фильтре. Причём мне это видится как-то так:

class UserController implements Controller
{
    public function handleRequest(HttpRequest $request)
    {
        $user = User::dao()->getById($request->getGetVar('id'));

        return
            ModelAndView::create()->
                setView('viewUser')->
                setModel(
                    Model::create()->set('user', $user)
                );
    }
}

class ResponseFormatFilter extends DecoratorController
{
    private $view;

    public function __construct(CustomView $view)
    {
        $this->view = $view;
    }

    public function handleRequest(HttpRequest $request)
    {
        $response = parent::handleRequest($request);

        $format = $request->hasAttachedVar('format'); // Enumeration (HTML, JSON, XML, Protobuf, etc.)

        if (!$format->isHtml()) {
            $response->getHeaderCollection()->set('Content-Type', $format->getContentType());
            $response->setView($this->view->setFormat($format));
        }

        return $response;
    }
}

class CustomView implements View
{
    private $serializer;
    private $format;

    public function __construct(JMS\Serializer $serializer)
    {
        $this->serializer = $serializer;
    }

    public function setFormat(Format $format)
    {
        $this->format = $format;

        return $this;
    }

    public function render(Model $model)
    {
        echo $this->serializer($model->toArray(), $this->format->getName());

        return $this;
    }
}

GET /42.user.html => ...<span class="name">Blah</span>... GET /42.user.json => {"user": {"id": 42, "name": "Blah"}} GET /42.user.pb => бинарый protobuf response

DanielPlainview commented 11 years ago

Кстати, а для ObjectNotFoundException можно запилить такой фильтр:

class NotFoundResourcesFilter extends DecoratorController
{
    private $logger;

    public function __construct(Logger $logger)
    {
        $this->logger = $logger;
    }

    public function handleRequest(HttpRequest $request)
    {
        try {
            $response = parent::handleRequest($request);
        } catch (ObjectNotFoundException $e) {
            $this->logger->logException($e);
            $response = $this->createNotFoundResponse();
        }

        return $response;
    }
}

Тогда в коде выше проверка наличия юзера не нужна и будет вполне RESTful.

AlexeyDsov commented 11 years ago

Примеры выше это примеры лишь одного проекта. В других проектах другие решения. То что тут делает ResponseFormatFilter, в других местах делают другие классы подругому.

stev commented 11 years ago

Идея поместить в MAV данные о заголовках, сомнительна

Может в мав помещать view а в в него уже заголовки ?