Как в разы упростить разработку модулей для %some% CMS

253
.
Как в разы упростить разработку модулей для %some% CMS

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

С небольшими изменениями всегда можно переделать один модуль в другой. Например модуль новостей в модуль билиотеки или наобророт.
Порой даже будет достаточно изменить названия для объектов.

Так почему бы не сделать что нибудь вроде генератора модулей?
Попробуйте только представить себе сколько сразу времени может освободиться.
Идея не нова, была попытка создать универсальную цмс, с помощью которой из админки можно напрограммировать мышкой себе модуль.
(например http://boolive.ru/)

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

Для начала поговорим немного об объектах.

Пусть объект представляет собой множество объектов и тем временем сам может входить в какое нибудь множество объектов
Объкты выстроены в иерархические цепи.
Над объектами можно совершать два действия чтение и запись. На основе этих действий будут строится другие действия.

Уровни абстракции объектов.
-Каждый уровень представляет собой группу объектов одного типа
-На каждом уровне ничего не известно о существованиях других уровней и их свойствах.
Связями будет заниматься компоновщик объектов.
-Каждый уровень может предоставлять интерфейс для работы со свойствами объектов.

Что это дает? Это позволяет сделать модуль очень гибким, изменения в компонеты будут вноситься один раз.
Например у нас есть модуль библиотеки, есть категории и статьи, у статей два свойства Заголовок и текст.
Понадобилось добавить еще одно свойство. И вывести его везде где встречаются статьи. Если система маленькая, то особых сложностей это не вызовет,
Но чем больше система, тем больше вероятность допустить ошибку, плюс ко всему это достаточно рутинная работа.
А если вдруг понадобится изменить или удалить это свойство?
Это снова придется пройтись во всем точкам где оно всречается.

Уже есть пара набросков как это будет выглядеть, но нужно их доработать.
В ближайшие дни надеюсь поделиться. А пока можно будет обсудить все это дело, может быть у кого то тоже есть какие нибудь идеи.
.
(\/)____o_O____(\/)
мужно какой либо проектировщик создания базы данных и таблиц
.
Screamer
Koenig, Ну да я что то вроде этого и хотел.
Например спомощью объекта класса ObjectCreator создаем объекты со набором свойств и потом компоновщиков выстраиваем иерархию.
И далее все это каким то образом рендерится
.
Screamer
Небольшой апдейт микроблога :-)
Перед тем как делать кодогенератор нужно сформировать структуру модуля.
В основе структуры будет типичный MVC
Рассматривать будем все напримере JohnCMS
Т.к. в нем для совместимости с предыдущими версиями были оставлены глобальные переменные то придется делать костыли.
Создаем в корне директорий module и в нем файл index.php
со следующей строкой
error_reporting(-1);

Это будет нашей точкой входа.
в htaccess ничего писать не будем обойдемся глобальными переменными.
Начнем с определения констант.
define('_IN_JOHNCMS', 1);

/* Корневой директорий */
define('ROOTDIR', realpath($_SERVER['DOCUMENT_ROOT']). DIRECTORY_SEPARATOR);
/* Директорий системных файлов */
define('INCDIR', ROOTDIR . 'incfiles' . DIRECTORY_SEPARATOR);
/* Текущий директорий */
define('CURRENTDIR', dirname(__FILE__) . DIRECTORY_SEPARATOR);

Подключаем core.php и начинаем лепить костыль
require INCDIR . 'core.php';

/* Системные переменные */
$data = array(
    'id' => $id,
    'user' => $user,
    'act' => $act,
    'mod' => $mod,
    'do' => $do,
    'page' => $page,
    'start' => $start,
    'headmod' => $headmod,
    'kmess' => $kmess,
    'textl' => isset($textl) ? $textl : ''
);

Подключаем системные файлы модуля
/* Загружаем системные файлы модуля */
loadFile('storage');
loadFile('controller');
loadFile('model');
loadFile('template');

А вот и роутинг
/* Загружаем запрашиваемый контроллер */

$module = !empty($_GET['action']) ? $_GET['action'] : 'main';

if(!file_exists(CURRENTDIR . 'controller' . DIRECTORY_SEPARATOR . $module . '.php'))
{

    header('Location: ' . core::$system_set['homeurl'] . '/?err');

}

loadFile($module, 'controller');
$module = 'Controller_' . $module;

if (!class_exists($module))
{

    header('Location: ' . core::$system_set['homeurl'] . '/?err');

}

$module = new $module($data);

/* Выполняем запрашиваемый метод контроллера */
$mode = !empty($_GET['mode']) ? $_GET['mode'] : '_index';

if (!method_exists($module, $mode))
{

    header('Location: ' . core::$system_set['homeurl'] . '/?err');

}
$module->$mode();

Далее рассмотрим системные файлы модуля и пожалуй хватит пока.
.
Создаем в директории module директорий system
И еще три директория с названиями model , view, controller
во все эти директорий можно закинуть htaccess со строчкой Deny From All
Рассморим файлы директория system

storage.php - будет выполнять роль реестра
Выполним его с помощью магии
class Storage {

    protected $storage = array();

    public function __construct(array $storage)
    {


        $this->storage = $storage;

    }

    public function __set($name, $value)
    {

        if (!empty($name)) {

            if (is_array($value))
            {

                $value = new Storage($value);

            }

            $this->storage[$name] = $value;

        }

    }

    public function __get($name)
    {

        return isset($this->storage[$name]) ? $this->storage[$name] : NULL;

    }

    public function __isset($name)
    {

        return isset($this->storage[$name]);

    }

    public function __unset($name)
    {

        if (isset($this->storage[$name]))
        {

            unset($this->storage[$name]);

        }

    }

}

controller.php
Ну тут думаю все ясно.
abstract class Controller_Base extends Storage {

    protected $tpl;

    public function __construct(array $storage)
    {

        parent::__construct($storage);
        $variables = array(
            'set' => core::$system_set,
            'lng' => core::$lng,
            'set_user' => core::$user_set,
            'login' => isset(core::$user_data['name']) ? core::$user_data['name'] : FALSE,
            'user_id' => core::$user_id,
            'act' => $storage['act'],
            'datauser' => core::$user_data,
            'agn' => core::$user_agent,
            'ban' => core::$user_ban

        );
        $this->tpl = new Template($variables);
        unset($storage, $variables);

    }

    abstract public function _index();

}

model.php
и здесь без комментариев
class Model_Base extends Storage {

    public function __construct($storage)
    {

        parent::__construct($storage);

    }

}

Ну и template.php - шаблонизатор
class Template {

    protected $path = '';
    /* Переменные для файлов движка */
    protected $variables = array();
    /* Шаблоны с переменными для них */
    protected $templates = array();

    public function __construct(array $variables = array())
    {

        $this->variables = $variables;
        $this->path = CURRENTDIR . 'view' . DIRECTORY_SEPARATOR;

    }

    /**
     * Загрузка шаблона
     */
    protected function capture($_tplFile, array $data = array())
    {

        ob_start();
        if (is_array($_tplFile))
        {

            foreach ($_tplFile as $_tplData)
            {

                if (!empty($_tplData['data']))
                {

                    extract($_tplData['data'], EXTR_SKIP);

                }

                include_once $_tplData['name'];

            }

        }
        else
        {

            if (!empty($data))
            {

                extract($data, EXTR_SKIP);

            }

            include_once $_tplFile;

        }

        $contents = ob_get_contents();
        ob_end_clean();
        return $contents;

    }

    /**
    * Установка шаблона
    */
    public function setTemplate($name, array $data = array())
    {

        $name = $this->path . $name . '.php';
        if (file_exists($name))
        {

            $this->templates[] = array('name' => $name, 'data' => $data);

        }
        else
        {

            die('Template: ' . $name . ' is not found');

        }


    }

    /**
     * Собираем все в кучу и выводим на экран.
     */
    public function display()
    {

        array_unshift($this->templates, array('name' => INCDIR . 'head.php', 'data' => $this->variables));
        array_push($this->templates, array('name' => INCDIR . 'end.php', 'data' => $this->variables));
        echo $this->capture($this->templates);

    }

}

Вот вроде и все.
.
Чуть не забыл
листинг функции loadFile
/**
 * Подключение файлов
 * @param  (string) $name Имя файла
 * @param  (string) $type Директорий файла (system по умолчанию)
 * @return (void)
 */
function loadFile($name, $type = 'system')
{

    $dirs = array('system', 'controller', 'model');

    if (!in_array($type, $dirs))
    {

        $type = 'system';

    }

    if (file_exists(CURRENTDIR . $type . DIRECTORY_SEPARATOR . $name . '.php'))
    {

        require_once CURRENTDIR . $type . DIRECTORY_SEPARATOR . $name . '.php';

    }
    else
    {

        die('Unable to load file: ' . $type . ' ' . $name);

    }

}

Ее можно просто поместить в index
.
Вот такой вот мини мвц фреймворк получился
.
ога. Ну ты вумный!!!
.
matis4e,
.
принципе идея хорошая, я очень часто беру за основу чужие пабл модули и изменяю их под себя, изменяю названия весь текст и т.д ну а зачем париться и изобретать велосипед? думаю так намного проще, ну иногда конечно не могу найти нужний модуль из которого можно было бы сделать то что нужно мне тогда пишу сам...
Всего: 10