Продолжаю работать над своей учебной CMS.

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

Много читал о роутинге на основе регулярных выражений. Но во первых мне кажется такой подход сложней в реализации. В этих «регулярках» без пол литра не разобраться. Во вторых у меня есть идея как сделать так, чтоб решение соответствовало требованиям и задачам моей CMS. Супер навороченный роутер мне на данном этапе не нужен. Он просто должен учитывать всё что нужно мне, не больше и не меньше.

Мне нужно было написать раутер для которого есть всего несколько условий.
В соответствии с моим с техническим заданием у меня всего 3 модуля: Панель управления, сайт и блог. А также должна быть возможность дописать при необходимости ещё модули. Я решил что все маршруты будут храниться в базе данных MySQL. Через PhpMyAdmin с создал таблицу cms_route. Теперь заполнил её тестовыми данными чтоб протестировать работу нового класса.

скриншот экрана моего ноутбука

Так же создал в папке публик создал папки соответствующие названиям модулей и в них классы контроллеров (да я буду стараться придерживаться MVC).
Например: blog/Controller.class.php.
В каждом классе есть метод startApp(). И дополнительно в класе pages/Controller.class.php добавил метод sitemap, а в классе blog/Controller.class.php добавил метод tags().

Работать всё это вместе должно следующим образом:

mysite/ — вызовет blog\Controller::startApp()
mysite/tags — вызовет blog\Controller::tags()
mysite/post-o-tovare — вызовет blog\Controller::startApp()
mysite/pages — вызовет pages\Controller::startApp()
mysite/abot — вызовет pages\Controller::startApp()
mysite/pages/sitemap — вызовет pages\Controller::sitemap()
mysite/pages-invent — вызовет pages\Controller::startApp()

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

В этот раз я впервые попробовал использовать тернарный оператор. Получилось, работает. Запись получается сильно короче чем обычная конструкция if() {…} else {…}. Но мне пока такой код писать сложнее. Вероятно нужно больше опыта.

Пока писал этот класс попутно пришлось немного изменить класс для работы с базой данных MySQL. А точнее метод select.
Теперь при простом запросе он возвращает простой массив, а не многомерный как раньше.

В кслассе получилось 3 приватных метода, 1 публичный (статический) и конструктор.

  • public bootstrap() — инициализирует класс
  • private loadingApp() — делает всю основную работу
  • private defaultApp() — спрашивает в базе какой модуль загружать по умолчанию
  • private searchApp() — спрашивает в базе какой модуль должен загрузитсья на основе URL

Чуть подробней о loadingApp. Проестировал его как мог, на первый взгляд всё работает как надо.
Этот метод делает следующее:

Сначала устанавливается переменная в которой хранятся параметры для загрузки по умолчанию

После чего проверяю если текущий URL соответствует значению по умолчанию, загружаю нужный метод необходимого класса. В данном случае это будет blog\Controller::startApp(). То есть по сути главная страница.

Если в URL после домена есть ещё что-то, тогда ищу есть ли это что-то в базе данных. Предположим нет там такого роута, значит загружаем роут по умолчанию. Но предворительно проверим может это имя метода такое. Тогда нужно загружать метод по умолчанию или тот который указан в адресной строке.

Ну и сам код класса:

<?php

class Route
{
    private static $instance;
    private $route = '/';
    private $action;

    private function __construct()
    {

        $url = new UrlParse;
        $this->route  = ($url->getByNumUrlPath(1) != '') ? $this->route  = $url->getByNumUrlPath(1) : '/';
        $this->action = ($url->getByNumUrlPath(2) != '') ? $this->action = $url->getByNumUrlPath(2) : 'startApp';
        $this->loadingApp();

    }

    /**
     * Инициализация класса
     *
     * @return void
     */
    public static function bootstrap()
    {

        if (!isset(self::$instance)) {
            self::$instance = new self();
        }
        return self::$instance;

    }

    /**
     * Загруззка необходимого модуля
     *
     * @return void
     */
    private function loadingApp()
    {

        $defaultApp = $this->defaultApp();

        if($this->route === $defaultApp['route']) {

            $loadApp = $defaultApp['app'] . '\Controller';
            $loadAct = $defaultApp['action'];
            
        } else {
            $searchApp  = $this->searchApp($this->route);

            if($searchApp === false) {
                $loadApp = $loadApp = $defaultApp['app'] . '\Controller';
                $loadAct = $this->route;

                if (!method_exists($loadApp, $loadAct)) {
                    $loadAct = $defaultApp['action'];
                }
            } else {
                $loadApp = $searchApp['app'] . '\Controller';
                $loadAct = $this->action;
                if(!method_exists($loadApp,$loadAct)) {
                    $loadAct = $searchApp['action'];
                }
            }
        }

        $app = new $loadApp;
        $app->$loadAct();

        // Вместо тестов, попытка проверить всё это безобразие на работоспособность
        
        /*echo '<div style="border:1px solid #000;background: aliceblue;padding: 20px">';
        echo '<h1>Testing class Route</h1>';
        echo '<b>Default:</b>';
        var_dump($defaultApp);
        echo '<b>Search:</b>';
        var_dump($searchApp);
        echo '<hr>';
        echo 'LoadApp - ' . $loadApp . '<br>';
        echo 'LoadAct - ' . $loadAct . '<br>';
        echo '</div>';*/

    }

    private function defaultApp()
    {
        $db  = new DatabaseMysql;
        $app = $db->select('SELECT `route`, `app`, `action` FROM `cms_route` WHERE `route` = ?', ['/']);
        return $app;
    }

    private function searchApp($search)
    {
        $db = new DatabaseMysql;
        $app = $db->select('SELECT `route`, `app`, `action` FROM `cms_route` WHERE `route` = ?', [$search]);
        return $app;
    }

}

Сегодня я активно задействовал ранее написанные мной классы UrlParse, DatabaseMysql и конечно за загрузку самих классов отвечает Autoloader.

Надеюсь получилось неплохо.

Продолжение следует…