Собираем и устанавливаем первую версию пакета

На прошлом занятии мы определились с примерным функционалом, написали схему таблиц и сгенерировали модель xPDO для работы с БД MySql.

А сегодня нам нужно собрать и установить первую версию пакета и разобраться, как работают Custom Manager Pages (CMP).

Учитывая, что мы используем заготовку modExtra, и уже разобрали, как она работает — сборка пакета заключается в выполнении скрипта build.transport.php на сервере.

Если с конфиге build.config.php выставлена константа PKG_AUTO_INSTALL, то компонент будет сразу установлен на сайт.

Итак, я запускаю c2263.paas2.ams.modxcloud.com/Sendex/_build/build.transport.php и в конфиге у меня включена автоустановка, поэтому сразу после сборки пакета им уже можно пользоваться.
Иначе нужно было бы зайти в управление пакетами, поискать их локально, и установить. Пакеты я собираю часто, и делать это каждый раз давно надоело.

Давайте теперь разберем, как же работает CMP — то есть, наш новый раздел админки Sendex.

Меню

Все меню MODX состоят из двух частей: собственно пункт меню и действие, которое он вызывает. Как и всё в Revolution, меню и действие — тоже объекты, и мы устанавливаем их в файле transport.menu.php.

Видите, там в массиве с пунктами меню (у нас он один) modMenu, есть и ключ action — вот это и есть параметры для создаваемого modAction.

Если мы захотим туда добавить еще одни пункт, то будет примерно так:
$tmp = array(
	'sendex' => array(
		'description' => 'sendex_menu_desc',
		'action' => array(
			'controller' => 'index',
		),
	),
	'another_menu' => array(
		'description' => 'Мое описание',
		'action' => array(
			'controller' => 'а здесь файл-контроллер',
		),
	),

);

Еще мы можем указать, в каком родительском меню будет находиться наш пункт — это параметр parent, и в нашем случае он стандартный — components.
А вот у miniShop2, который изначально планировался под расширения, меню находится не в components, корне — вот, посмотрите на его transport.menu.php. Первый пункт там имеет parent = '', а следующие уже parent = 'minishop2'.

Для таких случаев нужно еще использовать параметр handler — это javascript функция, которая будет вызвана при клике на пункт меню. Для основного пункта меню MS2, который является просто контейнером подменю, это return false; — то есть, не делать ничего при клике.

Но у нас случай простой, можно сказать классический — один CMP и один пункт меню, который лежит в components.

Как правило (не не обязательно), modMenu связан с modAction — его цель состоит в запуске определённого контроллера. Поэтому с массиве с action мы указываем пункт controller.

Настройка для разработки

Прежде чем перейти к разбору контроллера, нужно сделать кое-какие настройки, чтобы нам было удобно работать на удалённом сервере.

Напоминаю, что весь наш проект находится в директории Sendex в корне сайта. А только что установленный пакет распаковался в /core и /assets и теперь, если мы что-то меняем — то это синхронизируется с директорией в корне, а на установленные файлы никак не влияет.

И тут у нас 2 варианта: после каждого изменения собирать и устанавливать пакет, или научить MODX загружать скрипты из директории /Sendex.

Конечно, второй вариант предпочтительнее, поэтому идём в СистемаПространства имён и делаем так:

Это позволит MODX обращаться за исходниками в нашу директорию. А теперь нужно создать еще системные настройки sendex_core_path и sendex_assets_url (а демонстрационную настройку sendex_some_setting можно удалить):
sendex_assets_path необязательна, но тоже может пригодиться. Эти настройки нужны нам, чтобы знать, где искать наши файлы.

В принципе, можно не создавать их, а каждый раз доставать modNamespace, но это не очень удобно.

С этого момента мы можем получать рабочие директории проекта вот так:
$corePath = $this->modx->getOption('sendex_core_path', $config, $this->modx->getOption('core_path') . 'components/sendex/');
$assetsUrl = $this->modx->getOption('sendex_assets_url', $config, $this->modx->getOption('assets_url') . 'components/sendex/');

Ну и последний штрих: нужно добавить
ini_set('display_errors', 1);
ini_set('error_reporting', -1);
в системные файлы MODX: /index.php и /manager/index.php — они нужны нам для того, чтобы MODXCloud не прятал от нас ошибки во время разработки.

Забегая немного вперед, кажу еще что нужно подредактировать /assets/components/sendex/connector.php, потому что он загружает MODX при запросах из админки, и должен уметь работать и при обычном расположении файлов, и при разработке в директории Sendex. Лично я и здесь добавляю вывод ошибок, чтобы видеть ругань при ajax запросах из админки.
В итоге у меня получился вот такой коннектор, советую скопипастить.

Контроллеры CMP

Итак, при клике на пункт меню, MODX смотрит, с каким действием тот связан и на какой контроллер ссылается, после чего вызывает этот файл.

Контроллер — это специальный php файл, лежащий в директории core компонента, и наследующий modManagerController. Указывается он просто по имени: если версия MODX < 2.2, то это может быть index.php или там controller.php, а если же MODX более свежий, то обычно используются index.class.php или controller.class.php.

У нас контроллер лежит в /core/components/sendex/index.class.php, поэтому в modAction указан index.
Учитывая наши настройки для разработки, первым делом меняем загрузку класса Sendex и приводим файл вот к такому виду. Теперь нам не нужно каждый раз собирать пакет, и все изменения сразу будут видны.

Ну а сейчас внимательно смотрим на файл и пытаемся въехать в логику.
  1. MODX нужен контроллер index, и в нём он ищет для запуска IndexManagerController — это делается автоматически
  2. IndexManagerController наследует абстрактный класс SendexMainController со всеми его методами
  3. SendexMainController наследует modExtraManagerController со всеми его методами
  4. modExtraManagerController является общим классом всех CMP и содержит основную логику работы. Он запускает свой метод initialize()
  5. Этот метод переопределён в дочернем классе SendexMainController, поэтому запускается оттуда.
  6. Мы уже смотрим наши системные настройки и подгуржаем класс Sendex из директории, указанной в системных настройках.
  7. Создаётся новый экземпляр класса Sendex, при этом в PHP 5 вызывается его метод __construct() — и у нас он задаёт переменную Sendex::config, в которую забивает массив с настройками и путями к файлам.
  8. Пути к файлам определяются точно так же, с помощью системных настроек. А значит, мы грузим их из директории /Sendex/...
  9. В этот момент мы уже можем обращаться к любым методам и свойствам класса Sendex, включая $sendex->config
  10. Где-то в глубинах modExtraManagerController уже был добавлен класс modX, и мы можем к нему обращаться через $this->modx
  11. Ну а теперь мы комбинируем конфиг Sendex и методы modX для подключения нужных нам скриптов и стилей их директорий компонента
  12. После того, как мы подключили всё, что нам нужно, передааём дальнейшую логику родительскуму классу — пусть делает, что хочет.
  13. А он хочет узнать, какой дальше загрузить контроллер и класс IndexManagerController говорит — home
  14. Этот класс уже будет загружен из директории /Sendex/core/components/sendex/controllers/home.class.php
Все это вам знать не обязательно! Такая логика используется в 90% всех новых дополнений, и сводится она к тому, чтобы вызвать основной контроллер, тот загрузил основные файлы компонента и передал управление дочернему контроллеру. А тот уже загрузит всё нужное для конкретной страницы компонента.

Обычно у компонента используется одна страница, поэтому все эти сложности можно пропустить мимо ушей. По большому счету, вам вообще больше не понадобится залазить в index.class.php, работать будем только с controllers/home.class.php.

Честно говоря, я и сам не до конца понимаю эту цепочку контроллеров и кто кого загружает, поэтому делаю просто по отлаженной схеме.

Еще одно замечание: у меня в modExtra скрипты и стили загружаются по старинке, методами:
$this->modx->regClientCSS()
$this->modx->regClientStartupScript()
$this->modx->regClientScript()
$this->modx->regClientStartupHTMLBlock()
Если вы хотите, чтобы ваш компонент дружил с AjaxManager — их нужно заменить на
$this->addCss()
$this->addJavascript()
$this->addLastJavascript()
$this->addHtml()

При этом нужно перенести инициализацию страницы из /assets/components/sendex/js/mgr/sections/home.js в home.class.php, иначе при открытии страницы будет ошибка.

Лично я всё это сделал, поэтому просто сверяем и копипастим мои файлы: index.class.php, home.class.php и home.js.

Если вы всё сделали правильно, можно на всякий случай синхронизировать проект, почистить везде кэши и зайти на страницу Sendex в админке:

Это страница от modExtra, она вызывает его контроллеры, с его объектами (которые мы удалили), поэтому в логе MODX должны появиться ошибки:

Это нормально, они пропадут, когды мы перепишем процессоры.

Можно также и проверить, сраюатывают ли изменения при синхронизации проекта с сервером?
Пишем в index.class.php сразу после <?php:
echo 'Hello world';die;
Сохраняем и обновляем страницу в админке:

Если вы видите то же, что и я — всё хорошо.

Кстати, при последующей сборке и установке пакета наш namespace будет перезаписан на стандартный, поэтому я внес пару измений в установщик. Видите, как просто работать с установщиком? Советую сделать так же.

Основные методы контроллеров

Нужно еще немного рассказать о методах контроллера home.class.php, который будет у нас основным рабочим.
getPageTitle
Вывод текста для тега title страницы CMP. Сейчас там выводится sendex из лексикона, поэтому мы видим его в заголовке:


getTemplateFile
Этот метод отдаёт html шаблон страницы, который будет распарсен Smarty. Учитывая, что вся админка сделана на ExtJS, на файл-шаблон /core/components/sendex/elements/templates/home.tpl выглядит так:
<div id="sendex-panel-home-div"></div>
Можете сразу про него забыть, он просто выводит тег, который будет использован для отрисовки компонента ExtJs.

getLanguageTopics
Этот метод возвращает массив словарей, которые будут использованы в работе компонента

checkPermissions
Проверять или нет права на доступ к странице. Мы можем указать их в настройке меню:

чтобы закрыть страницу от кого-то. Например, можно указать save_document, чтобы пускать только юзеро с правом редактирования документов.

loadCustomCssJs
Основной метод контроллера, именно он загружает все нужные скрипты и стили для работы страницы. В него мы еще будем добавлять всякое.

Заключение

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

У нас даже открывается демонстрационная страничка в админке, благодаря скриптам modExtra, с которыми мы будем разбираться на следующем занятии.

Посмотреть текущий смотрите на GitHub, в истории изменений файлов.

Следующая заметка
Пишем интерфейс: виджеты ExtJS и процессоры
Предыдущая заметка
Продумываем логику работы, определяем схему и модель БД


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

  1. Илья Уткин 16 ноября 2013, 14:42 # 0
    А может, один из уроков посвятить тому, как откатить некоторые изменения, если делал-делал и вдруг все сломал?

    Хочется пробовать самому идти несколько вперед, пытаться на своем опыте все понять, а если не получится, откатываться и продолжать вместе со всеми…
    1. Alex Vakhitov 16 ноября 2013, 14:58 # 0
      Я делаю ветку в git и бекап бд, как в голову что-то взбредет.
      1. Василий Наумкин 16 ноября 2013, 16:01 # 0
        Что конкретно у тебя сломалось?

        На прошлом уроке мы закоммитили изменения в Git, так что ты можешь просто откатить все изменённые файлы к этому состоянию. Это называется revert

        Потом синхронизизируй изменения с сервером, обнови кэш и можно начинать заново.

        Ну и в MODXCloud где-то должны быть ежедневные бэкапы — можно и с этой стороны зайти. Правда, я сейчас не могу войти туда в админку — что-то глючит.
        1. Илья Уткин 16 ноября 2013, 16:36 # 0
          Не, ничего не сломалось, но это пока что)
          Спасибо, буду знать
      2. Сергей Росоловский 17 ноября 2013, 02:08 # 0
        Василий уверяет что самое сложное позади :) Как по мне так еще впереди. Хотя вроде сложностей не возникает но понимание приходит как-то не спеша.
        1. Василий Наумкин 17 ноября 2013, 06:08 # 0
          Лично я потратил очень много времени, чтобы прийти к верной организации труда.

          А потом все уже просто — сиди, да работай: внёс изменение, страничку обновил, проверил, закомитил, следующее изменение.
        2. Василий 18 ноября 2013, 12:30 # 0
          Если вы видите то же, что и я — всё хорошо.
          Правильно ли понимаю, что это состояние — остаточный результат от modExtra?

          Т.е. после того, как мы изменяем файлы index.class.php, home.class.php и home.js — админка компонента должна быть пустой?
          1. Василий Наумкин 18 ноября 2013, 12:32 # 0
            Да, я же написал в конце
            Это страница от modExtra, она вызывает его контроллеры, с его объектами (которые мы удалили), поэтому в логе MODX должны появиться ошибки:

            Сегодня или завтра будет новая заметка, где мы разберём и перепишем эти файлы.
          2. Илья Ершов 22 ноября 2013, 11:50 # 0
            Все это вам знать не обязательно!
            Ну не соглашусь… Статья при первом прочтении самая сложная из всего курса до этого. И если бы не было подробных разъяснений нумерованным списком, то не усвоилось бы ни чего. А так всё в голове улеглось по полочкам!

            Из психологии человека:
            Трудно усваивать информацию которая просто описывает последовательность действий. Человеческий мозг запоминает ассоциативно, поэтому намного лучше усваиваются причинно-следственные связи.



            Уже только благодаря этой статье я не пожалел, что вписался в этот курс! Василий спасибо!
            1. Василий Наумкин 22 ноября 2013, 12:07 # 0
              На здоровье!

              Просто, на самом деле, я с этим механизмом разбирался только при разработке miniShop2, а для более простых дополнений там ничего менять не нужно.

              Но кругозор всегда расширить полезно, не спорю =)
            2. Илья Ершов 22 ноября 2013, 13:59 # 0
              Блин не понимаю где косяк…

              echo 'Hello world';die; // не подхватывается…

              настройки:
              sendex_assets_path {base_path}Sendex/assets/components/sendex/
              sendex_assets_url /Sendex/assets/components/sendex/
              sendex_core_path {base_path}Sendex/core/components/sendex/

              уже качнул с GitHub'а последнюю версию, почистил кэш (удалением папки /core/cache/) и всё равно никакой реакции…
              1. Василий Наумкин 22 ноября 2013, 14:02 # 0
                Ты забыл про настройки namespace — контроллеры грузятся в зависимости от неё.
                1. Илья Ершов 22 ноября 2013, 14:03 # 0
                  :)
                2. Илья Ершов 22 ноября 2013, 14:02 # 0
                  Сам уже догнал… после переустановки пакета слетело Пространство имён…
                  1. Василий Наумкин 22 ноября 2013, 14:04 # 0
                    Да, и там мы потом делаем новую настроечку, чтобы указывать этот параметр при сборке пакета.
                3. Илья Ершов 22 ноября 2013, 14:38 # 0
                  Что-то я пропустил. А где хранится дефолтный конфиг класса Sendex?
                  1. Илья Ершов 22 ноября 2013, 14:38 # 0
                    Ага,… в самом классе…
                    1. Василий Наумкин 22 ноября 2013, 14:38 # 0
                      Поясни, про что ты.

                      Если про конфигурацию сборки — то в /_build/build.config.php
                      1. Илья Ершов 22 ноября 2013, 14:59 # 0
                        Не не, я про то, откуда взялось:
                        $this->Sendex->config['jsUrl']

                        Нашёл уже.
                    2. Михаил 29 ноября 2013, 20:43 # 0
                      А меня вот так. Не могу найти кто же его ищет.
                      UPD. Мистика, после 5-ти раз чистки кеша все ок.
                      1. Василий Наумкин 29 ноября 2013, 20:48 # 0
                        Ты не забывай, что файлы синхронизируются с удалённым сервером — это может занять какое-то время.
                      2. Roman Smile 31 января 2014, 16:03 # 0
                        Если мы захотим туда добавить еще одни пункт, то будет примерно так:
                        $tmp = array(
                        	'sendex' => array(
                        		'description' => 'sendex_menu_desc',
                        		'action' => array(
                        			'controller' => 'index',
                        		),
                        	),
                        	'another_menu' => array(
                        		'description' => 'Мое описание',
                        		'action' => array(
                        			'controller' => 'а здесь файл-контроллер',
                        		),
                        	),
                        
                        );
                        А как теперь убрать этот пункт меню? Нужно вручную его удалять из таблицы modx_menus? Следует что-то еще подчистить?
                        1. Василий Наумкин 31 января 2014, 16:06 # 0
                          В админке MODX есть управление верхним меню. Просто удали ненужный пункт оттуда, больше ничего чистить не нужно.

                          Это просто ссылка.
                        2. Комментарий был удален.
                          1. Комментарий был удален.
                            1. Комментарий был удален.
                              1. Комментарий был удален.
                                1. Алексей Карташов 13 марта 2014, 17:12 # 0
                                  Вот жеж ёлки-палки! Вот это я стормозил… Дошло, наконец.

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