Этот урок показывает как сделать рефакторинг смешаного PHP/HTML файла в MODx Revolution. Подходит для тех, кто хорошо знает PHP, но по-прежнему изучает основы MODx. Если вы соответствуете следующему описанию, то урок будет для вас полезным:

  • Вы пишете скрипты, которые совмещают PHP и HTML;
  • Вы понимаете PHP циклы и массивы;
  • Вы пишете PHP код, который обращается к базе данных и извлекает записи;
  • У вас есть базовое понимание того, как MODx снипетты и другие элементы работают.

Для примера я буду работать с упрощённой версией реального проекта, который я недавно завершил. Приложение было разработано в PHP/HTML и я сделал следующее:

  • Извлёк список записей из таблицы базы данных;
  • Отобразил записи в хтмл таблице;
  • Вывел два выпадающих окна выбора для фильтрации результатов значений колонок.

Начинаем со смешаного PHP/HTML файла

Оригинальные искодники для этого приложения выглядят приблизительно так (упрощено для чёткости понимания):

Файл: acctCodes.php (оригинальная версия)

&lthtml&gt &lthead&gt &lttitle&gtSFS Account Codes and Definitions&lt/title&gt &lt/head&gt &ltbody&gt &lth1&gtSFS Account Codes and Definitions&lt/title&gt &ltform&gt &lt!—первое окно ВЫБОРА –&gt &ltdiv&gt &ltlabel for=“byCategory”&gtFind by Category:&lt/label&gt &ltselect name=“byCategory” id=“byCategory”&gt &ltoption&gtPick a Category&lt/option&gt &lt?php //некоторый код для соединения с базой данных $values = // некоторый код, который запускает запрос для извлечения данных для первого окна выбора foreach ($values as $value) { echo ‘&ltoption value="’ . $value . ‘"&gt’ . $value . ‘&lt/option&gt’; } ?&gt &lt/select&gt &lt/div&gt

&lt!—второе окно ВЫБОРА –&gt &ltdiv&gt &ltlabel for=“byType”&gtFind by Type:&lt/label&gt &ltselect name=“byType” id=“byType”&gt &ltoption&gtPick a Type&lt/option&gt &lt?php $values = // код для запуска запроса для извлечения значений для 2го окна выбора foreach ($values as $value) { echo ‘&ltoption value="’ . $value . ‘"&gt’ . $value . ‘&lt/option&gt’; } ?&gt &lt/select&gt

&lt/div&gt &lt!—таблица кодов аккаунта –&gt &lttable id=“acctCodes”&gt &ltthead&gt &lt!—для упрощения вместо всех &ltэтих тегов&gt я вставил этот комментарий –&gt &lt/thead&gt &lttbody&gt &lt?php $records = // код, который запускает запрос для извлечения записей таблицы foreach ($records as $record) { ?&gt &lttr&gt &lttd&gt&lt?php echo $record[‘category’]; ?&gt&lt/td&gt &lttd&gt&lt?php echo $record[‘status’]; ?&gt&lt/td&gt &lttd&gt&lt?php echo $record[‘account’]; ?&gt&lt/td&gt &lttd&gt&lt?php echo $record[‘acctType’]; ?&gt&lt/td&gt &lttd&gt&lt?php echo $record[’title’]; ?&gt&lt/td&gt &lttd&gt&lt?php echo $record[‘definition’]; ?&gt&lt/td&gt &lt/tr&gt &lt?php } ?&gt &lt/tbody&gt &lt/table&gt

&lt/body&gt &lt/html&gt

PHP код, приведенный здесь – очень прост; есть три отдельных блока кода, где осуществляются запросы и затем результаты выводятся на страницу, обёрнутые в соответствующие HTML теги. В третьем – несколько изощрённый PHP цикл с HTML кодом, для того, чтобы избежать громоздких echo выражений. Как видите – ничего сложного.

Но нужно кое-что еще подчистить, чтобы подготовить данный код к MODx.

Часть 1: Очистка (Вначале установите значения, а дальше вывод)

Первый шаг для подготовки этого PHP кода для MODx (и основной шаг) – это избавится от перемешивания логики приложения и разметки страницы. Вместо исполнения каждого запроса, как раз перед их запуском, мы вначале выполним все наши запросы, а потом при необходимости выведем через echo результат.

Код преобразится к следующему виду (я снова упростил, для чёткого понимания процесса):

Файл: acctCodes.php(очищенная версия)

&lt?php //некоторый код соединения с базой данных $values = // некоторый код, который выполняет запрос, чтобы извлечь данные для 1го выпадающего меню $options1 = ‘’; foreach ($values as $value) { $options1 .=’&ltoption value="’ . $value . ‘"&gt’ . $value . ‘&lt/option&gt’; }

$values = // некоторый код, который выполняет запрос, чтобы извлечь данные для 2го выпадающего меню $options2 = ‘’; foreach ($values as $value) { $options2 .= ‘&ltoption value="’ . $value . ‘"&gt’ . $value . ‘&lt/option&gt’; }

$records = // некоторый код, который выполняет запрос, чтобы извлечь записи из таблицы $trrows = ‘’; foreach ($records as $record) { $trrows .= ‘&lttr&gt&lttd&gt’ . $record[‘category’] . ‘&lt/td&gt&lttd&gt . $record[‘status’] . ‘&lt/td&gt &lttd&gt’ . $record[‘account’] . ‘&lt/td&gt &lttd&gt’ . $record[‘acctType’] . ‘&lt/td&gt &lttd&gt . $record[’title’] . ‘&lt/td&gt &lttd&gt . $record[‘definition’] . ‘&lt/td&gt&lt/tr&gt’; } ?&gt &lthtml&gt &lthead&gt &lttitle&gtSFS Account Codes and Definitions&lt/title&gt &lt/head&gt &ltbody&gt &lth1&gtSFS Account Codes and Definitions&lt/title&gt &ltform&gt &lt!– первое окно выбора –&gt &ltdiv&gt &ltlabel for=“byCategory”&gtFind by Category:&lt/label&gt &ltselect name=“byCategory” id=“byCategory”&gt &ltoption&gtPick a Category&lt/option&gt &lt?php echo $options1; ?&gt &lt/select&gt &lt/div&gt

&lt!– второе окно выбора –&gt &ltdiv&gt &ltlabel for=“byType”&gtFind by Type:&lt/label&gt &ltselect name=“byType” id=“byType”&gt &ltoption&gtPick a Type&lt/option&gt &lt?php echo $options2; ?&gt &lt/select&gt

&lt/div&gt &lt!– таблица кодов аккаунта–&gt &lttable id=“acctCodes”&gt &ltthead&gt &lt!—для упрощения вместо всех &ltэтих тегов&gt я вставил этот комментарий –&gt &lt/thead&gt &lttbody&gt &lt?php echo $trrows; ?&gt &lt/tbody&gt &lt/table&gt

&lt/body&gt &lt/html&gt

Это сделает разметку намного проще для просмотра и редактирования. Теперь весь код PHP содержится в 4 блоках кода:

Первый делает запросы и устанавливает значение, следующие три просто выводят значения на экран.

Таким образом мы намного лучше оформили наше маленькое вебприложение в MODx.

Часть 2: Сниппеты и заполнители

Чтобы привести наш улучшенный код в MODx, нам нужно сделать несколько дополнительных изменений. Наш код HTML может быть прямо скопирован в MODx Ресурс. В то же время, мы не можем сделать то же самое с кодом PHP. Нам нужно заменить его нашим Сниппетом и некоторыми Заполнителями.

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

MODx Ресурс: ‘acctCodes’

[[accountCodes]] &lthtml&gt &lthead&gt &lttitle&gtSFS Account Codes and Definitions&lt/title&gt &lt/head&gt &ltbody&gt &lth1&gtSFS Account Codes and Definitions&lt/title&gt &ltform&gt &lt!– первый блок SELECT –&gt &ltdiv&gt &ltlabel for=“byCategory”&gtFind by Category:&lt/label&gt &ltselect name=“byCategory” id=“byCategory”&gt &ltoption&gtPick a Category&lt/option&gt [[+options1]] &lt/select&gt &lt/div&gt

&lt!– второй блок SELECT –&gt &ltdiv&gt &ltlabel for=“byType”&gtFind by Type:&lt/label&gt &ltselect name=“byType” id=“byType”&gt &ltoption&gtPick a Type&lt/option&gt [[+options2]] &lt/select&gt

&lt/div&gt &lt!– таблица кодов аккаунтов –&gt &lttable id=“acctCodes”&gt &ltthead&gt &lt!– энный элемент таблицы. Для упрощения –&gt &lt/thead&gt &lttbody&gt [[+trrows]] &lt/tbody&gt &lt/table&gt

&lt/body&gt &lt/html&gt

В нашем ресурсе, четыре основные блока кода PHP были заменены тегами MODx. Первый, &#91[accountCodes]] будет запускать Сниппет accountCodes (который мы еще не написали). Следующие три тега – это Заполнители, которые будут заменены значениями, которые будет устанавливать наш Сниппет.

Теперь нам нужно продолжить и создать этот Сниппет.

Часть 3: Сниппет

Наш “accountCodes” сниппет будет содержать код из нашего первого PHP блока – все запросы кода и циклы, которые создают HTML, который выводится на экран. Единственной вещью, которую мы собираемся добавить – это несколько строчек MODx API кода, который определит эти выходные значения как значения для заполнителей (для наших тегов заполнителей ресурса).

Сниппет: “accountCodes”

&lt?php //некоторый код для соединения с базой данных $values = // некоторый код, который выполняет запрос, чтобы извлечь данные для 1го выпадающего меню выбора $options1 = ‘’; foreach ($values as $value) { $options1 .=’&ltoption value="’ . $value . ‘"&gt’ . $value . ‘&lt/option&gt’; }

$values = // некоторый код, который выполняет запрос, чтобы извлечь данные для 2го выпадающего меню выбора $options2 = ‘’; foreach ($values as $value) { $options2 .= ‘&ltoption value="’ . $value . ‘"&gt’ . $value . ‘&lt/option&gt’; }

$records = // некоторый код, который делает запрос для взятия записей для таблицы $trrows = ‘’; foreach ($records as $record) { $trrows .= ‘&lttr&gt&lttd&gt’ . $record[‘category’] . ‘&lt/td&gt&lttd&gt . $record[‘status’] . ‘&lt/td&gt &lttd&gt’ . $record[‘account’] . ‘&lt/td&gt &lttd&gt’ . $record[‘acctType’] . ‘&lt/td&gt &lttd&gt . $record[’title’] . ‘&lt/td&gt &lttd&gt . $record[‘definition’] . ‘&lt/td&gt&lt/tr&gt’; }

$modx-&gtsetPlaceholder(‘options1’, $options1); $modx-&gtsetPlaceholder(‘options2’, $options2); $modx-&gtsetPlaceholder(’trrows’, $trrows);

?&gt

Это наш сниппет. Единственное отличие от оригинального PHP кода – это три строчки “setPlaceholder” кода внизу. Они просто говорят MODx, что если и когда он встречает заполнитель &#91[+options1]] на странице – он должен заменять его на значение $options1.  Заполнители – это простой и элегантный способ в MODx управление значениями вывода в сниппетах.

Теперь мы сделали всё, что нужно, чтобы взять наши веб приложения и запустить их внутри MODx. В то же время, нужно сделать несколько дополнительных шагов, которые сделают наше приложение намного лучше, это использовать функционал MODx API. Наше текущее выполнение пока я бы оценил только на троечку. Давайте сделаем его немного лучше.

Часть 4: Используйте Чанки как Шаблоны вывода

Недостаток нашего текущего кода PHP состоит в том, что он по-прежнему смешивает разметку с логикой. Наши циклы ‘foreach’ по-прежнему содержат грубые куски кода HTML разметки:

foreach ($values as $value) { $options1 .= ‘&ltoption value="’ . $value . ‘"&gt’ . $value . ‘&lt/option&gt’; }

Эта стратегия далека от идеальной: предположим начальник просит какого-нибудь верстальщика поменять HTML для этого приложения, добавив некоторый дополнительный атрибут к каждому тегу

Есть несколько способов поправить данную ситуацию в PHP – брать HTML из внешних файлов и делать действия над строками. В то же время, нам не нужно беспокоится о таком решении. Для такого случая, MODx предлагает другое простое и элегантное решение: Чанки и Заполнители.

Тег “option” из HTML кода сниппета сверху, может быть записан как MODx чанк, называемый “option”:

&ltoption value="[[+value]]"&gt[[+value]]&lt/option&gt

Как видите – ничего сложного. Это всего лишь HTML код с парочкой заполнителей внутри. “option” -  это общиепринятый полезный чанк, который может быть использован где-угодно – многочисленными сниппетами во различных ресурсах.

Теперь мы можем поменять наш сниппет - больше MODx API, чтобы использовать значение этого чанка в нашем цикле.

&lt?php //некоторый код для соединения с базой данных $values = // некоторый код, который делает запрос, чтобы взять данные для первого ниспадающего меню выбора $options1 = ‘’; foreach ($values as $value) { $modx-&gtsetPlaceholder(‘value’, $value); $options1 .= $modx-&gtgetChunk(‘option’); }

Теперь наш цикл устанавливает заполнитель для $value и обрезает значение чанка ‘option’ для строки вывода.

Это позволит нам полностью отделить HTML код от нашего PHP скрипта. Если HTML верстальщику нужно добавить атрибут к элементам

Строки таблицы могут быть сделаны аналогичным образом:

Чанк: “trrows”

&lttr&gt &lttd&gt[[+category]]&lt/td&gt &lttd&gt[[+status]]&lt/td&gt &lttd&gt[[+account]]&lt/td&gt &lttd&gt[[+acctType]]&lt/td&gt &lttd&gt[[+title]]&lt/td&gt &lttd&gt[[+definition]]&lt/td&gt &lt/tr&gt и код PHP

foreach ($records as $record) { $trrows .= $modx-&gtgetChunk(’trrows’, $record); } Примите во внимание, что нам не нужно устанавливать какие-либо заполнители! Так как $record – это ассоциативным массив, то мы можем передать его в getChunk() как аргумент и заполнители автомагически** будут установлены для каждой пары ключ/значение в массиве!   (MODX просто неимоверно крут!)

Теперь нам можно оставить все наши HTML куски в чанки и привести наш Сниппет следующим образом:

&lt?php //некоторый код для соединения с базой данных $values = // некоторый код, который делает запрос, чтобы взять данные для первого выспадающего меню выбора $options1 = ‘’; foreach ($values as $value) { $modx-&gtsetPlaceholder(‘value’, $value); $options1 .= $modx-&gtgetChunk(‘option’); } $values = // некоторый код, который делает запрос, чтобы взять данные для второго выспадающего меню выбора $options2 = ‘’; foreach ($values as $value) { $modx-&gtsetPlaceholder(‘value’, $value); $options2 .= $modx-&gtgetChunk(‘option’); }

$records = // код, который делает запрос для взятия записей для таблицы $trrows = ‘’; foreach ($records as $record) { $trrows .= $modx-&gtgetChunk(’trrows’, $record); }

$modx-&gtsetPlaceholder(‘options1’, $options1); $modx-&gtsetPlaceholder(‘options2’, $options2); $modx-&gtsetPlaceholder(’trrows’, $trrows); ?&gt

Это начинает выглядеть довольно-таки хорошо. Мы используем преимущества MODx API для шаблонизации наших выводимых строк и наш сниппет теперь выглядит действительно аккуратно. В то же время есть некоторая вещь по этому поводу, которая беспокоит меня и может быть проблематической – теперь мы имеем вызовы MODx API, котоыре пересекаются с логикой основного приложения. Если мы на 100% уверенны, что мы будем выводить эти данные на эту страницу в таком формате, тогда это не является проблемой. Но, если есть какая-либо вероятность того, что эти данные могут быть использованы другим образом – где-нибудь вне MODx, то было бы неплохо держать логику основного приложения отдельно от MODx кода. В данный момент наше приложение получает оценку «хорошо». Давайте нашу оценку изменим на «отлично»,

Часть 5: Отделите логику вашего основного приложения

Всё, что нам нужно сделать – это финальный этам разделения нашего сниппета на 2 секции. Первая секция будет содержать логику основного приложения и будет возвращать данные массивов как члены одного большого массива. Это будет сделано с помощью только PHP и в идеале должно хранится в виде файла на нашем сервере.

Вторая секция будет сам сниппет. Он вначале возьмёт массив, возвращаемый с помощью файла логики, дальше будет использовать чанки для шаблонизации выводимых радов и наконец установит заполнители для вывода результатов в ресурсе.

Вот как всё будет выглядеть в финале:

Файл: acctCodes.php

&lt?php // $values = // код c запросом, чтобы взять данные для первого выспадающего меню выбора $values2 = // код c запросом, чтобы взять данные для второго выспадающего меню выбора $records = // код c запросом для взятия записей для таблицы return array(‘values’=&gt$values, ‘values2’=&gt$values2, ‘records’=&gt$records); ?&gt

Сниппет: ‘acctCodes’

&lt?php $data = include_once(MY_INCLUDE_PATH . ‘acctCodes.php’);

$options1 = ‘’; foreach ($data[‘values’] as $value) { $modx-&gtsetPlaceholder(‘value’, $value); $options1 .= $modx-&gtgetChunk(‘option’); }

$options2 = ‘’; foreach ($data[‘values2] as $value) { $modx-&gtsetPlaceholder(‘value’, $value); $options2 .= $modx-&gtgetChunk(‘option’); }

$trrows = ‘’; foreach ($data[‘records’] as $record) { $trrows .= $modx-&gtgetChunk(’trrows’, $record); }

$modx-&gtsetPlaceholder(‘options1’, $options1); $modx-&gtsetPlaceholder(‘options2’, $options2); $modx-&gtsetPlaceholder(’trrows’, $trrows); ?&gt

Теперь у нас все как нужно. Давайте еще раз просмотрим, что мы сделали:

  • Вся логика нашего основного приложения в файле на сервере (полностью вне MODx). 
  • Мы передаём строго данные (никакой HTML разметки) в наш сниппет MODx.
  • Мы позволяем MODx управлять шаблонизацией выводимых записей.
  • Мы устанавливаем все значение заполнителей одним сниппетом вверху ресурса.
  • Мы выводим наши данные на страницу, используя при необходимости заполнители.

Мы прошли довольно-таки значительный путь от нашего смешанного PHP/HTML файла, не правда ли? Я бы оценил эту финальную часть на оценку «отлично».  Остальные, возможно, имеют даже лучшие идеи как отделить некоторые вещи. Но для такого «пхпшника» как я, это неплохое начало;). Надеюсь вы научились ключевым идеям, когда писали своё первое PHP приложение для использования в MODx. Не стесняйтесь добавлять комментарии, делится идеями или предложениями по  поводу этого урока ниже.