Search
Write a publication
Pull to refresh

Content Collector — сборка контента в kohana

Итак, в чём же проблема? Дело в том, что при работе с фреймворком kohana иногда требуется из какой либо части приложения записать какую-либо строку контента (html, js, css и др) в произвольную часть документа.
Для примера: все css линки и сами наборы стилей лучше подключать только в заголовке и красивее всего будет если они будут идти друг за другом. Весь JS лучше подключать в конце файла.

А что если мы не знаем какая часть документа сейчас выводится? Обычное echo не подходит. Ещё сложнее ситуация становится если мы хотим подключить сие в виде, если этот вид представляет не всю страницу, а лишь часть её (например форму), то доступа ни к заголовку ни к концу документа мы не имеем никакого.
Или допустим мы написали несколько модулей для нашей любимой коханы. К каждому модулю мы хотим подключить js-фреймворк jQuery. Но мы же не знаем какие модули точно будут использоваться, как и не знаем будет ли подключен данный файл в самом приложении. Если будем подключать js-файл в каждом модуле, то в конечном документе будет куча линков на один и тот же файл, что не есть гуд.
Решение состоит в том, чтобы написать библиотеку, которая будет собирать строки контента, сгруппированные по типу (js, css и тд), а потом по запросу будет выдавать его в нужную часть документа. Более того хотелось бы проверять на уникальность если это нужно.
Итак, создаём библиотеку в папке libraries и называем её «Content_Collector». Библиотека будет выглядеть так:
Copy Source | Copy HTML
  1. <?php defined('SYSPATH') OR die('No direct access allowed.');
  2.  
  3. class Content_Collector_Core
  4. {
  5.     private static $instance = null;
  6.     /**<br/>     * @return Content_Collector<br/>     */
  7.     public static function instance()
  8.     {
  9.         if (self::$instance === null)
  10.             self::$instance = new Content_Collector_Core();
  11.         return self::$instance;
  12.     }
  13.  
  14.     protected $content = array();
  15.     protected $markers = false;
  16.  
  17.     /**<br/>     * Собирает $content контент типа $type<br/>     * $unique - true если контент должен быть уникален<br/>     * <br/>     * @return Content_Collector<br/>     */
  18.     public function collect($type, $content, $unique = false)
  19.     {
  20.         if ($unique && isset($this->content[$type]))
  21.             if (in_array($content, $this->content[$type]))
  22.                 return $this;
  23.  
  24.         $this->content[$type][] = $content;
  25.         return $this;
  26.     }
  27.  
  28.     /**<br/>     * Возвращает весь контент типа $type<br/>     *<br/>     * @return string<br/>     */
  29.     public function get($type)
  30.     {
  31.         if (!isset($this->content[$type]))
  32.             return '';
  33.  
  34.         $output = '';
  35.         foreach($this->content[$type] as $content)
  36.             $output .= $content;
  37.  
  38.         return $output;
  39.     }
  40.  
  41.     /**<br/>     * Возвращает маркер для контента типа $type<br/>     *<br/>     * @return string<br/>     */
  42.     public function marker($type)
  43.     {
  44.         if (!$this->markers)
  45.         {
  46.             $this->markers = true;
  47.             $actions = Event::get('system.display');
  48.             if (!empty($actions))
  49.                 Event::add_before('system.display', $actions[0], array($this, 'replace_markers'));
  50.             else
  51.                 Event::add('system.display', array($this, 'replace_markers'));
  52.         }
  53.         return "{content_collector.$type}";
  54.     }
  55.  
  56.     public function replace_markers()
  57.     {
  58.         $output = &Event::$data;
  59.         preg_match_all("/\\{content_collector\\.[^}]*}/", $output, &$matches);
  60.         $matches = $matches[0];
  61.         if (empty($matches))
  62.             return;
  63.  
  64.         $begin = strlen('{content_collector.');
  65.         $replace = array();
  66.         foreach($matches as $key => $value)
  67.         {
  68.             $type = substr($value, $begin, strlen($value) - $begin - 1);
  69.             $replace[$value] = $this->get($type);
  70.         }
  71.  
  72.         $output = strtr($output, $replace);
  73.     }
  74. }
  75.  
  76.  



Теперь с помощью метода collect можно собирать определённый контент из любой части приложения. Затем методом get этот контент можно получить и отрендерить.
Зачем нужен метод marker? Этот метод возвращает маркер для нужного типа контента. Затем перед рендерингом всего контента на экран, он просматривается на наличие этих маркеров и они заменяются на соответствующий контент. Нужно, например для того чтобы собирать ссылки на js и css файлы внутри видов, чтобы потом в самом общем виде (где, собственно и будут стоять маркеры) получить эти ссылки (если рендерить не маркер, а сам контент, вполне возможно, что он отрендерится раньше чем все ссылки будут добавленны).
Ну, а вот и примеры использования:
В модуле использующем jQuery:
Copy Source | Copy HTML
  1. /Последним параметром устанавливаем true, чтобы ссылка была уникальной
  2. Content_Collector::instance()->collect('js', html::script('files/js/jquery.js'), true);


В каком либо виде. Например в шаблоне формы:
Copy Source | Copy HTML
  1. Content_Collector::instance()
  2.    ->collect('js', html::script('files/js/scripts.js'))
  3.    ->collect('css', html::stylesheet('files/js/style.css'));


Опять же в каком либо модуле, но уже не ссылка, а конкретный скрипт
Copy Source | Copy HTML
  1. $script = "";
  2. Content_Collector::instance()
  3.    ->collect('js', $script);


А теперь выводим это всё в самом общем виде:
Copy Source | Copy HTML
  1. <?php defined('SYSPATH') OR die('No direct access allowed.'); ?>
  2. <html>
  3. <head>
  4.   <?php echo Content_Collector::instance()->marker('css'); ?>
  5. </head>
  6. <body>
  7.    <?php echo Content_Collector::instance()->marker('js'); ?>
  8. </body>
  9. </html>



Или другая ситуация. Мы выводим информацию для отладки. Но не хотим, чтобы она была разбросана по всей странице, для этого собираем её, а потом рендерим в отдельной части документа:
В некоторой части кода:
Copy Source | Copy HTML
  1. Content_Collector::instance()
  2.    ->collect('debug', 'All right!');


В виде:
Copy Source | Copy HTML
  1. <?php defined('SYSPATH') OR die('No direct access allowed.'); ?>
  2. <html>
  3. <head>
  4.   <?php echo Content_Collector::instance()->marker('css'); ?>
  5. </head>
  6. <body>
  7.    <div class='debug_info'>
  8.       <?php echo Content_Collector::instance()->get('debug'); ?>
  9.    </div>
  10.    <?php echo Content_Collector::instance()->marker('js'); ?>
  11. </body>
  12. </html>


Здесь мы не стали ставить маркер, а сразу получили контент, так как уверены, что весь нужный контент соберётся до того, как начнёт рендериться.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.