Итак, в чём же проблема? Дело в том, что при работе с фреймворком kohana иногда требуется из какой либо части приложения записать какую-либо строку контента (html, js, css и др) в произвольную часть документа.
Для примера: все css линки и сами наборы стилей лучше подключать только в заголовке и красивее всего будет если они будут идти друг за другом. Весь JS лучше подключать в конце файла.
А что если мы не знаем какая часть документа сейчас выводится? Обычное echo не подходит. Ещё сложнее ситуация становится если мы хотим подключить сие в виде, если этот вид представляет не всю страницу, а лишь часть её (например форму), то доступа ни к заголовку ни к концу документа мы не имеем никакого.
Или допустим мы написали несколько модулей для нашей любимой коханы. К каждому модулю мы хотим подключить js-фреймворк jQuery. Но мы же не знаем какие модули точно будут использоваться, как и не знаем будет ли подключен данный файл в самом приложении. Если будем подключать js-файл в каждом модуле, то в конечном документе будет куча линков на один и тот же файл, что не есть гуд.
Решение состоит в том, чтобы написать библиотеку, которая будет собирать строки контента, сгруппированные по типу (js, css и тд), а потом по запросу будет выдавать его в нужную часть документа. Более того хотелось бы проверять на уникальность если это нужно.
Итак, создаём библиотеку в папке libraries и называем её «Content_Collector». Библиотека будет выглядеть так:
Для примера: все css линки и сами наборы стилей лучше подключать только в заголовке и красивее всего будет если они будут идти друг за другом. Весь JS лучше подключать в конце файла.
А что если мы не знаем какая часть документа сейчас выводится? Обычное echo не подходит. Ещё сложнее ситуация становится если мы хотим подключить сие в виде, если этот вид представляет не всю страницу, а лишь часть её (например форму), то доступа ни к заголовку ни к концу документа мы не имеем никакого.
Или допустим мы написали несколько модулей для нашей любимой коханы. К каждому модулю мы хотим подключить js-фреймворк jQuery. Но мы же не знаем какие модули точно будут использоваться, как и не знаем будет ли подключен данный файл в самом приложении. Если будем подключать js-файл в каждом модуле, то в конечном документе будет куча линков на один и тот же файл, что не есть гуд.
Решение состоит в том, чтобы написать библиотеку, которая будет собирать строки контента, сгруппированные по типу (js, css и тд), а потом по запросу будет выдавать его в нужную часть документа. Более того хотелось бы проверять на уникальность если это нужно.
Итак, создаём библиотеку в папке libraries и называем её «Content_Collector». Библиотека будет выглядеть так:
Copy Source | Copy HTML
- <?php defined('SYSPATH') OR die('No direct access allowed.');
-
- class Content_Collector_Core
- {
- private static $instance = null;
- /**<br/> * @return Content_Collector<br/> */
- public static function instance()
- {
- if (self::$instance === null)
- self::$instance = new Content_Collector_Core();
- return self::$instance;
- }
-
- protected $content = array();
- protected $markers = false;
-
- /**<br/> * Собирает $content контент типа $type<br/> * $unique - true если контент должен быть уникален<br/> * <br/> * @return Content_Collector<br/> */
- public function collect($type, $content, $unique = false)
- {
- if ($unique && isset($this->content[$type]))
- if (in_array($content, $this->content[$type]))
- return $this;
-
- $this->content[$type][] = $content;
- return $this;
- }
-
- /**<br/> * Возвращает весь контент типа $type<br/> *<br/> * @return string<br/> */
- public function get($type)
- {
- if (!isset($this->content[$type]))
- return '';
-
- $output = '';
- foreach($this->content[$type] as $content)
- $output .= $content;
-
- return $output;
- }
-
- /**<br/> * Возвращает маркер для контента типа $type<br/> *<br/> * @return string<br/> */
- public function marker($type)
- {
- if (!$this->markers)
- {
- $this->markers = true;
- $actions = Event::get('system.display');
- if (!empty($actions))
- Event::add_before('system.display', $actions[0], array($this, 'replace_markers'));
- else
- Event::add('system.display', array($this, 'replace_markers'));
- }
- return "{content_collector.$type}";
- }
-
- public function replace_markers()
- {
- $output = &Event::$data;
- preg_match_all("/\\{content_collector\\.[^}]*}/", $output, &$matches);
- $matches = $matches[0];
- if (empty($matches))
- return;
-
- $begin = strlen('{content_collector.');
- $replace = array();
- foreach($matches as $key => $value)
- {
- $type = substr($value, $begin, strlen($value) - $begin - 1);
- $replace[$value] = $this->get($type);
- }
-
- $output = strtr($output, $replace);
- }
- }
-
-
Теперь с помощью метода collect можно собирать определённый контент из любой части приложения. Затем методом get этот контент можно получить и отрендерить.
Зачем нужен метод marker? Этот метод возвращает маркер для нужного типа контента. Затем перед рендерингом всего контента на экран, он просматривается на наличие этих маркеров и они заменяются на соответствующий контент. Нужно, например для того чтобы собирать ссылки на js и css файлы внутри видов, чтобы потом в самом общем виде (где, собственно и будут стоять маркеры) получить эти ссылки (если рендерить не маркер, а сам контент, вполне возможно, что он отрендерится раньше чем все ссылки будут добавленны).
Ну, а вот и примеры использования:
В модуле использующем jQuery:
Copy Source | Copy HTML
- /Последним параметром устанавливаем true, чтобы ссылка была уникальной
- Content_Collector::instance()->collect('js', html::script('files/js/jquery.js'), true);
В каком либо виде. Например в шаблоне формы:
Copy Source | Copy HTML
- Content_Collector::instance()
- ->collect('js', html::script('files/js/scripts.js'))
- ->collect('css', html::stylesheet('files/js/style.css'));
Опять же в каком либо модуле, но уже не ссылка, а конкретный скрипт
Copy Source | Copy HTML
- $script = "";
- Content_Collector::instance()
- ->collect('js', $script);
А теперь выводим это всё в самом общем виде:
Copy Source | Copy HTML
- <?php defined('SYSPATH') OR die('No direct access allowed.'); ?>
- <html>
- <head>
- <?php echo Content_Collector::instance()->marker('css'); ?>
- </head>
- <body>
- <?php echo Content_Collector::instance()->marker('js'); ?>
- </body>
- </html>
Или другая ситуация. Мы выводим информацию для отладки. Но не хотим, чтобы она была разбросана по всей странице, для этого собираем её, а потом рендерим в отдельной части документа:
В некоторой части кода:
Copy Source | Copy HTML
- Content_Collector::instance()
- ->collect('debug', 'All right!');
В виде:
Copy Source | Copy HTML
- <?php defined('SYSPATH') OR die('No direct access allowed.'); ?>
- <html>
- <head>
- <?php echo Content_Collector::instance()->marker('css'); ?>
- </head>
- <body>
- <div class='debug_info'>
- <?php echo Content_Collector::instance()->get('debug'); ?>
- </div>
- <?php echo Content_Collector::instance()->marker('js'); ?>
- </body>
- </html>
Здесь мы не стали ставить маркер, а сразу получили контент, так как уверены, что весь нужный контент соберётся до того, как начнёт рендериться.