» » Пишем свой модуль для DLE - подробная инструкция

Пишем свой модуль для DLE - подробная инструкция

29.09
68
50 812
Пишем свой модуль для DLE - подробная инструкция

В этой статье я постараюсь очень подробно разобрать процесс создания простого модуля для DLE с кешированием и собственными шаблонами. Сначала разберём модуль без шаблона, а после дополним его собственным шаблоном. Итогом статьи будет работоспособный модуль без админки, вызываемый в любом месте сайта через строку подключения.

Вступление


На написание данного поста меня натолкнул один вопрос на сайте dle-faq.ru. Точнее заставил задуматься, почему люди усложняют себе жизнь постоянными правками движка там, где это по большому счёту не нужно. Причина оказалась очень проста - мало кто умеет и знает как правильно написать свой модуль для DLE, хотя это довольно таки просто на самом деле.
Сразу стоит оговориться, что я человек, имеющий слабое знание даже основ php (что не мешает мне делать качественные модули для dle), поэтому статья для опытного программиста скорее всего не будет представлять интерес, а вот для новичка - будет крайне полезна.

С чего начать?


Пишем свой модуль для DLE - подробная инструкция

Конечно же начинать лучше с идеи или исходя из конкретной потребности!
В нашем случаи будем рассматривать необходимость вывода в профиле пользователя в любом месте сайта количество новостей определённого пользователя в определённых категориях.
Таким образом мы уже определили пару переменных, которые будут в нашем модуле - это имя пользователя и id категории.
Естественно результат работы модуля лучше кешировать т.к. лишние запросы в БД нам совершенно не нужны. Так же нам не нужен шаблон модуля, но для примера я приведу код модуля и с шаблоном, т.к. правильное подключение шаблона тоже очень важно и при более или менее сложном модуле экономит много ресурсов за счёт уменьшения кода самого модуля.
Круг задач определён, можно приступать к написанию кода. Вспоминаем что у DLE есть API, и вполне логичным кажется использование готового API для этой задачи, но я крайне не рекомендую вообще его использовать даже (особенно!) в сложных модулях.

Почему не стоит использовать DLE_API


Всё просто - это крайне кривая штука, которая не развивается аж с версии 8.2 (на момент написания текущая версия движка - 10.0 и по сравнению с предыдущей версией пофиксили лишь баг с невозможностью регистрации пользователя через api, никаких доработок не производилось).
Пишем свой модуль для DLE - подробная инструкция

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

Пишем код


Прежде, чем писать любой модуль (кроме файлов, отвечающих за ajax), нужно в обязательном порядке, в самом начале прописать одну строку:
if (!defined('DATALIFEENGINE')) die("Error!");

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

Конфигурация и кеширование


Я решил остановиться на этом моменте подробно, т.к. это один из самых частых вопросов, которые задаются, если человек решил написать свой модуль.

Конфигурацию модуля лучше всего записывать в массив - это даст возможность беспроблемного создания кеша для каждого вызова модуля с разным набором конфигурации, в нашем случаи для каждого пользователя.
Объясню почему. Допустим мы написали модуль, он кешируется, и строка создания кеша выглядит следующим образом:
create_cache($var1.$var2.$var3.$var4, $myModule, $config['skin']);

где:
- $var1.$var2.$var3.$var4 - переменные модуля.
- $myModule - текст, который должен записаться в кеш.

Тут всё замечательно ровно до тех пор, пока не понадобится добавить новую переменную. Как правило автор просто забывает прописать эту новую переменную в строку формирования и получения кеша и потом гадает над причинами неработоспособности или неправильной работы модуля (что негативно сказывается на его психологическом равновесии). Таким образом конфиг модуля лучше писать так:
$myConfig = array(
'var1' => $var1,
'var2' => $var2,
'var3' => $var3,
'var4' => $var4
);
$cacheName = md5(implode('_', $myConfig));

а строка создания кеша будет такая:
create_cache($cacheName, $myModule, $config['skin']);

Таким образом нам вообще не придётся лазить в этот код никогда, всё будет происходить автоматом.

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

В DLE есть много дефолтных префиксов, но нас интересуют лишь те, которые автоматически чистятся при определённых условиях, поэтому приведу список таких префиксов:
  1. news, rss, comm - при добавлении новости или комментария.
  2. news, related, tagscloud, archives, calendar, topnews, rss - при добавлении новости.
  3. comm - при редактировании комментария.
  4. news, rss - при редактировании новости, при выcтавлении рейтинга
  5. news, full, comm, rss - при массовом удалении комментариев
  6. news, full, comm, tagscloud, archives, calendar, rss - при удалении новости

Значит в нашем случаи нужен префикс archives т.к. кеш модуля надо сбрасывать только при добавлении или удалении новости (он мне просто понравился, можно использовать и calendar и rss). Код конфига и создания кеша тут приводить не буду дабы не захламлять статью, весь код модуля можно посмотреть ниже.

Текст кеша - это результат работы модуля, который будет записан в кеш, тут всё просто.
ID кеша или его имя - сюда лучше всего передавать переменную $cacheName, о которой писалось выше и переменную $config['skin'] - это для того, чтобы иметь разные кеши для разных шаблонов сайта.
Суффикс кеша - может принимать два значения true или false, если передано значение true, то для каждой группы пользователей будет создаваться свой кеш-файл, это бывает нужно, если разным группам пользователей нужно показывать разный контент.

Универсальная заготовка для модуля с кешем, без шаблона


Учитывая всё выше написанное мы можем создать простую заготовку для модуля, который будет использовать кеш, но не будет в своей работе использовать шаблон. Всего 15 строк кода!
<?php
if (!defined('DATALIFEENGINE')) die("Go fuck yourself!");
$myConfig = array(
'cachePrefix' => !empty($cachePrefix) ? $cachePrefix : 'archives',
'cacheSuffix' => !empty($cacheSuffix) ? $cacheSuffix : false
);
$cacheName = md5(implode('_', $myConfig));
$myModule = false;
$myModule = dle_cache($myConfig['cachePrefix'], $cacheName . $config['skin'], $myConfig['cacheSuffix']);
if (!$myModule) {
$myModule = 'Hello World!'; // Результат работы модуля.
create_cache($myConfig['cachePrefix'], $myModule, $cacheName . $config['skin'], $myConfig['cacheSuffix']);
}
echo $myModule;
?>

Всё довольно просто, правда?

Запрос в БД и проверки.


Ранее мы определили, что нам потребуется две переменные - имя юзера и id категории, а для выборки нужных значений нам необходимо будет составить запрос в БД.
Заведём две переменные: $userName и $caId - эти переменные будут передаваться в модуль через строку подключения.
Однако для нормальной работы модуля нужно проверять данные (ведь от кривых рук спасения практически не существует).

Небольшое отступление:
Я советую использовать однообразный тип переменных модуля и переменных конфига модуля, т.е.
'userName' => $userName,

или
'user_name' => $user_name,

выглядит гораздо более читабельным, чем
'user_name' => $userName,

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


Самая простая проверка - условие if else:
$userName = !empty($userName) ? $db->safesql(strip_tags(stripslashes($userName))) : false;

т.е. мы просто проверяем передана ли переменная через строку подключения, если передана (т.е. не пустая) - прогоняем её значение через $db->safesql - это обезопасит нас от "неправильных" логинов пользователей, т.к. значение переменной будет вставлено в запрос к БД.
Переменную $catId фильтровать не нужно, т.к. она кроме как цифрой никак не задаётся и глупо писать что-то другое в строке подключения. Однако должно быть какое-то дефолтное значение, поэтому проверка нужна и её мы можем прописать непосредственно в конфиге модуля:
$myConfig = array(
...
'catId' => !empty($catId) ? $catId : '1', // По умолчанию id=1
...
);

С переменными разобрались, теперь запрос в БД.
В нашем случаи необходимо всего одно значение из БД и использовать полноценный запрос, а потом его разбирать - не имеет смысла, поэтому мы будем использовать метод super_query, он по умолчанию возвращает одномерный массив.
Наш итоговый запрос будет таким:
$row = $db->super_query("SELECT COUNT(*) as count FROM " . PREFIX . "_post WHERE category = '".$myConfig['catId']."' AND autor = '".$myConfig['userName']."'");

Где:
$myConfig['catId'] - id категории.
$myConfig['userName'] - имя пользователя.
ниже пропишем отладочный код:
echo "<pre>"; print_r($row); echo "</pre>";

Вызов модуля осуществляем так:
{include file="engine/modules/mymodule.php?&userName=user&catId=5"}

Если переменные указаны правильно - результатом отладки будет массив с данными, состоящий из одного элемента count
Array (
[count] => 155
)

где:
155 - количество новостей у указанного пользователя в указанной категории

Теперь можно вывести результат по нормальному:
$count = ($row['count'] > 0) ? $row['count'] : 'новостей нет';
$myModule = $count;


Тут следует отметить, что если в кеш будет записан один нолик - dle его не будет "считать кешем" и создаст новый, поэтому нужно писать туда что-то, отличное от нуля.


Итоговый код нашего модуля будет таким:
<?php
if (!defined('DATALIFEENGINE')) die("Go fuck yourself!");
$userName = !empty($userName) ? $db->safesql(strip_tags(stripslashes($userName))) : false;
$myConfig = array(
'userName' => $userName,
'catId' => !empty($catId) ? $catId : '1',
'cachePrefix' => !empty($cachePrefix) ? $cachePrefix : 'archives',
'cacheSuffix' => !empty($cacheSuffix) ? true : false
);
$cacheName = md5(implode('_', $myConfig));
$myModule = false;
$myModule = dle_cache($myConfig['cachePrefix'], $cacheName . $config['skin'], $myConfig['cacheSuffix']);
if (!$myModule) {
$row = $db->super_query("SELECT COUNT(*) as count FROM " . PREFIX . "_post WHERE category = '" . $myConfig['catId'] . "' AND autor = '" . $myConfig['userName'] . "'");
$count = ($row['count'] > 0) ? $row['count'] : 'новостей нет';
$myModule = $count;
create_cache($myConfig['cachePrefix'], $myModule, $cacheName . $config['skin'], $myConfig['cacheSuffix']);
}
echo $myModule;
?>

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

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

Универсальная заготовка для модуля с кешем и собственным шаблоном


В целом тот же модуль, что мы написали без шаблона вполне легко перетащить и на "шаблонный" модуль, однако в нашем случаи это не имеет смысла, т.к. модуль выводит всего одну цифру. Собственный шаблон в модуле лучше использовать когда необходимо вывести несколько значений с разным оформлением в разных местах сайта.
Не буду расписывать что к чему, т.к. статья уже и так довольно таки большая, а лучше оставлю два листинга кода, один без комментариев - для использования в качестве заготовки:
<?php
if (!defined('DATALIFEENGINE'))
die("Go fuck yourself!");
$userName = !empty($userName) ? $db->safesql(strip_tags(stripslashes($userName))) : false;
$myConfig = array(
'userName' => $userName,
'catId' => !empty($catId) ? $catId : '1',
'template' => !empty($template) ? $template : 'default',
'cachePrefix' => !empty($cachePrefix) ? $cachePrefix : 'news',
'cacheSuffix' => !empty($cacheSuffix) ? true : false
);
$cacheName = md5(implode('_', $myConfig));
$myModule = false;
$myModule = dle_cache($myConfig['cachePrefix'], $cacheName . $config['skin'], $myConfig['cacheSuffix']);
if (!$myModule) {
if (file_exists(TEMPLATE_DIR . '/' . $myConfig['template'] . '.tpl')) {
if (!isset($tpl)) {
$tpl = new dle_template();
$tpl->dir = TEMPLATE_DIR;
} else {
$tpl->result['myModule'] = '';
}
$tpl->load_template($myConfig['template'] . '.tpl');
$row = $db->super_query("SELECT COUNT(*) as count FROM " . PREFIX . "_post WHERE category = '" . $myConfig['catId'] . "' AND autor = '" . $myConfig['userName'] . "'");
$count = ($row['count'] > 0) ? $row['count'] : 'новостей нет';
$tpl->set('{tag_name}', $count);
$tpl->compile('myModule');
$myModule = $tpl->result['myModule'];
create_cache($myConfig['cachePrefix'], $myModule, $cacheName . $config['skin'], $myConfig['cacheSuffix']);
$tpl->clear();
} else {
$myModule = '<b style="color:red">Отсутствует файл шаблона: ' . $config['skin'] . '/' . $myConfig['template'] . '.tpl</b>';
}
}
echo $myModule;
?>

А второй с подробными комментариями, для понимания что-к-чему:


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

Похожие материалы

Комментарии

Korobasow
Korobasow 29 сентября 2013 21:57
Спасибо!
Доходчиво!
HbIXA
HbIXA 7 октября 2013 11:01
Вот это помойму лишнее, эта проверка при выполнении load_template итак проходит. А так все замечательно.

if (file_exists(TEMPLATE_DIR . '/' . $myConfig['template'] . '.tpl')) {
		if (!isset($tpl)) {
			$tpl      = new dle_template();
			$tpl->dir = TEMPLATE_DIR;
		} else {
			$tpl->result['myModule'] = '';
		}


Если отсутствует $tpl, то скорее и TEMPLATE_DIR тоже =). Правильнее указать через конфиг сайта папку с шаблоном, или вставить свою.
ПафНутиЙ
ПафНутиЙ 7 октября 2013 12:09
Цитата: HbIXA
Вот это помойму лишнее, эта проверка при выполнении load_template итак проходит.

Это на случай, если используется переменная, отличная от $tpl. По большому счёту можно и опустить этот код, но вот когда возникают проблемы - найти решение гораздо сложнее, чем удалить лишнее в данном конкретном модуле.
HbIXA
HbIXA 7 октября 2013 12:13
ПафНутиЙ, это да, может тогда лучше будет, описать возможность подключения с папкой и без
ПафНутиЙ
ПафНутиЙ 7 октября 2013 12:16
Я считаю, что правильнее подключать шаблоны модуля из собственной папки (ШАБЛОН_САЙТА/ИМЯ_МОДУЛЯ/шаблон.tpl) иначе запутаешься совсем в собственных шаблонах при росте проекта)
HbIXA
HbIXA 7 октября 2013 12:41
ПафНутиЙ, ну вот я как раз это и имею ввиду, просто все будут после статьи клепать в корень шаблона. Статья конечно итак большая, но кому интересно, для подключения шаблона из папки:
меняем
if(file_exists(TEMPLATE_DIR.'/'.$myConfig['template'].'.tpl')) {

на
if(file_exists(TEMPLATE_DIR.'/ваша папка или название модуля например из конфига/
'.$myConfig['template'].'.tpl')) {

меняем
$tpl->load_template($myConfig['template'].'.tpl');

на
$tpl->load_template('/ваша папка или название модуля например из конфига/'.$myConfig['template'].'.tpl');


ну и создаем с соответсвующим названием папку в корне шаблона, например /templates/Default/myModule
ПафНутиЙ
ПафНутиЙ 7 октября 2013 12:56
Зачем усложнять? Ведь не все проекты - крупные и содержат вагон разных шаблонов на разные условия. Вот такой код вполне будет достаточен:
{include file="engine/modules/mymodule.php?template=myfolder/mytemplate"}


К тому же приведённый в статье код - заготовка, т.е. просто указание верного направления при разработке собственного модуля.
По хорошему тут много чего не хватает и можно превратить эту заготовку в некий универсальный модуль, в который нужно будет подставить только запрос, но в таком случаи код разрастётся во много раз и новичек не сможет понять что тут такое вообще происходит, а опытному проще будет написать свой модуль, на основе кода без комментариев)
HbIXA
HbIXA 7 октября 2013 13:02
ПафНутиЙ,
Давно не было подобных статей и все устарели, правильно сделал, что выбрал данную тему, её и продолжать можно, изучая нюансы/прелести дле.
websot
websot 16 октября 2013 17:10
Спасибо Паш, изучаю язык PHP, но я честно говоря единственное не могу понять как реализовать ($pafnuty_name) такой пример))
ПафНутиЙ
ПафНутиЙ 16 октября 2013 17:14
не понял что за пример.
websot
websot 16 октября 2013 17:19
ну вот это $pafnuty_name - чтоб в php файле работал нормально, допустим так....
<?php
$pafnuty_name = 'Павел';
echo $pafnuty_name;
?>
Привет $pafnuty_name, как жизнь? (вместо $pafnuty_name был Павел)
ПафНутиЙ
ПафНутиЙ 16 октября 2013 17:20
http://dle-faq.ru/faq/questhacks/76-ustanovka-username.html
websot
websot 16 октября 2013 17:24
спасибо за ссылку.
Если будем время то напиши статью про "смс оповещение с помощью Email сервера" иои что то подобие такого, просто читал про такую схему и честно говоря незнаю, возможно ли так или нет
Примечание
Долго добавляет комментарии
HbIXA
HbIXA 16 октября 2013 17:29
ПафНутиЙ, для меня так удобнее в шаблоны просто вставлять {list} например. В твоем случае если модуль небольшой, а если шаблонов около 10, и нужно путь поменять, не так уж удобно. А для небольших не спорю.

PS. Паш, в твоем блоге неудобно, что авторизация только на главной сделай всплывающее окно чтоли, когда не на главной. Я вот сейчас с почты зашел, и пришлось, чтобы ответить искать авторизацию =(
ПафНутиЙ
ПафНутиЙ 16 октября 2013 17:32
Да, самому неудобно иногда. Сделаю как время пявится сквозняком этот блок. Да и вообще меню немного переделать надо бы, а то не умещается всё, что хочется.
OTM
OTM 26 октября 2013 13:02
в заготовках при создании кеша используется
$config['skin']

без подключения длешного конфига engine/data/config.php , либо без удаления этой переменной (т.е. при использовании шаблона в чистом виде) у меня кеш не создаётся
ПафНутиЙ
ПафНутиЙ 26 октября 2013 21:39
странно, т.к. при размещении модуля в папке с модулями конфиг не требует подключения.
OTM
OTM 26 октября 2013 21:59
Действительно. Значит актуально только при подключении из подкаталога:
modules/bla-bla-bla/
ПафНутиЙ
ПафНутиЙ 29 октября 2013 18:28
проверю сегодня, если не забуду.
D0Gmatist
D0Gmatist 31 октября 2013 12:52
Решил опробовать... Рассыпается структура сайта. на картинке показано...
в чём может быть проблема?

Вопрос закрыт .. сам понял что ступил...
zloy_admin
zloy_admin 5 ноября 2013 09:59
ПафНутиЙ, Спасибо за статью!
А можешь так же расписать создание модуля но только с админкой?
ПафНутиЙ
ПафНутиЙ 5 ноября 2013 11:26
возможно в следующей статье будет.
Aper
Aper 8 ноября 2013 19:35
Thank you very mach Pafnuty, i have just made an image crop module and i have used your codes to create cache files. Just little remark:
Префикс кеша - может принимать два значения true или false...
I think it is about sufix not prefix.
Thanks.
ПафНутиЙ
ПафНутиЙ 8 ноября 2013 20:07
Да, опечатка, именно суффикс.
Поправил текст.
DeL Esprit
DeL Esprit 5 октября 2014 18:17
А как сделать что бы теги которые я создал в модуле, можно было использовать в других файлах шаблонов, например "фуллстори"?
ПафНутиЙ
ПафНутиЙ 5 октября 2014 22:46
Подключить модуль в нужный шаблон.
DeL Esprit
DeL Esprit 5 октября 2014 22:49
подключаю вместо своего шаблона

$tpl->load_template('fullstory.tpl');


выдает:
Fatal error: Maximum function nesting level of '100' reached, aborting! in K:\home\dle\www\engine\classes\templates.class.php on line 141
ПафНутиЙ
ПафНутиЙ 5 октября 2014 22:53
вы подключаете в шаблоне fullstory шаблон fullstory, и вгоняете модуль в бесконечную рекурсию.
DeL Esprit
DeL Esprit 6 октября 2014 12:49
ПафНутиЙ,
ага.. спасибо.
а где подключить модуль тогда нужно? в маин.тпл?
или я чего не понял )))
я модуль выводил вот таким образом в fullstory.tpl
{include file="engine/modules/mymodule/info.php"}


где его теперь вызвать? или как поступить?
DeL Esprit
DeL Esprit 6 октября 2014 13:03
блин, так и не понял где тогда вызывать работу модуля, что бы подключить теги в фуллстори шаблон.

подскажите )

Добавить комментарий

Комментировать могут только зарегистрированные пользователи

Информация

Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.