Доброго дня, друзья! Извиняюсь за долгое отсутствие – оказывается у меня началась сессия, на которую и парочка дедлайнов выпала. Ну теперь, когда хотя бы с одним пунктом покончено, появилось немного свободного времени и пора мне вернуться к нашей CMS.
Кто знаком с прошлыми статьями помнят, что я собирался в этом месяце переходить на новый хостинг а вместе с этим на на другую CMS для своего блога – на свою. К сожалению CMS пока не готова, а перенос на новый хостинг потерпел фиаско (не без “помощи” текущего хостера). Сейчас я занимаюсь бэкэнд-частью и могу показать пару скринов панели администрирования:
This slideshow requires JavaScript.
Это не окончательный вариант как дизайна, так и возможностей, судить о которых можно по меню слева… Но сегодня не об этом.
Сегодня нам предстоит немного доработать класс работы с шаблонами, расширив его задачи. А добавлять мы будем …. функционал подключения стилей и JS-скриптов. Согласитесь, не совсем удобно подключать эти файлы непосредственно в верстке. К тому же, может возникнуть нужда для каких-то отдельных страниц, использующих один шаблон, добавить или удалить какой-то скрипт или css-стиль – сейчас это не так-то просто. А вот упростить этот момент мы можем и ходить далеко не надо: в том же WP реализована похожая возможность – методы wp_enqueue_script и wp_enqueue_style и файл functions.php. У нас есть главный файл шаблона, в котором мы указываем сейчас список подключаемых шаблонов и информацию об авторе (которую пока никак не используем). С помощью этого файла мы и будем производить подключение необходимых стилей и скриптов в наш шаблон.
Логика проста – мы в главном файле шаблона будем указывать какой css/js будет подключаться в шаблон. А в шаблоне просто будем указывать новый псевдотег, куда будет записываться код подключения. Так же, было бы неплохо иметь возможность указывать в хедер или футер подключать конкретный JS-скрипт или добавлять media-запросы к css-стилям. Порядок подключения оставим пока линейным – в каком порядке указаны пути к файлам, в том они и будут подключаться в шаблоне.
Для начала – оговорим имена новых псевдотегов которые будут выполнять функцию флагов, куда шаблонизатор будет вставлять тот или иной код. Я предлагаю такой:
STYLES – где мы укажем этот тег, там будет производиться подключение всех CSS-файлов.
HEADER_SCRIPTS – в место этого тега будет вставлен код подключения всех скриптов для блока <head>
FOOTER_SCRIPTS – ну и, соответственно, скрипты в футере
Теперь стоит оговорить стандарт для массивов, которые будут использоваться при подключении файлов. Для файлов JS я выбрал формат [путь] => [футер?] – вполне понятно и кратко. Открываем файл шаблона, у меня это /templates/Conversion/Conversion.php и добавляем новый массив t_scripts:
$t_scripts = array( '/js/jquery.min.js' => false, '/js/imagesloaded.pkgd.min.js' => false, '/js/masonry.pkgd.min.js' => false );
В моем шаблоне пока используется всего 3 js-скрипта, которые я хочу подключить в хедер шаблона – для чего указываю false. Если необходимо подключить скрипт в футере – мы укажем true.
Теперь CSS-стили. Подключать будем подобным массивом, но вместо указания куда подключать, мы будем писать media-запрос. Если запрос ненужен – пишем all или оставляем значение пустым. В тот же файл дописываем массив t_styles:
$t_styles = array( '/styles/main.css' => 'all', '/styles/large-screen.css' => 'screen and (min-width: 1440px)', '/styles/middle-screen.css' => 'screen and (max-width: 1440px) and (min-width: 980px)', '/styles/small-screen.css' => 'screen and (max-width : 980px ) and (min-width : 588px)', '/styles/smallest-screen.css' => 'screen and (max-width: 640px)' );
Стилей несколько больше, за счет того, что я разделил стили для разных экранов.
Теперь откроем непосредственно наши основные шаблоны index_template.tpl и page_template.tpl и после тега <title> вместо всех подключенных стилей укажем наш псевдотег {:SCRIPTS:} а после него, вместо всех подключений JS добавим {:HEADER_SCRIPTS:}. Не забываем также о том, что можно подключить скрипты и в футер – для этого прямо перед закрывающим </body> укажем {:FOOTER_SCRIPTS:}. Все, база для обработки готова. Теперь перейдем к самому интересному.
И начнем с того, что немного упростим себе работу с путями в нашей системе. Для этого добавим несколько новых элементов в наш конфиг. Открываем файл /core/config.php и начнем с того, что заменим значение $template на простую переменную, вместо массива. И добавим несколько новых методов:
public static $template = 'Conversion'; public static $dirs = array(); public static function setPath($name, $value){ self::$dirs[$name] = $value; } public static function getPath($name, $is_url = false){ if(isset(self::$dirs[$name])){ return ($is_url ? self::$dirs[$name] : $_SERVER['DOCUMENT_ROOT'].self::$dirs[$name]); } else return false; }
Как можно понять из названий – setPath() устанавливает путь, getPath() его возвращает. Но для того, чтоб что-то возвратить, необходимо заполнить базовыми данными массив $dirs. Для этого в метод __construct добавим несколько операций установки этих путей к нашим папкам системы. Содержимое метода должно получиться примерно таким:
public function __construct() { self::$dirs = array( 'root' => '/', 'core' => '/core', 'libraries' => '/core/libraries', 'includes' => '/includes', 'modules' => '/includes/modules', 'plugins' => '/includes/plugins', 'media' => '/media', 'images' => '/media/images', 'templates' => '/templates' ); self::$global_cms_vars['SITE_NAME'] = self::$site_name; self::$global_cms_vars['PAGE_TITLE'] = ''; self::$global_cms_vars['CONTENT'] = ''; self::$global_cms_vars['YEAR'] = date('Y'); self::$global_cms_vars['TIME'] = date('H:i:s'); }
Теперь нам будет проще работать с путями, достаточно будет запросить желаемый ключ у конфига, например config::getPath('libraries') и мы получим тот путь, который будет установлен при создании экземпляра конфига. В случае если нам необходимо будет какой-то из путей переопределить (а нам это потребуется, когда будем разрабатывать админскую панель) или создать новый – будет достаточно воспользоваться методом config::setPath('somepath', '/some/path/to/new/dir').
Ну а теперь открываем наш класс шаблонизатора /core/templates.php и добавляем метод для генерации кода подключения CSS:
private static function loadStyles($styles){ if(count($styles) > 0){ foreach($styles as $style => $media) $system_styles['/'.config::$template.$style] = $media; } self::$template_vars['STYLES'] = null; foreach($system_styles as $style => $media) self::$template_vars['STYLES'] .= '<link type="text/css" rel="stylesheet" '.(!empty($media) ? 'media="'.trim($media).'"' : null).' href="'.config::getPath('templates', true).$style.'">'; }
Тут логика несложная: на вход мы подаем тот самый массив из файла шаблона (t_styles) и если он не пустой, перебираем его, формируя из него в глобальную переменную системы STYLES обычную верстку. Таким образом, когда шаблонизатор будет парсить шаблоны, вместо псевдотега {:STYLES:} будет выведена получившаяся в этом методе верстка.
Теперь займемся скриптами. Для них добавим аналогичный метод:
private static function loadScripts($scripts){ if(count($scripts) > 0){ foreach($scripts as $script => $in_footer) $system_scripts['/'.config::$template.$script] = $in_footer; } self::$template_vars['HEADER_SCRIPTS'] = null; self::$template_vars['FOOTER_SCRIPTS'] = null; foreach($system_scripts as $script => $in_footer){ if($in_footer) self::$template_vars['FOOTER_SCRIPTS'] .= '<script type="text/javascript" src="'.config::getPath('templates', true).$script.'"></script>'; else self::$template_vars['HEADER_SCRIPTS'] .= '<script type="text/javascript" src="'.config::getPath('templates', true).$script.'"></script>'; } }
Тут похожим способом формируется 2 глобальных переменных в которых содержится верстка подключения скриптов для футера и хедера соответственно. На вход передавать будем массив $t_scripts и главного файла шаблона.
Может возникнуть вопрос – для чего сначала мы формируем из входного массива другой, практически такой же, массив? Это нам пригодится в дальнейшем, когда будет необходимо добавить обязательные “системные” скрипты и стили – об этом в следующих статьях. Пока просто оставим так.
Теперь нам необходимо использовать эти два новых метода в процессе загрузки обработки шаблона. Но помимо прочего, у нас теперь есть удобные методы для работы с путями, которые будет весьма удобно тут использовать. В конечном итоге метод loadTemplate станет выглядеть так:
public static function loadTemplate($template_name, $page, $vars, $fast = false) { if(!empty($template_name)) { if($fast) { if(!file_exists(config::getPath('templates') . '/' . $template_name . '/' . $page . '.tpl')) { handler::engineError('template_not_found', config::getPath('templates') . '/' . $template_name . '/' . $page . '.tpl', __FILE__, __LINE__); } else { self::$template_dir = config::getPath('templates') . '/' . $template_name . '/'; self::$template_page = $page; self::$template_vars = $vars; self::getTemplate($fast); self::parseTemplate(); return self::$template; } } else { if(!file_exists(config::getPath('templates') . '/' . $template_name . '/' . $template_name . '.php')) { handler::engineError('template_not_found', $_SERVER['DOCUMENT_ROOT'].'/'.$template_name['dir'] . '/' . $template_name . '/' . $template_name . '.tpl', __FILE__, __LINE__); return false; } else { self::$template_dir = config::getPath('templates') . '/' . $template_name . '/'; require_once self::$template_dir . $template_name . '.php'; self::$template_page = $page; self::$template_parts = $t_files; self::$template_vars = $vars; self::getTemplate($fast); self::loadStyles($t_styles); //загрузили стили self::loadScripts($t_scripts); //загрузили скрипты self::parseTemplate(); return self::$template; } } } else { handler::engineError('template_not_configure', null, __FILE__, __LINE__); return false; } }
Как видите, новые методы подключения стилей и скриптов я добавил лишь в условие загрузки полного шаблона. Так же можно заметить, что на вход методу handler::engineError() помимо привычных параметров я передаю еще и волшебные константы __FILE__ и __LINE__ – таким образом я передаю в вывод сообщения об ошибке место и файл, в котором была вызвана та или иная ошибка. Пока на это не обращаем внимания, в следующих статьях поговорим об этом более подробно.
Итак, у нас все готово. Можно открывать браузер и проверять работу нашего шаблонизатора с новыми фичами. Результат внешне не будет виден, но “под капотом” у нас теперь будет хороший инструмент, позволяющий влиять на загрузку тех или иных скриптов или стилей – и это нам пригодится в следующей статье, которая будет посвящена авторизации и, думаю, будет разбита как минимум на 2 части. Ну а до тех пор, советую пробежаться по всем модулям вашей системы и прописать новую работу с путями, везде, где они используются.
Ну а на этом, у меня все. До скорых встреч. Комментарии, критика и просто ваше мнение всегда приветствуются. Так же не забывайте про форму обратной связи – если возникают какие-то проблемы – смело пишите, я всегда отвечу и постараюсь помочь всем, что в моих силах )
P.S. долгое время на сайте была проблема, когда он просто отказывался работать при переходе на другие страницы, попытке оставить комментарий и т.п. – я ее, вроде как, поборол, но сказать как именно сложно. Если вдруг у кого-то похожие симптомы – рекомендую присмотреться к работе модулей кеша, таких как Autoptimize и WP Super Cache и т.п. потому как именно они, как оказалось, пакостили у меня на сайте.
Разработка CMS с нуля. Часть 9. Снова шаблоны (и не только) Доброго дня, друзья! Извиняюсь за долгое отсутствие - оказывается у меня началась сессия, на которую и парочка дедлайнов выпала.