Здравствуйте, разлюбезные мои хабровчане. А ежели кто из вас веб-разработкой на PHP занимается, то и вообще добро пожаловать. Я тут давеча стал смотреть на разные фреймворки… Развелось их у нас в ПеХаПе — страсть. Яблоку негде упасть, как говорят блондинки.



Вспомнилось тут, что когда-то давно (когда DOM-деревья были большими, а юникод однобайтовым) грешным делом внес махонький коммит в Zend Framework (тогда еще только 1-я версия была). Дай думаю, посмотрю — чем живет, чем дышит этот Зенд сегодня. Смотрю — вырос-то как… возмужал… А я его вот такусеньким помню… Стал читать, что дальше будет, а еще статью про PSR-7, HTTP Messages и их реализацию в PHP. Батюшки-светы! Надо дальше смотреть, надо учиться, учиться и учиться. А то придет молодая шпана, да как сотрет меня с лица Земли:



Все пришлось учить сызнова. Покряхтел, прошел Getting Started Quest Guide, задумался. Что-то мне во всем этом не понравилось. Может, мрачные слова «Skeleton Application» в верхней части? Или то, что конфиг для Nginx пришлось самому писать (что это они мне своей Апачьей харей в морду тычут?). Да нет, не то…

О! Там ведь в ZF2 шаблонизатор на PHP!

Tired of Spaghetti code? Try Blitz!


За годы работы в Badoo шаблоны Blitz стали привычными и родными. Надо их сюда срочно прикрутить. Погуглил — нету. Что за черт?? (наверное, плохо гуглил?) В общем, начистил скальпель XDebug, взял вату, спирт и огурец, и стал препарировать.



Пациентом выступило приложение-обучалка. Точнее, его форк.

Тэк-с, посмотрим, посмотрим… Какая здоровая печень у пациента, вы не находите, коллега? Хоть в гроб клади… Вот ее-то мы и пересадим. Под печенью я подразумеваю реализацию RendererStrategy из Zend View Quick Start. Берем класс AcceptStrategy и копируем к себе. Назовем копию, как водится, Полуэкт. Шучу, назовем Application\View\RenderingStrategy. Почему не BlitzRenderingStrategy? Да, собсно, она создает и настраивает наш движок рендеринга, а значит, она же будет, например, настраивать рендеринг JSON, в случае соответствующего запроса. Так что вот.

В получившемся классе я ампутировал свойства «xxxRenderer» — а зачем они нам сразу нужны? Ну и чтобы иметь под рукой зендовский ServiceManager, решил передавать его в конструкторе:

/**
 * @param ServiceManager $sm
 */

public function __construct(ServiceManager $sm = null)
{
    $this->sm = $sm;
}


selectRenderer() и injectResponse() пришлось переписать. Вот до чего доходит!

/**
 * @param  ViewEvent $e
 * @return \Zend\View\Renderer\RendererInterface
 */

public function selectRenderer($e)
{
    $model = $e->getModel();
    if ($model instanceof JsonModel) {
        return $this->sm->get(JsonRenderer::class);
    }
 
    return $this->sm->get(BlitzRenderer::class);
}
 
/**
 * @param  \Zend\Mvc\MvcEvent $e The MvcEvent instance
 * @return void
 */

public function injectResponse($e)
{
    $renderer = $e->getRenderer();
    $response = $e->getResponse();
 
    if ($renderer instanceof JsonRenderer) {
        // JSON Renderer; set content-type header
        $headers = $response->getHeaders();
        $headers->addHeaderLine('content-type', 'application/json');
    }
 
    // Inject the content
    $response->setContent($e->getResult());
}

Что-то давно картинок не было.



Вот. Уже и целый класс сделал! Но и этого оказалось мало! Нужно еще, чтобы наш «модуль» (слово-то какое — мооодуль) знал о том, что мы ему готовим! Нет, каков гусь, а?! Это все происходит в классе Application\Module

/**
 * @param  \Zend\Mvc\MvcEvent $e The MvcEvent instance
 * @return void
 */

private function registerRenderingStrategy($e) {
    $app = $e->getTarget();
    $locator = $app->getServiceManager();
    $view = $locator->get(\Zend\View\View::class);
    $renderingStrategy = $locator->get(RenderingStrategy::class);
     // Attach strategy, which is a listener aggregate, at high priority
    $view->getEventManager()->attach($renderingStrategy, 100);
}
 
public function getServiceConfig()
{
    return array(
        ...
        'factories' => array(
            RenderingStrategy::class => function ($sm) {
                /** @var ServiceManager $sm */
                return new RenderingStrategy($sm);
        },..
    ),
);
}

Ну и немного изменить onBootstrap() в модуле приложения:
$eventManager->attach('render', function($e) {
    /** @var \Zend\Mvc\MvcEvent $e */
    return $this->registerRenderingStrategy($e);
}, 100);

Что еще добавить? Я решил, что негоже Blitz-шаблоны сохранять с расширением .phtml и до кучи поменял конфиг:

'view_manager' => array(
    ...
    'default_template_suffix' => 'blitz.html',
),

Ну, еще пришлось чуууть-чуть изменить код контроллера там, где во вьюху передается результат выборки из БД: Blitz не поддерживает все эти новомодные итераторы, ему массивы подавай (деды наши без итераторов программировали, и мы будем! Правда, fisher?) Попутно обнаружилось, что если вызвать toArray() у экземпляра ResultSet, то он будет вызывать тот же toArray() на каждом ряде. Пришлось дополнить модель Album методом toArray(). А, ну еще пришлось шаблоны перевести на Blitz и сохранить с суффиксом «blitz.html».

Даже не знаю, насколько плох такой способ интеграции шаблонного движка в ZF2. Может, профессионалы знают более толковый путь? Обсудим?

Комментарии (5)


  1. uLow
    05.10.2015 01:49
    +2

    Своеобразная подача.
    По материалу — с одной стороны, обозначены места где и что делалось.
    С другой — почему делались именно такие изменения — не понятно.
    То есть подача материала аля «для самых маленьких», а сам материал 18+.


    1. crocodile2u
      05.10.2015 09:33

      Эээ, ну не знаю, это то, что я сделал за пару часов после 3-4 часов изучения ZF2. Как бы не понимаю, что тут подробнее расписывать. К тому же результат-то лежит на гитхабе — бери да смотри, да пользуйся ;-)


  1. Arilas
    05.10.2015 10:54
    +3

    Для inject'а ServiceLocator'а достаточно было использовать ServiceLocatorAwareInterface либо ServiceManagerAwareInterface. В конфиг засовывать функции колбеки — не очень (getServiceConfig). Конфиг должен быть таким, чтобы легко кешировался, поэтому, если заимплементили один из интерфейосв выше, могли бы в module.config.php добавить:
    'service_manager' => [
    'invokables' => [
    BlitzStrategy::class => BlitzStrategy::class
    ]
    ].
    Зарегистрировать стратегию можно проще:
    view_manager:
    strategies:
    BlitzStrategy
    В конфиге.

    А вообще наконец, первая нестандартная статья про ZF2.


    1. crocodile2u
      05.10.2015 11:50

      Спасибо, ценные рекомендации, я пока не дошел до «invokables» и «view_manager/strategies».


      1. Arilas
        05.10.2015 12:27

        В ZF2 очень много интересных вещей, которые очень хорошо зарыты в коде, и в доке достаточно сложно понять, что зачем.
        К примеру то, что есть множество разных ServiceManager'ов, и для них отдельные сервисы и поведение можно задать. Те же контроллеры бывают не только «invokables» но также можно использовать Фабрики и Абстрактные фабрики для них.

        Также есть так называемые «loaders» — предназначены для инициализации любого сервиса (к примеру реализующего ServiceLocatorAwareInterface)

        Вообще когда-то писал рендер вьюшек в pdf файл, даже модуль есть: packagist.org/packages/krona/wkhtml-module Но это очень давно было уже