Всем доброго времени суток. Сегодня мы с Вами познакомимся с таким понятием как «Кабинет» в CMS RS-MINI, и реализуем алгоритм который позволит работать с этими самыми кабинетами. Давайте сначала поймем, что я подразумеваю под словом «Кабинет»
Сайт на CMS делятся на две (минимум) части:
это для Вас не ново, подобное деление встречается почти в каждом движке. Так вот, в системе RS-MINI публичную часть сайта будет принято называть публичным кабинетом, а админ панель — кабинетом администратора.
Как известно, обычно кабинет администратора реализуется как сайт внутри сайта. Я имею ввиду что у админки имеется свой набор функционала, шаблонов и стилей. Ранее мы бы с Вами создали папку admin поместили туда точку входа index.php, свои стили и шаблоны, ну и конечно же написали бы очень много кода который позволил бы управлять содержимым сайта.
При реализации RS-MINI мы так поступать не будем. У нас есть дерево узлов (таблица rs_map), и нам не составит труда создать узел admin. Так же наш движок умеет подключать и исполнять контроллеры (то есть та самая портянка кода). Нам остается решить вопрос с подключением своих стилей и шаблонов (про подключение шаблоны говорить будем не сегодня)
Наша файловая система разбита на два глобальных каталога:
Подразумевается, что все что касается самой системы будет лежать в каталоге rs-mini, а все что касается не посредственно самого сайта — в project. Вы могли заметить, что и в том и в том каталоге есть такие папки как:
Наличие подобного разбития позволит нам отделить визуальное оформление кабинетов. По сути, нам всего лишь нужно решить, из какой папки (rs-mini или project) тянуть стили, скрипты, картинки и шаблоны когда мы находимся на той или иной странице сайта.
Кстати, у системы RS-MINI помимо кабинета администратора так же будет кабинет разработчика, в котором нам, как разработчикам, будут доступны инструменты для сборки других кабинета.
Начнем реализацию алгоритма, определения откуда тянуть оформление кабинета, с создания таблицы где мы будем хранить информацию о кабинетах
CREATE TABLE IF NOT EXISTS `rs_room` (
`room_id` int(11) NOT NULL AUTO_INCREMENT,
`room_name` varchar(255) NOT NULL,
`room_section` enum('project','rs-mini') NOT NULL,
`room_folder` varchar(255) NOT NULL,
`room_path` varchar(255) NOT NULL,
`room_access` enum('0','1','2','3','4') NOT NULL,
PRIMARY KEY (`room_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
INSERT INTO
`rs_room`
(
`room_id`,
`room_name`,
`room_section`,
`room_folder`,
`room_path`,
`room_access`
)
VALUES
(
1,
'Публичный кабинет',
'project',
'pub',
'/',
'0'
),
(
2,
'Кабинет администратора',
'rs-mini',
'admin',
'/admin/',
'3'
)
;
Данный скуль запрос создаст таблицу rs_room и вставит информацию о двух кабинетах:
В таблице будут следующие поля:
Что нам нужно сделать, что бы определить в каком кабинете мы находимся? Нам нужно взять урл из адресной строки и сравнить его с урлом в поле room_path. При совпадении урлов нужно будет сохранить информацию о найденном кабинете в специальном свойстве класса.
Доступ к этому свойству мы будем получать через магический метод __callStatic() (это тоже самое что и метод __call() только для статичных методов)
Вроде все просто, но тут есть несколько проблем.
Если у нас есть кабинет у которого в поле room_path прописано /admin/, то заходя на узел /admin/ у нас все определиться верно, ведь /admin/ равен /admin/. А что будет, если мы зайдем вот на такой узел:
/admin/document/1/edit/
Правильно, ничего хорошего не будет, так как простым сравнением поля room_path и значением из адресной строки, кабинет мы (корректно) не найдем. Единственный (ну может не единственный) способ — это отрезать у значения из адресной строки столько же узлов сколько и в поле room_path. То есть при заходе на страницу
/admin/document/1/edit/
сохранить только узлы (два узла как и в поле room_path)
тем самым получим тоже равенство /admin/ равно /admin/
Если у нас есть два кабинета чьи поля room_path равны:
и мы заходим на узел /admin/, то большая вероятность того, что движок ошибочно решит, что мы находимся в публичном кабинете, ведь алгоритм при проверки приведет урлы к равенству / равно /. Что бы этого избежать, нужно позаботится о том, что бы RS-MINI проверял сначала кабинеты чье поле room_path имеет большое кол-во узлов (в нашем случае проверка должна будет начинаться не с публичного кабинета, а с админского, так как у админсткого кабинета узлов больше)
В общем, давайте уже посмотрим код класса (файл лежит в папке /rs-mini/core/ и называется room.class.php)
<?php
/*
* @package RS-MINI
* @copyright (c) 2015 Alexey Glumov aka Rio-Shaman (support@rio-shaman.ru)
* @license GNU General Public License version 2; see LICENSE.txt
*
*/
namespace core;
if(!defined('RS-MINI')) die();
/*
* room v 1.0
*
* класс по работе с кабинетом системы
*
*/
class room
{
/*
* инфа о кабинете
*
* @var - array
* @access - private
*/
private static $room = NULL;
/*
* метод имитирует вызов методов получения
* инфы о кабинете. к таким методам относятся:
* - ::getID() - id кабинета
* - ::getName() - имя кабинета
* - ::getFolder() - физ папка кабинета
* - ::getSection() - секция (ядро или проект)
* - ::getPath() - путь на сайте до кабинета
* - ::getAccess() - доступ к кабинету
*
* @access - public
* @return - информация кабинета или NULL
*
* @param string name - имя вызываемого метода
* @param array argumentList - список аргументов
*/
public static function __callStatic($name, $argumentList)
{
// приводим к нижнему регистру
$name = mb_strtolower($name);
// в $argument помещаем часть имени метода
$argument = mb_substr($name, 3, mb_strlen($name)-1);
// если вызываемый метод начинается не на get
if (mb_substr($name, 0, 3) != 'get')
// вертаем null
return NULL;
// вертаем информацию, если она имеется или null если ее нет
return (isset(self::$room['room_' . $argument])) ? self::$room['room_' . $argument] : NULL;
}
/*
* метод устанавливает кабинет
*
* @access - public
* @return - возвращает TRUE в случае успеха или FALSE в случае неудачи
*/
public static function setRoom()
{
// определяем кабинет
// получаем адрес страницы
$uri = request::getHttpServer('REQUEST_URI')->toUri();
// бьем строку на массив
$uri = explode('/', $uri);
// удаляем последний элемент массива, так как он всегда пустой
unset($uri[(count($uri) - 1)]);
// смысл в том что нам необходимо отсортировать пути к кабинетам от большего
// кол-ва узлов к меньшему.
// например если у нас имеется два кабинете
// 1. публичный /
// 2. кабинет администратора /admin/
// то видим что до публичного кабинета ОДИН узлел а до админского ДВА узла.
// стало быть если мы сейчас на странице /admin/document-a/ то скрипт должен
// понять что мы в админском кабинете, а не в публичном (в адресе к публичному
// кабинету ведь тоже присутствует ПЕРВЫЙ узел). если не отсортировать по
// кол-ву узлов, то скрипт может закончить обработку на публичном кабинете
// не сверяясь с админским. если же первым делом скрипт будет проверять тот
// кабинет в котором больше узлов, то ошибка выдать неверный кабинет упадет
// до нуля
// массив в который сложим кабинеты предварительно подсчитав кол-во узлов
$roomArr = array();
// получаем список кабинетов
if (!$roomList = self::getRoomList())
return FALSE;
// листаем кабинеты
foreach ($roomList as $roomKey => $room) {
// бьем путь до кабинета на массив
$path = explode('/', $room['room_path']);
// наполняем массив кабинетом с подсчетом количества узлов до него
$roomArr[] = array(
'count' => count($path),
'room' => $roomList[$roomKey]
);
}
// сортируем кабинеты по полю count от большего к меньшему
usort($roomArr, array('\core\room', 'fSort'));
// листаем кабинеты
foreach ($roomArr as $roomKey => $room) {
// делаем из пути массив
$path = explode('/', $room['room']['room_path']);
// удаляем последний элемент массива, так как он всегда пустой
unset($path[(count($path) - 1)]);
// вытаскиваем из пути к странице столько же элементов сколько до кабинета
// массив проверки
$checkPath = array();
// листаем узлы вбитые в адресную строку
foreach ($uri as $key => $alias)
// если ключ (0,1,2,3...n) меньше кол-ву узлов в кабинете
if ($key < count($path))
// заполняем стек узлом из адресной строки
$checkPath[] = $alias;
// сравниваем пути
if ($path == $checkPath) {
// если они равны, значит кабинет мы нашли, запоминаем его
self::$room = $roomArr[$roomKey]['room'];
// назначаем константы для быстрого доступа
// основываясь на имя секции
define('IMG', '/' . self::getSection() . '/img/'); // путь к папке img кабинета
define('JS', '/' . self::getSection() . '/js/'); // путь к папке js кабинета
define('CSS', '/' . self::getSection() . '/css/'); // путь к папке css кабинета
// контент всегда лежит в проекте
define('CONTENT', DIR_PROJECT . '/content/'); // полный путь к папке контента
define('SHORT_CONTENT', SHORT_DIR_PROJECT . '/content/'); // короткий путь к папке контента
break;
}
}
return (is_null(self::$room)) ? FALSE : TRUE;
}
/*
* статичный пользовательский метод для сортировки массива
*
* @access - public
*
*/
public static function fSort($a, $b)
{
if ($a['count'] == $b['count'])
return 0;
return ($a['count'] > $b['count']) ? -1 : 1;
}
/*
* метод получает список кабинетов
*
* @access - public
* @return - массив с кабинетами
*
*/
private static function getRoomList()
{
$sql = ''
. ' SELECT'
. ' r.room_id AS room_id,'
. ' r.room_name AS room_name,'
. ' r.room_folder AS room_folder,'
. ' r.room_section AS room_section,'
. ' r.room_path AS room_path,'
. ' r.room_access AS room_access'
. ' FROM'
. ' rs_room AS r'
;
return sql::query($sql)->fetch();
}
}
Код хорошо документирован, думаю проблем с понимание быть не должно (если есть какие либо вопросы, пишите их в комментариях)
Давайте подключим наш только что написанный класс. Так же было бы не плохо написать пару строк для тестирования проделанной работы. Заходим в application.class.php что лежит в папке /rs-mini/core/ и перепишем метод run()
/*
* главный метод. запуск системы
*
* @access - public
*
*/
public static function run()
{
// поднимаем некоторые классы ядра
// объект по работе с структурой сайта
self::$map = new map();
// объект прослойка между контроллерами и вьюшкой
self::$dom = new dom();
// определяем кабинет
if (!room::setRoom())
self::notFound();
// смотрим какой определен кабинет
?><xmp><?
var_dump(room::getName());
var_dump(CSS);
var_dump(IMG);
var_dump(JS);
var_dump(CONTENT);
var_dump(SHORT_CONTENT);
?></xmp><?
die();
// получаем открываемую страницу
if (!self::$map->getPage())
self::notFound();
// мероприятия по объеденению унаследованных контроллеров с конечной страницей
self::unionRun();
// запускаем контроллеры
self::controllerRun();
}
Обратите внимание, что метод setRoom() класса room.class.php помимо сохранения информации о кабинете (в свойство self::$room), объявляет некоторые константы, для упрощения формирования путей к стилям (CSS), картинкам (IMG), скрипта (JS) и контенту сайта (CONTENT и SHORT_CONTENT). Я вывел содержимое этих констант (сразу после вызова метода setRoom()), что бы удостовериться, что все работает так как надо.
Ну что же, у нас в базе зарегистрировано два кабинета:
давайте протестируем алгоритм.
Для начала заходим на главную (в публичный кабинет) страницу сайта. Вот что мы должны увидеть:
Как и ожидалось, система поняла, что мы в публичном кабинете.
Теперь давайте перейдем вот по такому адресу http://mini.test.ru/admin/ (кабинет администратора), мы должны увидеть вот такую картинку:
Собственно все верно, система поняла что мы перешли в кабинет администратора.
На по следок давайте перейдем на страницу http://mini.test.ru/about/ (это адрес публичного кабинета). Мы должны увидеть вот такую картинку:
Исходя из увиденного могу сказать, что система определяет кабинеты корректно, чему я собственно рад
Если у Вас есть какие либо вопросы, то задавайте их в комментариях. Результат проделанной работы Вы как всегда можете скачать в конце статьи.
Всего Вам наилучшего, на сегодня у меня все!
Доброго времени суток.
Этот материал создан не для того что бы втюхать пользователю какую-то новую CMS, а для того что бы он (пользователь) смог написать свой мини движок под себя.
К сожалению цикл не закончен, и неизвестно когда я смогу собраться с силами и дописать его :(