В части 2 цикла статей о подготовке фреймворка мы работаем над шаблонизатором. Определим наши задачи:
- Один файл шаблона с header и footer областями, разный контент.
- Возможность подгружать любые файлы в header и footer области шаблона, а так же выбирать разный шаблон.
- Возможность скреплять методы add_header и add_footer в цепочки используя method chaining.
- Предусмотреть поддержку AJAX.
Зачем это нужно
Шаблонизатор позволяет избегать повторов в разметке: постоянно копировать header и footer области в каждом файле view. Конечно, всегда можно создать 2 файла header_view.php и footer_view.php, и подгружать их отдельно. Однако, это требует повторов в коде и определенной концентрации внимания на подобных мелочах. Метод, который я предлагаю, намного удобнее. Идея этого шаблонизатора взята из дебрей nettuts, за что им огромный респект.Как это будет работать
В каждом контроллере мы будем использовать новую библиотеку (library), которая будет осуществлять все операции по подгрузке шаблона. Вот как будет выглядеть контроллер Home из первой части цикла статей:class Home extends Controller { function __construct() { parent::__construct(); $this->tpl->set_title('My title'); } public function index() { $data = array(); if ($this->_isAjaxRequest()) { $this->load->view('home_view_ajax', $data); } else { $this->tpl->view('home_view', $data); } } private function _isAjaxRequest() { return (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'); } }
Создаем нашу библиотеку
Создадим app/libraries/Tpl.php. Обратите внимание, первая буква названия написана в верхнем регистре, это важно. Обращаться к нашей библиотеке из CI мы будем тоже как «Tpl»<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); /* * Code by BrainCube.ru - Smart solutions for the Web. * @author Max Degterev <max@braincube.ru> * @copyright (c) 2010 BrainCube */ class Tpl { private $CI; private $title_for_layout = NULL; private $title_separator = ' | '; private $header = array(); private $footer = array(); public function __construct() { $this->CI =& get_instance(); } public function set_title($title) { $this->title_for_layout = $title; return $this; } public function view($view_name, $params = array(), $layout = 'default') { $rendered_view = $this->CI->load->view($view_name, $params, TRUE); if ($this->title_for_layout !== NULL) { $this->title_for_layout = $this->title_separator . $this->title_for_layout; } $this->CI->load->view('layouts/' . $layout, array( 'content_for_layout' => $rendered_view, 'title_for_layout' => $this->title_for_layout )); } public function __call($name, $arguments) { if (substr($name,0,4) == 'get_') { $property = substr($name,4); return $this->_parseIncludes($property); } else if (substr($name,0,4) == 'add_') { $property = substr($name,4); if (!isset($arguments[1])) { $this->CI->load->helper('url'); // Just in case! $project = ((stripos(base_url(), 'http://') !== FALSE) ? base_url() : 'http://'.base_url()); array_push($this->$property, $project . $arguments[0]); } else { array_push($this->$property, $arguments[0]); } } return $this; } private function _parseIncludes($arrayname) { $final_includes = ''; foreach ($this->$arrayname as $include) { if (preg_match('/js$/', $include)) { $final_includes .= '<script src="' . $include . '"></script>'; } elseif (preg_match('/css$/', $include)) { $final_includes .= '<link rel="stylesheet" href="' . $include . '" />'; } } return $final_includes; } }Часть методов вполне понятна сама по себе. Дополнительные пояснения к коду:
- 9-16 строки инициализируют переменные. $CI требуется для утилизации API CI, $title_separator – разделитель для тега <title> шаблона. Остальные переменные содержат необходимые для работы данные (сам заголовок, массив с файлами header и footer)
- 18-20 получаем инстанцию CI для работы с API. Этот трюк необходим при работе над любой библиотекой CI т.к. прямой доступ к API мы имеем только в контроллерах.
- 40-60 магия PHP5. Чтобы не дублировать одинаковый код для методов add_header и add_footer, мы воспользуемся magic методом __call. $name — вызываемый метод, $arguments — передаваемые ему аргументы. Сначала мы определяем требуемое действие. В случае вызова «get_» метода, мы передаем действие сторонней функции, которую мы рассмотрим чуть ниже. В случае «add_» метода мы добавляем в соответствующий массив ($header или $footer) новый элемент. Далее мы возвращаем инстанцию класса $this для возможности создавать цепочки методов. Обратите внимание на строку 51, этого действия можно избежать. В случае, если вы вводили адрес сайта в виде строки в переменной $config['base_url']. Поскольку мы внесли изменения в конфигурацию, при работе с localhost будет ошибка с загрузкой css и javascript. Чтобы исправить это недоразумение требуется небольшая проверка.
- 62-75 метод, отвечающий за выдачу файлов в header и footer области шаблона. Определяем тип элемента и создаем соответствующие теги.
- 27-38 метод, загружающий шаблон. Его мы будем вызывать из контроллеров. Ожидаемые параметры: файл view, массив данных и файл шаблона. По умолчанию используется «default». Далее мы получаем view в виде строки, готовим заголовок документа и вызываем шаблон. Данные из view файла передаются в общем массиве с заголовком и загружаются в шаблон.
Создаем шаблон по умолчанию
Для этой задачи возьмем за основу наработки замечательного ресурса HTML5 ★ Boilerplate. Архив содержит множество интересных файлов и папок, и определенно заслуживает отдельной статьи. Однако, нам требуются только папки css, js; и файлы index.html, apple-touch-icon.png, favicon.ico. Переместим все, кроме index.html, с которым нам придется поработать, в корень нашего CI. apple-touch-icon.png, favicon.ico это иконки нашего сайта для аппаратов Apple и обычных браузеров соответственно.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); /* * Code by BrainCube.ru - Smart solutions for the Web. * @author Max Degterev <max@braincube.ru> * @copyright (c) 2010 BrainCube */ // Get year property for copyright and some other stuff. $year = (date("Y") == '2010') ? date("Y") : ('2010-'.date("Y")); $project = ((stripos(base_url(), 'http://') !== FALSE) ? base_url() : 'http://'.base_url()); ?> <!doctype html> <!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]--> <!--[if IE 7 ]> <html lang="en" class="no-js ie7"> <![endif]--> <!--[if IE 8 ]> <html lang="en" class="no-js ie8"> <![endif]--> <!--[if IE 9 ]> <html lang="en" class="no-js ie9"> <![endif]--> <!--[if (gt IE 9)|!(IE)]><!--> <html lang="en" class="no-js"> <!--<![endif]--> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="description" content="" /> <meta name="author" content="BrainCube.ru - Smart solutions for the Web" /> <meta name="copyright" content="" /> <title>Company<?php echo $title_for_layout ?></title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="shortcut icon" href="<?php echo $project;?>favicon.ico"> <link rel="apple-touch-icon" href="<?php echo $project;?>apple-touch-icon.png"> <link rel="stylesheet" href="<?php echo $project;?>css/style.css?v=2"> <script src="<?php echo $project;?>js/libs/modernizr-1.6.min.js"></script> <?php echo $this->tpl->get_header(); ?> </head> <body> <header> </header> <?php echo $content_for_layout; ?> <footer> <?php echo $year; ?> © Company </footer> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.js"></script> <script>!window.jQuery && document.write(unescape('%3Cscript src="<?php echo $project;?>js/libs/jquery-1.4.4.js"%3E%3C/script%3E'))</script> <script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.6/jquery-ui.min.js"></script> <script>!window.jQuery && document.write(unescape('%3Cscript src="<?php echo $project;?>js/libs/jquery-ui-1.8.6.custom.min.js"%3E%3C/script%3E'));</script> <script src="<?php echo $project;?>js/script.js"></script> <?php echo $this->tpl->get_footer(); ?> <!--[if lt IE 7 ]> <script src="<?php echo $project;?>js/libs/dd_belatedpng.js"></script> <script src="<?php echo $project;?>js/ie6fix.js"></script> <![endif]--> </body> </html>HTML прост как топор. Небольшие пояснения по части PHP:
- 9-10 получаем переменные $year — требуется для футера, а также $project, которая указывает абсолютный путь до файлов в зависимости от base_url().
- 26, 43 выводят передаваемые в шаблон заголовок и контент.
- 34, 55 отвечают за выгрузку дополнительных файлов в области хедера и футера.
Проверим в действии!
Для этого добавим нашу библиотеку в автозагрузку. В app/config/autoload.php:$autoload['libraries'] = array('Tpl');Приведем наш контроллер app/controllers/home.php к следующему виду:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); /* * Code by BrainCube.ru - Smart solutions for the Web. * @author Max Degterev <max@braincube.ru> * @copyright (c) 2010 BrainCube */ class Home extends Controller { function __construct() { parent::__construct(); $this->tpl ->set_title('Welcome to our index page') ->add_header('css/handheld.css') ->add_footer('js/plugins.js'); } public function index() { $data = array(); if ($this->_isAjaxRequest()) { $this->load->view('home_view_ajax', $data); } else { $this->tpl->view('home_view', $data); } } private function _isAjaxRequest() { return (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'); } }Разберемся, что тут происходит:
- 12 строка мы загружаем в нашу библиотеку данные о заголовке и дополнительных файлах областей header и footer.
- 17-22 мы определяем тип запроса. В случае, если это AJAX запрос, шаблонизатор нам не требуется, и мы просто загружаем файл view.
Комментариев нет:
Отправить комментарий