Поддержка тем в CodeIgniter

Владимир | | CodeIgniter, PHP, Web разработка.

Практически все современные CMS имеют поддержку тем. Т.е. вы можете создать несколько вариантов оформления ресурса и переключаться между ними. В этой статье речь пойдет о том, как добавить поддержку тем к фреймворку CodeIgniter.

Примечание. Если вы не знакомы с этим фреймворком, то сначала вам стоит почитать статью «Как создать свой сайт на PHP? Или зачем нужны фреймворки?».

Прежде всего, сформулируем задачу:

1) контроллер должен оставаться неизменным при использовании любой из тем;

2) файлы тем должны находится в отдельных папках;

3) минимальная нагрузка на систему (т.е. шаблонизаторы и дополнительные библиотеки не используем).

В принципе, поддержка тем изначально заложена в CodeIgniter. Если придерживаться архитектуры MVC, то весь код, связанный с отображением страниц, будет находиться в представлениях, а работа с данными и обработка запросов пользователя – в моделях и контроллере.

Отсюда вытекает простейший вариант решения задачи. Если в папке view создать несколько вложенных папок (theme1, theme2, …) и разместить в них представления, то нам останется только управлять их загрузкой из контроллера.

Рассмотрим конкретный пример.

Допустим, у нас есть 4 представления: header.php, content.php, sidebar.php и footer.php. Кроме того, каждая тема должна иметь свою таблицу стилей styles.css.

Папка view содержит две темы (default и classic) и имеет такую структуру:

view/
	default/
		header.php
		content.php
		sidebar.php
		footer.php
		styles.css
	classic/
		header.php
		content.php
		sidebar.php
		footer.php
		styles.css

Теперь мы должны указать фреймворку, какую тему использовать. Эти настройки можно хранить либо в конфигурационных файлах, либо в базе данных. Допустим, мы решили использовать первый вариант.

Создаем файл application/config/themes.php, и сохраняем в нем название выбранной темы.

<?php
if ( ! defined('BASEPATH')) exit('No direct script access allowed');
$config['current_theme'] = 'classic';
?>

Теперь рассмотрим контроллер:

class Main extends Controller {
	
	private $curTheme = 'default';

	function Main() {
		parent::Controller();
		$this->load->helper('html');
		$this->config->load('themes');
		$this->curTheme = $this->config->item('current_theme');
	}

	function index() {
		$pageData['title'] = "Использование тем в CodeIgniter";
		$pageData['theme'] = $this->curTheme;
		$pageData['contentTitle'] = "Это заголовок";
		$pageData['contentData'] = "Тут должен находится текст статьи с".
			"картинками, комментариями и т.п. В общем, все, что хотите....";
		$pageData['sidebarTitle'] = "Рубрики";
		$pageData['sidebarData'] = array('PHP', 'CSS', 'HTML', 'JavaScript');
		$this->load->view($this->curTheme.'/header', $pageData);
		$this->load->view($this->curTheme.'/content');
		$this->load->view($this->curTheme.'/sidebar');
		$this->load->view($this->curTheme.'/footer');
	}
}

В конструкторе контроллера мы загружаем конфигурационный файл (строка 8 ) и читаем параметр current_theme (строка 9).

В методе index мы добавляем к названию представления имя папки с активной темой (строки 20-23). И, кроме того, сохраняем название темы в массиве $pageData (строка 14). Дело в том, что файл header.php содержит заголовок страницы, в котором подключается таблица стилей. Т.к. каждая тема имеет свой css файл нам нужно правильно сформировать путь к нему.

<link href="<?php echo $this->config->system_url(); ?>application/views/<?php echo $theme; ?>/styles.css" rel="stylesheet" type="text/css" media="all" />

Содержимое файлов представлений я приводить не буду, т.к. разметка страницы обычная, а названия элементов массива $pageData говорят сами за себя. И в любом случае вы можете скачать архив с примером (ссылки внизу страницы).

Здесь я приведу только два скриншота для обоих тем.

Тема classic

codeigniter themes classic

Тема default

codeigniter themes default

Как видите, они отличаются только размещением колонок. Содержимое страниц одинаково (если не считать текста с названием темы).

Попробуем немного усовершенствовать код.

Уберем из контроллера код, выполняющий загрузку представлений. Для этого напишем небольшой хелпер (helper).

Создаем файл application/helpers/theme_helper.php со следующим содержимым:

<?php
function createPage($pageData) {
	$CI = & get_instance();
	$curTheme = $CI->config->item('current_theme');
	$pageData['theme'] = $curTheme;
	$CI->load->view($curTheme.'/header', $pageData);
	$CI->load->view($curTheme.'/content');
	$CI->load->view($curTheme.'/sidebar');
	$CI->load->view($curTheme.'/footer');
}
?>

Как видите, мы написали всего одну функцию, которая просто читает параметр current_theme и загружает представления.

Обратите внимание на использование функции get_instance() она позволяет получить доступ к объекту CodeIgniter (в контроллерах и моделях вместо нее используется $this).

Теперь код контроллера можно переписать следующим образом:

class Main extends Controller {
	
	function Main() {
		parent::Controller();
		$this->load->helper('html');
		$this->load->helper('themes');
		$this->config->load('themes');
	}

	function index() {
		$pageData['title'] = "Использование тем в CodeIgniter";
		$pageData['contentTitle'] = "Это заголовок";
		$pageData['contentData'] = "Тут должен находится текст статьи с ".
			"картинками, комментариями и т.п. В общем, все, что хотите....";
		$pageData['sidebarTitle'] = "Рубрики";
		$pageData['sidebarData'] = array('PHP', 'CSS', 'HTML', 'JavaScript');
		createPage($pageData);
	}
}

Как видите, работать стало удобнее.

Теперь представим, что наш сайт имеет разную структуру страниц. Например, на главной странице сайдбар отображается, а на других страницах – нет. Кроме того, разработчик темы может захотеть добавить дополнительные представления или изменить их названия.

Очевидно, что функция createPage должна каким-то образом определить, какие представления показывать в каждом конкретном случае.

В этом случае нужно, прежде всего, определиться с типами страниц. В нашем случае они будут называться: main (главная) и page (просто страница).

Теперь добавим несколько параметров в конфигурационный файл.

$config['themes']['default']['main'] = array('header', 'content', 'sidebar', 'footer');
$config['themes']['default']['page'] = array('header', 'content', 'footer');

Как видите, мы создали многомерный массив с настройками. Второй индекс массива указывает названия темы, третий – тип страницы.

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

Примечание. Аналогичные настройки нужно задать для всех тем.

Теперь нужно изменить хелпер.

function createPage($pageData, $pageType) {
	$CI = & get_instance();
	$curTheme = $CI->config->item('current_theme');
	$pageData['theme'] = $curTheme;
	$themeData = $CI->config->item('themes');
	foreach ($themeData[$curTheme][$pageType] as $curView) {
		$CI->load->view($curTheme.'/'.$curView, $pageData);
	}
}

Теперь, мы получаем массив с названиями представлений и загружаем только их. Кроме того, добавился еще один параметр $pageType (тип страницы).

В контроллере мы только добавляем метод page()

function page() {
	$pageData['title'] = "Использование тем в CodeIgniter";
	$pageData['contentTitle'] = "Это заголовок";
	$pageData['contentData'] = "Тут должен находится текст статьи с ".
		"картинками, комментариями и т.п. В общем, все, что хотите....";
	$pageData['sidebarTitle'] = "Рубрики";
	$pageData['sidebarData'] = array('PHP', 'CSS', 'HTML', 'JavaScript');
	createPage($pageData, 'page');
}

Ограничения и область использования такого решения

Если вы внимательно посмотрите на последний блок кода (метод page), то заметите один существенный недостаток (строки 6 и 7).

Дело в том, что в одной теме может использоваться сайдбар, а в другой – нет. Но в контроллере мы в любом случае должны подготовить данные для него. Иначе часть тем просто не будет работать.

Конечно, в данном примере, это всего лишь короткие строки текста, но в реальном приложении эти данные нужно будет получать из базы. А это означает, что для каких-то тем будут выполняться лишние запросы.

Отсюда следует простой вывод: «Такой способ создания тем можно применять только для сайтов с постоянной структурой страниц».

Если вам нужно более гибкое решение – используйте подход, который применяется в большинстве CMS (например, в движке WordPress).

Принцип следующий. Получение данных переносится в представление. Т.е. разработчик темы получает доступ к какому-то набору функций (API). И с его помощью получает нужные ему данные.

Например, в том же WordPress функция get_posts возвращает выбранные записи.

Но, несмотря на гибкость такого подхода у него есть свои недостатки:

1) Система усложняется (оправданно для CMS, но для обычного сайта может быть излишним).

2) Усложняется код представлений, т.к. нужно вызывать функции (опять же, это оправданно, если нужно обеспечить разную структуру для страниц одного типа).

3) Разработчик темы может написать далеко не оптимальный код. Например, вызвать несколько раз одну и туже функцию.

4) Практически всегда универсальное решение работает медленнее и потребляет больше ресурсов, чем узкоспециализированное. В результате может получиться тяжеловесный движок.

5) Нарушается архитектура MVC (получение данных выполняется внутри представления). В принципе, это не проблема, т.к. CodeIgniter не накладывает здесь жестких ограничений.

Скачать архивы с примерами

1) Простейший вариант (без хелпера).
2) Тоже самое + хелпер.
3) Вариант с поддержкой индивидуального оформления разных типов страниц.

Во всех архивах находится папка application, которую нужно скопировать в папку system дистрибутива CodeIgniter.

Заключение.

Как видите, выбор способа создания поддержки тем зависит от конкретной ситуации. Но меня интересует ваше мнение. Насколько оправданно вообще использование тем в небольшом проекте вроде сайта-визитки? Может проще его переделывать каждый раз и не морочить себе голову с темами?

Интересно почитать:

Забавный трюк по получению трафика с социалок