View Helper’ы Zend Framework в видах Symfony 2

// Январь 26th, 2016 // Symfony 2, Zend Framework, Веб-разработка

block-device-iconВ жизни возникает много всяких инетересных задач. Как правило, можно выделить задачи класса «реализовать такую-то фишку» и «извернуться, чтобы оно работало». Вот, как раз, о последнем типе задач и пойдёт речь в этом посте.

Иногда в процессе разработки параллельно с разработкой идут несколько «миграций». Ну например переход с SQL на NoSQL, я об этом как-то уже писал. Не суть. Так вот, представим, что идёт миграция с Zend Framework на Symfony (да простят меня любители ZF). Нам необходимо, чтобы наши горячо любимые (т.к. хорошо отлаженные) ZF View хелперы работали и в Sf-окружении. Как же это сделать.

Задача

Необходимо, чтобы одни и те же (да, без копипасты) Zend Framework View Helper-ы работали как в Zf окружении, так и в Sf окружении.

Решение

Многим разработчикам хорошо знаком механизм Zend_Application_Bootstrap  в ZF. Очень, надо сказать, удобная штука. Там можно описывать зависимости одних классов от других и порядок их загрузки. Вернее так, сначала описываем зависимости, потом подгружаем нужный класс, а уж он подгружает своё дерево зависимостей.

symfony_poster

Я был очень удивлён, но симфоневский Dependency Injection работает точно так же. Я просто перенес ресурсы в сервисы, определил зависимости, и они загрузились. Для решения нашей задачи нам понадобится Zend_layout. Воспользуемся механизмом factory_service/facctory_method

services.yml

# Класс с фабриками для сервисов (аналог zf bootstrap файла)
zf.bootstrap:
    class: OurNamespace\DefaultBundle\Service\Bootstrap

zf.front_controller:
    class: Zend_Controller_Front
    factory_service: zf.bootstrap
    factory_method: createZfFrontController
    arguments: []
zf.layout:
    class: Zend_Layout
     factory_service: zf.bootstrap
    factory_method: createZfLayout
    arguments: [@zf.front_controller, @templating.helper.actions]

Отлично, сразу как на ладони видны зависимости. Про содержимое методов не буду распространяться, если коротко, то в бандле есть специальный сервис, который называется (OurNamespace\DefaultBundle\Service\Bootstrap). Он содержит в себе методы, фабрики для классов-сервисов, которые аналогичны ресурсам в Zend_Application. А остальные сервисы используют его для ссылки на свои конструкторы.

Например, для создания объекта Zend_Controller_Front используется метод createZfFrontController@zf.bootstrap, т.е. OurNamespace\DefaultBundle\Service\Bootstrap:;createZfFrontController(). Для создания объекта лейаута используем OurNamespace\DefaultBundle\Service\Bootstrap::createZfLayout (Zend_Controller_Front, Symfony\Bundle\FrameworkBundle\Templating\Helper\ActionsHelper). В принципе на этом этапе всё понятно. Теперь, сама суть. Создаём сервис «zf.view_helpers_proxy»:

services.yml

zf.view_helpers_proxy:
class: OurNamespace\DefaultBundle\Helper\ZendViewHelpersProxy
    arguments:
        - { layout: @zf.layout }
    tags:
        - { name: templating.helper, alias: zf }

Тут используется уже не функция-фабрика из Bootstrap, а вполне себе обычны конструктор класса ZendViewHelpersProxy.

А вот и сам класс:

ZendViewHelpersProxy.php

<?php
namespace OurNamespace\DefaultBundle\Helper;

use Symfony\Component\Templating\Helper\Helper;

class ZendViewHelpersProxy extends Helper
{

 protected $options;

 protected $zendView = null;

 public function __construct(array $options)
 {
  $this->options = $options;
  if(!array_key_exists('layout', $options))
  throw new \Exception('Layout object did not pass to ViewHelpersProxy');
  /** @var $layout \Zend_Layout */
  $layout = $options['layout'];
  $this->zendView = $layout->getView();
 }

 protected function getOption($name)
 {
 if (array_key_exists($name, $this->options)) {
  return $this->options[$name];
 }

 throw new Exception('Options does not exist');
 }

 public function getName()
 {
  return 'zf';
 }

 /**
 * Accesses a helper object from within a script.
 *
 * If the helper class has a 'view' property, sets it with the current view
 * object.
 *
 * @param string $name The helper name.
 * @param array $args The parameters for the helper.
 * @return string The result of the helper output.
 */
 public function __call($name, $args)
 {
  // is the helper already loaded?
  $helper = $this->zendView->getHelper($name);

  // call the helper method
  return call_user_func_array(
  array($helper, $name),
  $args
  );
 }
}

Обращаем вниманеи на функцию getName() и теги в описании сервиса { name: templating.helper, alias: zf }. Это позволит нам использовать ЛЮБОЙ Zend Framework View Helper в Symfony вот так:

<?= $view['zf']->ImagePath($image, 129, 158); ?>

По сути обычный прокси, который позволит обеспечить совместимость и избежать дублирования кода. А со временем можно будет потихоньку пеерписать хелперы. Главное, что таким образом процесс миграции пойдёт плавно без авралов и факапов.

Ссылки

http://blog.logicexception.com/2011/11/creating-symfony2-helper.html
http://symfony.com/doc/2.0/book/templating.html

Share

Спасибо!


Если вам помогла статья, или вы хотите поддержать мои исследования и блог - вот лучший способ сделать это:


Комментировать