Разработка дополнения в MODX Revolution. Часть 1

Содержание статьи:

Обзор

Этот урок написан как всеобъемлющий пример разработки дополнения для MODX Revolution 2.2 и выше, а также описывает как упаковать дополнение в Транспортный пакет, кроме этого рассказывается как разрабатывать Дополнение вне корневого каталога MODX, чтобы можно было использовать программы контроля версий (такие как Git).

Мы рассмотрим Дополнение "Doodles" - простое дополнение, которое использует пользовательскую таблицу для хранения объектов называемых "Doodles" с полями name (имя) и description (описание). Мы создадим Сниппет, который извлекает их и выводит список, шаблонируемый через чанк, также создадим Пользовательскую страницу менеджера используя ExtJS с CRUD-сеткой для редактирования (CRUD - create, read, update, delete) и сделаем скрипт упаковки. Это всеобъемлющий урок, поэтому если вам нужны какие-то отдельные части, то см. выше Содержание.

Дополнение Doodles из этого урока можно найти на GitHub, вот ссылка: https://github.com/splittingred/doodles

Установка каталогов

Есть много способов как начать разрабатывать собственное дополнение - вы можете это сделать прямо в MODX и упаковать его с помощью такого инструмента как PackMan или можете разрабатывать вне корневого каталога MODX разместить его в такой системе контроля версий как Git, например. Этот урок будет использовать последний способ, так как он имеет несколько преимуществ:

  • Позволяет немедленную разработку прямо из Git репозитория
  • Позволяет лёгкое взаимодействие между разработчиками, так как нет копируемых файлов или изменяющих ядро - простая разработка в предпочитаемой IDE и далее некоторые начальные изменения путей при установке.
  • Позволяет изолировать ваш код, чтобы быть независимым от MODX ядра.

Давайте начнём. Я создал каталог в моём корневом /www/ каталоге: /www/doodles/

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

Можно добавить Системную настройку с именем "session_cookie_path" и дать ей значение "/" (без кавычек оба). Это скажет MODX использовать ту же сессию, когда вы запускаете что-то по адресу http://localhost/doodles/. Также, дать этой настройке уникальное имя через session_name (например "modxlocaldevsession") это тоже хорошая идея. Это предотвратит конфликты с другими MODX установками на вашей локальной машине. После этого очистите core/cache/ каталог и перезайдите после.

Итак у нас получиться несколько рабочих каталогов:

Давайте обратим внимание на несколько вещей. Во-первых наши 3 основных каталога - это core/, assets/ и _build/. Давайте разберёмся в нескольких моментах того, как работают MODX Дополнения - обычно (но не на 100% всегда), Дополнения разделяются при установке и попадают в 2 различных каталога: core/components/doodles/ и assets/components/doodles/. Почему? Потому что каталог assets/components/ содержит только веб-специфические объекты - JavaScript файлы, CSS файлы и т.д. Это файлы, которые доступны через сеть. Все PHP файлы, классы, карты и другие файлы в то же время находятся в каталоге core/components/. Это предохраняет от нежелательного доступа в каталоге core/ (который может быть перенесён куда-либо вне корня в некоторых MODX установках для большей безопасности).

Итак мы разделим наши файлы по каталогам для имитации того, как они будут разделены в MODX установке после установки из Транспортного пакета.

Каталог _build/ не упаковывается в zip файл после создания Транспортного пакета. Он служит для построения самого Транспортного пакета. Мы рассмотрим этот момент ближе к концу урока.

Давайте пройдёмся более детально по каждому подкаталогу. В каталоге assets/ единственный неочевидный файл - это connector.php. Этот файл позволит нам использовать кастомные процессоры на нашей Пользовательской странице менеджера (ПСМ), которую мы чуть позже создадим.

В каталоге core/components/doodles/ у нас есть несколько каталогов, которые нужно рассмотреть:

  • controllers - это контроллеры нашей ПСМ.
  • docs - просто содержит файлы changelog, readme и license.
  • elements - все наши Сниппеты, Чанки, Плагины и др.
  • lexicon - все наши i18n языковые файлы.
  • model - здесь лежать все файлы классов, а также наш XML схема файл для пользовательских таблиц БД.
  • processors - все наши кастомные процессоры для ПСМ.

Также обратите внимание, что эта директория находиться полностью вне нашего корневого каталога MODX. Поэтому, да - вы можете запускать "git init" и делать собственный Git репозиторий вне каталога /www/doodles/ (или как там вы его назовёте). И вы можете делать push без особых забот (есть правда несколько файлов о которых мы поговорим чуть позже, их нужно будет добавить к .gitignore файлу).

Ну вот. Полностью изолированная среда разработки от MODX, таким образом мы можем отделить разработку и гладкое сотрудничество. Давайте продолжим.

Создание сниппета-заготовки Doodles

Давайте продолжим и создадим файл snippet.doodles.php:

/www/doodles/core/components/doodles/elements/snippets/snippet.doodles.php

Вам нужно создать каталог snippets/, если он у вас ещё не создан. Давайте добавим пару строчек кода в ваш пустой файл:

Давайте разберёмся с этим кодом. Во-первых, у нас есть вызов getService. Сейчас он оформлен одной строкой, поэтому давайте разделим её и разберёмся что к чему:

Ну во-первых, что такое $modx->getOption? Это метод, который берёт системную настройку по ключу (первый параметр). В первой строчке мы получаем путь по-умолчанию к нашему сниппету Doodles (подразумеваем, что он находиться в папке core/). Полный путь будет: /www/modx/core/components/doodles/

Далее мы передаём это значение по-умолчанию как запасное для метода получения опции getOption. Этот метод содержит 3 параметра: ключ с именем "doodles.core_path", null и наш путь по-умолчанию. В getOption второй параметр - это массив для поиска ключа (который мы не используем, поэтому дали ему значение null) и 3й параметр для случая, когда ключ не найден.

Поэтому прямо сейчас наша 2ая строчка возвратит /www/modx/core/components/doodles/. Но это не путь к нашему Doodles ядру! (подсказка: правильный путь /www/doodles/core/components/doodles). Мы хотим этими строками задать поиск его. Так что же нам делать?

Создание настроек волшебного пути

Мы создаём несколько Системных настроек (специально для нашей среды разработки), которые сообщат нашим строкам, где нужно искать наши файлы! Давайте продолжим и создадим их:

  • doodles.core_path - /www/doodles/core/components/doodles/
  • doodles.assets_url - /doodles/assets/components/doodles/

Если вам нужно поменять данные значения для корректировки путей, таких как URL, то сделайте это. Теперь наша первая строчка кода возвратит: /www/doodles/core/components/doodles/ Отлично! Продолжим?

Для чего мы всё это делаем? Почему взять просто и не написать /www/doodles/core/components/doodles/? Ну, хотя бы потому, что это не сработает в чей-то другой установке. Скорее всего данный путь будет таким: MODXPATH/core/components/doodles/. Наш транспортный пакет (позже) будет обрабатывать все эти динамические настройки, но мы хотим добавить возможность записи их для того, чтобы можно было разрабатывать Doodles вне каталога MODX. Так как мы только что сделали!

Перейдём к третьей строке:

Метод $modx->getService загружает класс и инициализирует его объект, если он существует, устанавливает его в $modx->doodles в нашем случае (первый параметр передаётся). Больше информации можно найти здесь. Но подождите! У нас ещё нет Doodles класса! Самое время сделать его.

Создание базового Doodles класса

Во-первых, вы скорее всего спросите меня, почему мы создаём этот класс. Ну, он поможет нам в нескольких аспектах: мы можем определить некоторый базовые пути в нём, которые потом будем использовать во всём нашем Компоненте и он может дать нам некоторые методы для использования. Поверьте мне, он полезен. Давайте создадим его в /www/doodles/core/components/doodles/model/doodles/doodles.class.php:

Отлично! Всё пока довольно-таки просто - просто создаём объект класса, который имеет конструктор, который устанавливает ссылку на modX объект в $doodles->modx. Это пригодиться позже. Также он задаёт некоторые базовые пути, которые мы будем использовать позже в $doodles->config массиве и этот трюк с Системными настройками приведёт нас к пути в /www/doodles/.

Вернёмся к нашему сниппету. Давайте продолжим и добавим некоторые свойства по-умолчанию к нашему Сниппету. После упомянутых выше строчек он будет выглядеть так:

Отлично. Теперь мы хотим использовать xPDO для получения записей из базы... НО, мы ещё не сделали xPDO модель для них. Давайте вначале сделаем это.

Создание модели

xPDO абстрагирует БД в ООП методы запросов. На данный момент оно начало поддерживать различные типы БД и оно делает это через абстракцию запросов к БД. Также оно позволяет вам содержать ваши строки БД в виде упорядоченых, чистых классов и изящно выполнять все работы через короткие строчки кода. Но чтобы сделать это, нам нужно добавить xPDO модель к нашему сниппету (через $modx->addPackage метод). Но вначале нам нужно создать эту модель, используя xPDO схему. Есть отличный урок по созданию xPDO модели как это можно сделать, но мы пройдём быстро этот вопрос.

Продолжим и создадим xml файл в /www/doodles/core/components/doodles/model/schema/doodles.mysql.schema.xml. Вставим в него следующий код:

Отлично. Тут много всего. Во-первых, первая строка:

Она сообщает схеме, что наш xPDO пакет называется 'doodles'. Это то, на что мы будем ссылаться в нашем addPackage() вызове. Отлично. Это также говорит, что базовый класс для всех объектов определён здесь как "xPDOObject" и что эта схема сделана для MySQL. Наконец, она задаёт по умолчанию MySQL движёк как MyISAM. Дальше пойдём!

"Объект" в xPDO схеме - это в основном таблица БД. Эта строка сообщает, дайте xPDO имя для таблицы с названием '{table_prefix}doodles'. Подразумеваем, что в вашей установке префикс таблицы - это "modx", что создаст нам "modx_doodles". Далее она сообщает, что он расширяет "xPDOSimpleObject". Что это значит? xPDOObject - это базовый объект для любого xPDO табличного класса. xPDOSimpleObject расширяет его, но добавляет хорошенькое автоинкрементное поле "id" к этой таблице. Поэтому, если нам нужно поле "id" в таблице, то мы используем xPDOSimpleObject.

Следующие поля вполне очевидны - это поля таблицы БД. Давайте продвинемся к следующим двум частям:

Отлично, это место, где вступают в xPDO игру связанные объекты. Для целей этого урока, просто знайте, что они сообщают xPDO, что поле createdby связано с modUser и поле editedby ведёт к другому modUser. Круто? Теперь давайте перейдём к парсингу этого xml файла и к созданию наших классов и связей.

Скрипт парсинга схемы

Самое время взглянуть на нашу подзабытый _build каталог. Создадим там файл: /www/doodles/_build/build.schema.php и поместим туда код:

В основном этот файл парсит вашу XML схему и создаёт xPDO классы и связи (PHP представление XML файла) для вашего компонента. Мы вернёмся позже к этому, но так просто он не запустится. Он засбоит в поиске файла a /www/doodles/_build/build.config.php. Время сделать такой файл!

Очевидно, что вам понадобятся эти пути, где бы не находилась ваша MODX установка.

Теперь вы можете перейти к вашему _build/build.schema.php файлу и запустить его. Я делаю это в строке браузера: http://localhost/doodles/_build/build.schema.php. Вам скорее всего будет нужно поменять это на другой адрес, по которому находиться ваш doodle каталог.

Это должно запуститься и сгенерировать нужные файлы и связи:

Браво! Теперь вы сделали ваши связи и классы. Давайте сделаем уточнения в нашем базовом Doodle классе, чтобы он автоматически добавлял в Doodles xPDO пакет при загрузке этого класса. Добавьте следущую строку после этой $this->config = array_merge part, прямо в конце конструктора:

Это сообщает xPDO, что мы хотим добавить 'doodles' xPDO пакет, позволяя нам делать запросы к пользовательской таблице. Отлично!

Ок, наш сниппет теперь выглядит приблизительно так:

Ламерский сниппет, согласны? Хорошо, что мы сейчас делаем, так это устанавливаем объект Doodles класса в переменную $dood и устанавливаем некоторые значения по-умолчанию для свойств для дальнейшего использования. $scriptProperties - это массив, между прочим, всех передаваемых в сниппет свойств. Вызов getOption парсит его для поиска в них свойств и если они не установлены, то берёт свойства по-умолчанию.

Статический сниппет

Возможно вы подумаете: "Это всё ещё даже не в MODX manager! Что он нам тут рассказывает? Наверное шутит?" Отличный вопрос, давайте сделаем это!

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

Создайте новый сниппет чекнув "Is Static". Появятся два новых поля. Первое, Медиа Источник (Media Source) для поля файла (Static File). Оно предложит список медиа источников для базового использования. Так как наш Doodles находиться не в MODX веб пространстве, нам нужно выставить это поле в Нет ("None"). Теперь нам нужно ввести путь к файлу сниппета. Также используем установки пути, которые мы создали в Системных настройках и добавить остаток пути к действительному сниппет файлу.

Обратите внимание на дополнительный слеш между системной настройкой и остальным путём. В core_path этот слеш есть, но по дороге он где-то затерялся, поэтому добавим его здесь. Путь будет рендериться как /www/doodles/core/components/doodles/elements/snippets/snippet.doodles.php

Отлично сработано! Далее можно продолжать редактировать наш сниппет в своей любимой IDE и делать в нём нужные вещи. Он также передаёт все параметры, которые мы передаём в вызове сниппета в наш Doodles сниппет. Круто! Возвращаемся к нашему сниппету.

Построение запроса

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

Запускаем наш сниппет. Он автоматически создаст таблицу БД, которую мы сделали в нашей схеме. После этого удалите этот код и можем продолжать дальше.

Добавим это к нашему сниппету перед оператором return:

Это возьмёт наш массив Doodle объектов или в терминах не xPDO, несколько рядов из таблицы БД. Сохраним наш сниппет и запустим через строку браузера http://localhost/modx/doodles.html (или какой у вас там будет адрес ресурса). У вас должно вывести:

0

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

Для этого используйте любое удобное для вас ПО для работы с БД (например phpMyAdmin) и найдите таблицу 'modx_doodles' в вашей БД. Добавьте несколько рядов к ней (просто добавьте name/description значения). Это должно выдать вам какие-то данные. Допустим вы добавили 2 ряда. Продолжим и запустим сниппет, получим следующее:

2

Отлично! Ваш пользовательский запрос к БД сработал! Давайте усложним его. Мы можем использовать xPDO xPDOQuery для создания сложных запросов. На данный момент добавьте команду сортировки:

Отлично. Это отсортирует поле по $sort (который мы определили ранее) в направлении $dir. Теперь нам нужно создать вывод!

Метод getChunk класса Doodles class

Во многих из моих экстра, я добавляю парочку вспомогательных методов к моему базовому классу getChunk. Они позволяют мне использовать чанки-файлы для разработки. Давайте так и сделаем. Откроем Doodles класс и добавим эти два метода:

В данный момент всё что нужно знать: эти методы будут искать Чанки в вашем /www/doodles/core/components/doodles/elements/chunks/ каталоге с постфиксами '.chunk.tpl' и все в нижнем регистре (маленькими буквами). Если чанки не найдутся в файловой системе, то будет продолжен поиск в MODX. Поэтому, когда мы вызываем:

То это задаст для $o содержимое /www/doodles/core/components/doodles/elements/chunks/hello.chunk.tpl со свойством [[+name]] спарсенным как Joe. Это позволит вам отредактировать ваши Чанки в вашей IDE, а не в MODX. Это также позволит вам упаковать ваш Компонент без установки чанков по-умолчанию в пользовательскую MODX установку (которую они будут пытаться перезаписать, что будет стёрто при обновлении вашего Компонента).

Возвращаемся к нашему сниппету. Создадим файл-чанк /www/doodles/core/components/doodles/elements/chunks/rowtpl.chunk.tpl и вставим туда код:

Теперь добавим это после вашего запроса, но перед return в вашем Сниппете:

Этот код итерирует через все Doodle объекты, которые у нас есть с вызовом getCollection и создаёт PHP массив из их значений методом toArray. Далее он использует getChunk и этот массив для шаблонирования чанком каждого ряда вывода и склейкой выводимого результата в переменную $output. Таким образом мы получим группу \<li\> тегов (столько, сколько вы добавили рядов в БД). Всё будет выглядеть приблизительно так:

Конечно же, вы можете внести любые изменения в этот Чанк и можете как угодно его назвать, мы получили возможность шаблонизировать вывод Сниппета! Ура!

Давайте ещё раз всё пройдём. Наш сниппет выглядит вот так:

Что вышло в итоге: этот сниппет загружает наш базовый пользовательский класс по пути в системных настройках, добавляет наш пользовательский xPDO пакет БД, извлекает данные из таблицы и выводит их через чанк.

Выводы

Мы сделали неплохую пользовательскую модель БД, которую использует наш Doodles сниппет для получения записей из БД. Также мы рассмотрели базовую структуру обобщённого MODX компонента.

Нам нужно ещё, чтобы эти данные как-то редактировались в MODX менеджере, правильно? Вот где нам пригодятся Пользовательские страницы менеджера (Custom Manager Pages). Перейдём к следующей части данного урока.

Остальные статьи: