Кабинеты в системе RS-MINI

 

Всем доброго времени суток. Сегодня мы с Вами познакомимся с таким понятием как «Кабинет» в CMS RS-MINI, и реализуем алгоритм который позволит работать с этими самыми кабинетами. Давайте сначала поймем, что я подразумеваю под словом «Кабинет»

Сайт на CMS делятся на две (минимум) части:

  • публичная — это непосредственно сам сайт с контентом для конечного пользователя
  • и админ панель — место где администратор сайта может наполнять и видоизменять свой сайт

это для Вас не ново, подобное деление встречается почти в каждом движке. Так вот, в системе RS-MINI публичную часть сайта будет принято называть публичным кабинетом, а админ панель — кабинетом администратора.

Как известно, обычно кабинет администратора реализуется как сайт внутри сайта. Я имею ввиду что у админки имеется свой набор функционала, шаблонов и стилей. Ранее мы бы с Вами создали папку admin поместили туда точку входа index.php, свои стили и шаблоны, ну и конечно же написали бы очень много кода который позволил бы управлять содержимым сайта.

При реализации RS-MINI мы так поступать не будем. У нас есть дерево узлов (таблица rs_map), и нам не составит труда создать узел admin. Так же наш движок умеет подключать и исполнять контроллеры (то есть та самая портянка кода). Нам остается решить вопрос с подключением своих стилей и шаблонов (про подключение шаблоны говорить будем не сегодня)

Наша файловая система разбита на два глобальных каталога:

  • project
  • и rs-mini

Подразумевается, что все что касается самой системы будет лежать в каталоге rs-mini, а все что касается не посредственно самого сайта — в project. Вы могли заметить, что и в том и в том каталоге есть такие папки как:

  • css
  • img
  • js
  • view

Наличие подобного разбития позволит нам отделить визуальное оформление кабинетов. По сути, нам всего лишь нужно решить, из какой папки (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_name — имя кабинета
  • room_section — в какой из двух глобальных каталогов лежат необходимые для кабинета файлы
  • room_folder — Вы могли заметить, что в папке view (в папке будут лежать главные и узловые шаблоны кабинетов) каталога rs-mini есть два подкаталога:
    • admin
    • configurator
    это поле позволит определить в какой из этих двух папок лежат необходимые шаблоны для кабинета (такая же ситуация и для папки view из каталога project)
  • room_path — узел наткнувшись на который система поймет что она зашла в тот или иной кабинет.
  • room_access — доступ к кабинету. В системы будут следующие уровни доступа:
    • 0 — все пользователи сайта
    • 1 — зарегистрированный пользователь
    • 2 — оператор системы (это такой чувак который будет разгребать всякие заявки от пользователей)
    • 3 — администратор сайта
    • 4 — разработчик сайта
    Данное поле не влияет на сам доступ к кабинету, а лишь поможет нам определить показать ли пользователю кабинет в списке кабинетов, или нет.

Реализуем класс room.class.php

Что нам нужно сделать, что бы определить в каком кабинете мы находимся? Нам нужно взять урл из адресной строки и сравнить его с урлом в поле 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/ равно /admin/

Проблема номер два

Если у нас есть два кабинета чьи поля room_path равны:

  • / — публичный кабинет
  • и /admin/ — кабинет администратора

и мы заходим на узел /admin/, то большая вероятность того, что движок ошибочно решит, что мы находимся в публичном кабинете, ведь алгоритм при проверки приведет урлы к равенству / равно /. Что бы этого избежать, нужно позаботится о том, что бы RS-MINI проверял сначала кабинеты чье поле room_path имеет большое кол-во узлов (в нашем случае проверка должна будет начинаться не с публичного кабинета, а с админского, так как у админсткого кабинета узлов больше)

Код класса room.class.php

В общем, давайте уже посмотрим код класса (файл лежит в папке /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();
    }
}

Код хорошо документирован, думаю проблем с понимание быть не должно (если есть какие либо вопросы, пишите их в комментариях)

Подключение и вызов класса room.class.php

Давайте подключим наш только что написанный класс. Так же было бы не плохо написать пару строк для тестирования проделанной работы. Заходим в 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()), что бы удостовериться, что все работает так как надо.

Заключение

Ну что же, у нас в базе зарегистрировано два кабинета:

  • / — публичный
  • /admin/ — и кабинет администратора

давайте протестируем алгоритм.

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

публичный кабинет

Как и ожидалось, система поняла, что мы в публичном кабинете.

Теперь давайте перейдем вот по такому адресу http://mini.test.ru/admin/ (кабинет администратора), мы должны увидеть вот такую картинку:

кабинет администратора

Собственно все верно, система поняла что мы перешли в кабинет администратора.

На по следок давайте перейдем на страницу http://mini.test.ru/about/ (это адрес публичного кабинета). Мы должны увидеть вот такую картинку:

публичный кабинет

Исходя из увиденного могу сказать, что система определяет кабинеты корректно, чему я собственно рад Улыбаюсь

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

Всего Вам наилучшего, на сегодня у меня все!

Прикрепленные файлы

 

Возможно Вам будут интересны следующие заметки

Комментарии (4)

Ваше имя *
Сайт
Ваш E-mail *
Ваше сообщение *
 
Вы не подтвердили условия политики конфиденциальности.
Евгений, 30 Сентября 2017 г. 22:02 пишет:
Гость
В статье в коде класса room.class.php неверно определена константа как SHORTCONTENT вместо SHORT_CONTENT, из-за этого появляется ошибка. В архиве всё нормально.
Алексей, 02 Октября 2017 г. 12:17 пишет:
Автор
Спасибо, поправил.
Ответ для пользователя: Евгений
Василий (iklife.ru), 03 Октября 2017 г. 19:43 пишет:
Гость
Хотелось бы почитать о преимуществах данной C CMS RS-MINI, по сравнению скажем даже с wordpress, в чем преимущества, какие недостатки, достоинства, чтобы стало понятно нужно это пользователю или нет.
Алексей, 03 Октября 2017 г. 23:07 пишет:
Автор

Доброго времени суток.
Этот материал создан не для того что бы втюхать пользователю какую-то новую CMS, а для того что бы он (пользователь) смог написать свой мини движок под себя.
К сожалению цикл не закончен, и неизвестно когда я смогу собраться с силами и дописать его :(

Ответ для пользователя: Василий (iklife.ru)