Современная, быстрая и удобная система управления сайтом

Модульная структура сайта

Архив записейКомментарии: 0Просмотров: 5459

Модульность - это принцип, согласно которому приложение разделяется на отдельные сущности - модули. В вебстроительстве это будет разделение программного кода на отдельные файлы. Понятно, что обслуживать файлы по отдельности несколько проще, чем один большой файл, где всё собрано скопом.

Деление на модули может выполняться по самым разным критериям, начиная от востребованности (частоте использования функций) до схожей функциональности. Я стараюсь быть в курсе новостей мира CMS и могу сказать, что на текущем этапе, практически все системы используют именно модульный подход. Проблема здесь в том, что разработчики CMS понимают практическую реализацию модульности совершенно по разному и это приводит к разной структуре файлов и каталогов.

В какой-то мере ситуацию «выправляет» использование фреймворков, которые заставляют вебмастера следовать предустановленному порядку. Достаточно сравнить каталоги CodeIgniter и Zend Framework чтобы в этом убедиться. И здесь следует обязательно отметить тот факт, что оба фреймворка используют шаблон проектирования MVC (Модель-Вид-Контроллер). То есть теоретически, если следовать MVC, разработчики должны были получить примерно одинаковую структуру файлов. На деле же этого не происходит. Если же посмотреть другие фреймворки или CMS, то мы увидим ещё больше различий и несоответствий.

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

Достаточно простой пример жесткой структуры - WordPress. В нём чётко предопределенный набор данных, схема их получения и порядок вывода. Добавить или изменить эти вещи можно, но слишком трудозатратно, как в плане написания кода, так и в плане ресурсопотребления. Пример свободной структуры - CodeIgniter (и MaxSite CMS, на нём основанная). Здесь нет жесткой связки, а получение и вывод данных вообще отдано на откуп вебмастеру. За счет этого достигается высокая гибкость, быстродействие и низкие трудозатраты.

С другой стороны, слишком «гибкая» система может оказаться недостаточно функциональной, поскольку разработчики не могут предусмотреть всех стоящих задач вебмастера и ему придётся уже самостоятельно писать недостающий код.

И вот здесь, собственно, и возникает основная проблема - как именно разделить код на модули и какая оптимальная структура каталогов?

Чтобы разобраться в этом вопросе, вначале определимся с шаблоном MVC. Он предполагает разделение кода на модули получения данных и их вывод. То есть в файле вывода результирующего html-кода не должно быть кода получения данных. В самом простом варианте это может выглядеть так.

Файл get.php - здесь формируются данные (аля-«контролёр+модель»).

<?php 
$name_site = 'Название сайта';
$descr_site = 'Описание сайта';
?>

Файл out.php - здесь выводятся данные («вид»), полученные в get.php.

<?php require('get.php'); ?>
 
<div class="header">
	<div class="name_site"><?= $name_site ?></div>
	<div class="descr_site"><?= $descr_site ?></div>
</div>

Файл out.php по сути представляет собой html-шаблон, в котором можно оформить вывод согласно используемого шаблонизатора. Ну например, если подключить в парсер CodeIgniter, то файл out.php (или точнее out.tpl - он неисполняемый) можно будет записать так:

<div class="header">
	<div class="name_site">{$name_site}</div>
	<div class="descr_site">{$descr_site}</div>
</div>
Это несколько упрощённый вариант, демонстрирующий общий подход. Я планирую написать статью об использовании шаблонизатора CodeIgniter в MaxSite CMS. В последнем prelatest он доступен в Page_out

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

В реальности, конечно же, всё несколько сложней.

Одна из проблем - верстальщик и программист в одном лице. Это типичная ситуация. Возникает резонный вопрос - зачем делить модуль на два файла, если сам по себе код небольшой и всё прекрасно уживется в одном файле?

<?php 
$name_site = 'Название сайта';
$descr_site = 'Описание сайта';
?>
 
<div class="header">
	<div class="name_site"><?= $name_site ?></div>
	<div class="descr_site"><?= $descr_site ?></div>
</div>

Данный вариант удобен в первую очередь тем, что нет необходимости задумываться о зависимостях между файлами: один файл - один модуль.

Минусы возникают в тот момент, когда получается слишком сложный и большой код. В качестве примера приведу type-файлы MaxSite CMS. Здесь смесь PHP и HTML, который может и не очень сложный, но всё-таки большеват по объёму. Можно ли разбить его на отдельные файлы? Конечно, но здесь будет проблема в том, что такое деление приведёт к появлению нескольких десятков новых файлов, в которых разобраться будет куда сложней, чем с одним.

То есть предполагается, что модульное деление должно быть осмысленным. Я считаю, что слепо следовать догмам не нужно - подход должен помогать вебмастеру, а не вгонять его в неудобные рамки.

В примере с type-файлами для вебмастера есть возможность использовать type_foreach-файлы, которые являются подключаемыми подмодулями. Если же строго следовать доктрине, то вместо немногочисленных type-файлов мы бы увидели почти сотню модульных type_foreach-файлов.

В MaxSite CMS модульная структура шаблона (в текущем виде) появилась в Default-шаблоне 2.0 более года назад. В нём модульность выражается в разделении различных модулей в виде компонентов, custom-файлов и stock-файлов. Если раньше нужно было всё редактировать чуть ли не в исходных type-файлах, то сейчас компоновка шаблона происходит на основе раздельных компонентов.

Рассматривая другие CMS и фреймворки в поисках оптимальной структуры каталогов модулей, я понял только то, что такой структуры просто нет. Все существующие варианты годятся только для одного специфичного круга задач.

Например рассмотрим компоненты. Это может быть единственный php-файл. У него могут быть css-стили и свой js-файл. Компонент может получать опции, а опции могут быть отдельным ini-файлом, который, в свою очередь, может использовать отдельный php-файл.

С другой стороны, у нас есть stock-файлы, которые могут помимо основного php-файла, также содержать стили, js-код, опции, картинки и любые другие файлы. И если мы решим загнать такие модули в какую-то «универсальную» структуру каталогов/файлов, то она получится достаточно громоздкой и негибкой.

И в данном варианте гораздо разумней отдать на откуп вебмастера этот вопрос: пусть делает так, как ему удобней, поскольку бывают задачи, требующие буквально пару строк кода, а бывают - сложные, где требуется серьёзное программирование. Конечно в этом случае могут возникнуть «вольности» в структуре файлов, но, как показывает практика, разобраться в стороннем модуле, если он написан в «модульном стиле» не составляет большого труда. Главное, чтобы модули в пределах шаблона выполнялись примерно в одном стиле.

Есть ещё один момент, который несколько усложняет задачу - тот факт, что вебсайт это смесь html, php, css и js-кода. И часто возникают ситуации, когда «классический» вариант MVC может приводить к ухудшению кода.

Рассмотрим вышеприведенный пример, как компонент MaxSite CMS, выводящий название сайта и описание. Технически название сайта мы можем получить через getinfo('name_site'), а описание через getinfo('description_site'). То есть компонент будет иметь такой код:

<div class="header">
	<div class="name_site"><?= getinfo('name_site') ?></div>
	<div class="description_site"><?= getinfo('description_site') ?></div>
</div>

С точки зрения MVC, код «некошерный» - мы объединили получение данных (функци getinfo) и его вывод (html). Перепишем его согласно шаблону MVC (для простоты в одном файле).

<?php
 
$name_site = getinfo('name_site');
$description_site = getinfo('description_site');
 
?>
 
<div class="header">
	<div class="name_site"><?= $name_site ?></div>
	<div class="description_site"><?= $description_site ?></div>
</div>

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

В данном примере название сайта можно было бы выводить в виде ссылки, но только не на главной странице. То есть переменная $name_site должна быть либо простым текстом, либо html-ссылкой.

<?php

if (!is_type('home')) 
	$name_site = '<a href="адрес">' . getinfo('name_site') . '</a>';
else 
	$name_site = getinfo('name_site');
	
$description_site = getinfo('description_site');

?>
 
<div class="header">
	<div class="name_site"><?= $name_site ?></div>
	<div class="description_site"><?= $description_site ?></div>
</div>

Данный пример показывает, что разделить php-код от html очень сложно даже в таких примитивных случаях. Что же говорить о более сложных, где будут десятки переменных и различных html-блоков?

И совсем сложной будет ситуация, когда какой-то html-блок должен показываться только при каких-то условиях. Например метка записи будет выводиться только если она существует. Следовательно в самом html-шаблоне вывода мы не можем указать обрамляющий блок меток: он может оказаться пустым. Получается, что при получении данных мы уже должны формировать html-шаблон. Дробить же шаблон под каждую переменную будет слишком накладно и муторно.

Данные протворечия в MVC давно известны, что и приводит к другим различным шаблонам проектирования. Я лишь хочу показать, что следедует опираться на свой разум и делить код на модули до какого-то разумного предела. Совершенно нет смысла усложнять код, согласно различным концепциям, если он может быть написан проще и в меньшем объёме.

Строго говоря, в вебстроительстве вообще сложно говорить о «классическом» использовании MVC. Это скорее MVP (Model-View-Presenter), где Presenter и выполняет роль контролёра, но только уже формируя html-код (форматируя) для вида (View). То есть примерно тоже самое, что и в наших примерах с компонентом шапки. Поскольку HTML используется только для Вида, то любое его присутствие в контролёре MVC делает код «грязным». Чтобы этого избежать, придется передавать дополнительные данные «состояния» в различные Виды, что сильно усложняет логику работы. В нашем примере пришлось бы сделать два вида: один без ссылки, второй - с ссылкой.

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

Пусть нужно оформить вывод главной страницы. Мы знаем, что за это отвечает файл type/home.php. Так же мы знаем, что практически любая страница шаблона в type-файле выводится по вот такой схеме:

# начальная часть шаблона
require(getinfo('template_dir') . 'main-start.php');
 
сам вывод данных
 
# конечная часть шаблона
require(getinfo('template_dir') . 'main-end.php');

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

Очевидно, если всё это оформить в виде одного home.php, то разобраться в коде будет сложно не только стороннему вебмастеру, но и разработчику. Вот тут как раз и следует разбить файл на отдельные самостоятельные модули.

# начальная часть шаблона
require(getinfo('template_dir') . 'main-start.php');
 
// первый модуль
require(getinfo('template_dir') . 'type/units/unit01.php'); 
 
// второй модуль
require(getinfo('template_dir') . 'type/units/unit02.php'); 
 
// третий модуль
require(getinfo('template_dir') . 'type/units/unit03.php');
 
# конечная часть шаблона
require(getinfo('template_dir') . 'main-end.php');

То есть мы вынесли модули в подкаталог type/units по отдельным файлам.

Другой пример: пусть у нас будет сложный компонент шапки. Под сложностью я имею ввиду тот факт, что компонент состоит из нескольких файлов, скажем это какой-то сторонний скрипт. Хотелось бы его с минимальной переделкой подключить к шаблону.

Для этого скрипт располагаем в каталоге stock-файлов, например так stock/my_script/my_script.php. Здесь будут находиться и все остальные файлы скрипта. Теперь делаем в каталоге компонентов (/components/) файл, подключающий my_script.

$fn = getinfo('template_dir') . 'stock/my_script/my_script.php';
if (file_exists($fn)) require($fn);

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

И последний, но достаточно важный момент - как использовать модульность для css-стилей блоков. Если с js-файлами всё достаточо просто: они могут подключаться в любом месте html-страницы, то css-файл следует подключать в секции HEAD. Это приводит к некоторым сложностям, поскольку у нас нет механизма автоматического «подхвата» модульного css-файла.

Если вы используете LESS/CSS, то ситауцию достаточно легко «разрулить», если воспользоваться моим методом организации less-файлов. В кратком изложении: less-файл модуля располагается в css-подкаталоге и подключается к основному var_style.less обычным @import. CSS-код попадает в общий var_style.css и о дополнительных подключениях вообще можно не заботиться.

Если же требуется подключение именно css-файла модуля, то делается это стандартным способом через файл custom/head.php:

mso_add_file('stock/my_script/my_script.css');

Минус этого подхода в том, что нужно вручную отслеживать подключаемые css-файлы и дополнительном http-соединении с сервером.

Оставьте свой комментарий!

Комментарий будет опубликован после проверки

Вы можете войти под своим логином или зарегистрироваться на сайте.

(обязательно)