ZF2 + Blitz templates

    Здравствуйте, разлюбезные мои хабровчане. А ежели кто из вас веб-разработкой на 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. Может, профессионалы знают более толковый путь? Обсудим?
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

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

      +2
      Своеобразная подача.
      По материалу — с одной стороны, обозначены места где и что делалось.
      С другой — почему делались именно такие изменения — не понятно.
      То есть подача материала аля «для самых маленьких», а сам материал 18+.
        0
        Эээ, ну не знаю, это то, что я сделал за пару часов после 3-4 часов изучения ZF2. Как бы не понимаю, что тут подробнее расписывать. К тому же результат-то лежит на гитхабе — бери да смотри, да пользуйся ;-)
        +3
        Для inject'а ServiceLocator'а достаточно было использовать ServiceLocatorAwareInterface либо ServiceManagerAwareInterface. В конфиг засовывать функции колбеки — не очень (getServiceConfig). Конфиг должен быть таким, чтобы легко кешировался, поэтому, если заимплементили один из интерфейосв выше, могли бы в module.config.php добавить:
        'service_manager' => [
        'invokables' => [
        BlitzStrategy::class => BlitzStrategy::class
        ]
        ].
        Зарегистрировать стратегию можно проще:
        view_manager:
        strategies:
        BlitzStrategy
        В конфиге.

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

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

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

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое