- Введение
- Пути файлов
- Наборы полей Joomla
- Группы полей Joomla
- Динамически изменяемые формы в Joomla
- Методы отражения в Joomla
- Пример кода компонента Joomla
Введение.
Это одно из серии руководств по API, цель которых - помочь понять, как использовать API Joomla, предоставляя подробные объяснения и примеры кода, которые вы можете легко установить и запустить.
Это руководство описывает более продвинутые возможности API Joomla Form, чем те, которые рассматриваются в руководстве по основам использования форм в Joomla (Basic form guide), и включает в себя следующие аспекты:
- Установка пути к файлу, чтобы Joomla могла найти ваши определения формы, поля формы и валидацию полей формы, в случае, если вы не следуете стандартам Joomla.
- Определение групп полей - Joomla предоставляет два типа, а именно наборы полей и группы полей.
- Динамическое изменение формы (после ее загрузки из XML-файла).
- Методы, подобные отражению, которые позволяют извлекать информацию из структуры и данных формы.
В конце руководства приводится пример компонента с примерами вышеперечисленных аспектов.
Пути файлов.
По умолчанию Joomla будет искать XML-определение формы в папке .../models/forms вашего компонента, в файлах сайта, если форма отображается на пользовательской стороне, или в файлах администратора, если форма отображается в админке. Статическая функция addFormPath() позволяет вам добавить другой каталог в список каталогов, которые Joomla будет искать.
Аналогично функция addFieldPath() позволяет определить другую директорию для любых пользовательских определений полей формы (по умолчанию это .../models/fields), а функция addRulePath() позволяет вам определить другую директорию для любых пользовательских правил валидации (по умолчанию это .../models/rules).
Примеры всех трех функций приведены в примере кода компонента в конце данного руководства.
Наборы полей Joomla.
Наборы полей (Fieldsets) в Joomla связаны с элементом <fieldset name="myfieldset"> в XML-определении формы. Преимущество использования наборов полей заключается в том, что в файле макета вы можете использовать
$form->renderFieldset("myfieldset");
для рендеринга всех полей, имеющих элементы <field> внутри открывающего и закрывающего тегов <fieldset>. Вместо того, чтобы вызывать renderField() для каждого поля в наборе полей.
Набор полей можно рассматривать как набор полей, которые должны отображаться в форме, и, таким образом, они похожи по концепции на элемент HTML <fieldset>. Однако обратите внимание, что renderFieldset() не выводит HTML <fieldset> или связанные с ним теги.
Группы полей Joomla.
Группы полей (Field Groups) связаны в Jomla с элементом <fields name="mygroup"> в XML определении формы. Это влияет на атрибут name HTML, который присваивается элементам input HTML полей, определенных в открывающем и закрывающем тегах <fields> в XML определении формы, и, следовательно, на имя параметра, отправляемого на сервер в HTTP POST запросе.
Если вы укажете опцию "control" => "myform" при настройке экземпляра формы, то значения полей ввода будут отправлены на сервер в HTTP POST запросе в следующем виде
myform[field1]
myform[field2]
myform[field3]
Если вы заключите эти поля в XML определение формы в элемент <fields name="mygroup">, то POST параметры будут отправлены с именами типа
myform[mygroup][field1]
myform[mygroup][field2]
myform[mygroup][field3]
Если ваш компонент имеет таблицу базы данных и вы храните ряд параметров в json-строке в одном из столбцов таблицы, то вы можете сгруппировать HTML элементы ввода этих параметров внутри элемента <fields>. Если вы назовете тег fields в соответствии с названием колонки, то вы можете использовать функциональность класса Table Joomla для легкого преобразования ассоциативного массива PHP, возникающего из POST-параметров, в json-строку для хранения в базе данных.
Параметр $group, который появляется в нескольких методах класса Form API Joomla, ссылается на атрибут name тега <fields> в XML определении формы.
Примеры такого подхода можно увидеть в учебнике Joomla MVC "Добавление шага изображения" и во многих основных компонентах Joomla.
Обратите внимание, что наборы полей и группы полей являются независимыми. В XML определении формы можно иметь элементы <fields> внутри элементов <fieldset>, а также элементы <fieldset> внутри элементов <fields>.
Динамически изменяемые формы в Joomla.
Если вы определили свою форму статически в XML-файле, то после ее загрузки вы можете изменить ее диамически в своем PHP-коде, используя API класса Form Joomla:
- добавлением дополнительных полей в форму путем загрузки другого XML-определения формы,
- изменение существующего поля или полей,
- удаление поля или группы полей.
Добавление полей через определение формы в Joomla.
Чтобы включить в форму дополнительные определения из файла, сделайте следующее
$form->loadFile($filename);
передавая в $filename имя и путь до XML-файла, структурированный таким же образом, как и основной файл определений вашей формы.
В качестве альтернативы можно создать SimpleXMLElement ($xml say) в коде, который содержит тот же XML и затем вызвать
$form->load($xml);
(Код Joomla для loadFile() просто считывает данные из файла в переменную SimpleXMLElement и затем вызывает load()).
Обеим этим функциям можно передать дополнительные параметры:
$replace(в методеload()) /$reset(в методеloadFile())- оба параметра имеют одинаковый эффект и относятся к случаю, когда поле в загружаемом XML имеет имя
name, совпадающее с именем поля в форме. - Если установлено значение
true, то новое поле заменяет старое. - Если установлено значение
false, то новое поле игнорируется.
- оба параметра имеют одинаковый эффект и относятся к случаю, когда поле в загружаемом XML имеет имя
$xpathесли вы хотите, чтобы рассматривалась только часть загружаемой структуры XML, вы можете указатьxpathдля выбора части или частей XML, которые вы хотите включить.
В дополнение к примеру, приведенному в примере кода ниже, можно найти примеры загрузки дополнительных XML файлов в основном коде com_menu администратора Joomla, относящиеся к тому, когда администратор настраивает опции меню сайта. Основные опции для пункта меню сайта задаются в файле item.xml в каталоге administrator/com_menus/models/forms, но в коде com_menu модели item.php они дополняются опциями, определенными в XML-файле, который находится в каталоге шаблона, относящемся к странице сайта, которая будет отображаться.
Пример построения и загрузки XML-структуры приведен в статье по разработке Joomla MVC в разделе "Добавление ассоциаций", где поля ассоциаций добавляются динамически таким образом, потому что добавляемые ассоциации зависят от языка записи.
Динамическая настройка полей в Joomla.
Вы можете использовать setField() для добавления или замены одного поля в экземпляре формы, а setFields() - для добавления или замены нескольких полей. Чтобы использовать эти функции, создайте XML, относящийся к полю, а затем передайте его в setField()
$xml = new SimpleXMLElement('<field name="newfield" … />');
setField($xml);
Аналогично вы можете определить массив таких XML-элементов и передать их в setFields(), что эквивалентно вызову setField() для каждого отдельного элемента.
Укажите параметры $group и $fieldset, чтобы включить новое поле в определенную группу полей и набор полей.
- Если параметр
$replaceимеет значениеtrue, то при обнаружении существующего поля с той же группой полей и именем оно будет заменено. - Если параметр
$replaceимеет значениеfalseи найдено существующее поле с той же группой полей и именем, то новое поле будет проигнорировано.
Установка атрибутов и значений полей в Joomla.
setFieldAttribute() позволяет установить / изменить атрибут, связанный с полем. Обратите внимание, что атрибут относится к атрибуту поля Joomla, а не к атрибуту HTML элемента ввода. Например, чтобы установить атрибут HTML placeholder, вы должны установить атрибут поля hint Joomla, и это работает только в том случае, если тип поля формы поддерживает этот атрибут.
Атрибут HTML value обрабатывается несколько иначе, чем другие атрибуты HTML. Как указано в руководстве по основам использования форм, в экземпляре формы Joomla структура XML формы (определенная XML-файлом определения формы) хранится отдельно от данных предварительного заполнения формы (передаваемых в методе bind()). То, что выводится в атрибуте value элемента input HTML, в первую очередь является атрибутом поля формы Joomla default (если он поддерживается для данного типа поля), но это value отменяется любым значением, указанным для этого поля в вызове bind().
Таким образом, вы можете установить атрибут default с помощью setFieldAttribute(), но для установки значения поля непосредственно в данных предварительного заполнения используйте setValue().
Удаление полей в Joomla.
Вы можете удалить поля из определения формы, вызвав removeField() для удаления конкретного поля или removeGroup() для удаления всех полей в указанной группе полей.
Методы отражения в Joomla.
Существует ряд методов, позволяющих получить доступ к различным аспектам данных экземпляра формы. В основном они достаточно просты для понимания, и только те случаи, когда они могут быть не совсем понятны, объясняются ниже.
getData() возвращает в виде объекта реестра Joomla данные предварительного заполнения, которые были установлены с помощью вызова метода bind() класса Form.
Методы getField(), getFieldset() и getGroup() возвращают поля как объекты FormField Joomla, а не как они хранятся внутри экземпляра формы.
getFieldsets() возвращает массив объектов Fieldset со свойствами, которые отражают тег <fieldset> в файле определения формы. Таким образом, если имеется
<fieldset name="myfieldset" label="myfieldsetLabel" description="myfieldsetDescription">
тогда вы можно
$fieldsets = $form->getFieldsets();
echo $fieldsets['myfieldset']->label; // вывод "myfieldsetLabel"
getFormControl() возвращает строку из параметра $options, переданного при создании экземпляра формы. Если вы использовали стандарт Joomla "control" => "jform" в этом массиве $options, то getFormControl() вернет строку jform.
getInput() и getLabel() возвращают HTML для тега <input> и <label> соответственно поля, которое было передано в качестве параметра. Однако обратите внимание, что ни один из этих методов не работает, если у вас есть пользовательское поле. Также обратите внимание, что эти методы класса Form отличаются от методов getInput() и getLabel() класса FormField, которые нужно предоставить при настройке некоторых типов пользовательских полей.
getValue() возвращает значение поля, которое вы передаете в качестве параметра, считывая его из данных, переданных в вызове bind(). При этом не учитывается атрибут default, установленный для поля, который будет преобразован в атрибут значения поля HTML, если для этого поля не предоставлены данные для предварительного заполнения.
Пример кода компонента Joomla.
Ниже приведены 6 файлов, составляющих небольшой компонент, который вы можете установить и запустить для демонстрации ряда функций, описанных выше, и который вы можете адаптировать для экспериментов с другими функциями.
Создайте первые 3 файла в папке com_sample_form3, а остальные 3 файла в подкаталоге этой папки com_sample_form3/extra. Затем заархивируйте папку com_sample_form3, чтобы создать файл com_sample_form3.zip и установите его как расширение в админке Joomla. После установки перейдите через браузер на ваш сайт Joomla и добавьте параметр URL &option=com_sample_form3, который должен будет отобразить форму и позволить ввести данные и отправить форму. Комментарии в коде должны дать понять, что происходит.
Как описано в соответствующем руководстве по основам использования форм, это не лучший способ проектирования компонента Joomla MVC, но он написан таким образом, чтобы сделать возможным использование API Form как можно более понятным.
com_sample_form3.xml Файл манифеста для компонента
<?xml version="1.0" encoding="utf-8"?>
<extension type="component" version="3.1.0" method="upgrade">
<name>com_sample_form3</name>
<version>1.0.0</version>
<description>Пример form 3</description>
<administration>
</administration>
<files folder="site">
<filename>sample_form3.php</filename>
<filename>sample_form.xml</filename>
<folder>extra</folder>
</files>
</extension>
sample_form.xml Файл, содержащий XML для определения формы.
Три поля заключены в набор полей mainFieldset, а поля email и номера телефона находятся в группе полей details. Поле сообщения имеет пользовательскую валидацию noasterisk.
<?xml version="1.0" encoding="utf-8"?>
<form name="myFormName">
<fieldset name="mainFieldset" label="mainFieldsetLabel" description="mainFieldsetDescription">
<field
name="message"
type="text"
label="Message"
size="40"
validate="noasterisk"
class="inputbox"
required="true" />
<fields name="details">
<fieldset name="detailsFieldset">
<field name="email"
type="email"
label="Email"
required="true"
size="40"
class="inputbox" />
</fieldset>
<field name="telephone"
type="tel"
label="Telephone number"
required="true"
size="40"
class="inputbox"
validate="tel" />
</fields>
</fieldset>
</form>
sample_form3.php Основной файл кода, который запускается, когда HTTP GET или POST запрашивают этот компонент.
<?php
defined('_JEXEC') or die('Запрещённый доступ! Всем кыш!');
use Joomla\CMS\Form\Form;
use Joomla\CMS\Factory;
// определение базовой формы
$form = Form::getInstance("sample", __DIR__ . "/sample_form.xml", array("control" => "myform"));
// поскольку email и telephone относятся к группе "details", данные предварительного заполнения должны отражать это
$prefillData = array("details" => array("email" => ".@.", "telephone" => "0"), "time" => "12:34");
// добаление путей формы и поля (путь правила добавляется позже, перед валидацией при обработке HTTP POST)
Form::addFieldPath(__DIR__ . "/extra");
Form::addFormPath(__DIR__ . "/extra");
// с помощью параметра xpath включить только поле "time" из extra_form.xml, а не поле "ignored"
$extraForm = $form->loadFile("extra_form", true, "//field[@name='time']");
// добавить новый элемент в форму, в группу "details" и набор полей "mainFieldset"
$xml = new SimpleXMLElement('<field name="upload" type="file" label="Photo" accept="image/*" />');
$form->setField($xml, 'details', true, "mainFieldset");
// Изменить надпись в поле сообщения на "Description" и установите подсказку (html-заполнитель) на "No asterisks!"
$form->setFieldAttribute("message", "label", "Description");
$form->setFieldAttribute("message", "hint", "No asterisks!");
// Удалить поле telephone - для активации не раскомментировать строку ниже
// $form->removeField("telephone", "details");
// Некоторые методы отображения
echo "<h2>Reflection methods output</h2>";
$formName = $form->getAttribute("name");
echo "Form name is: $formName<br>";
$formControl = $form->getFormControl();
echo "Form control is: $formControl<br>";
$detailsGroup = $form->getGroup('details');
echo "Fields in details group: <br>";
foreach ($detailsGroup as $key => $value)
{
echo "Key is: $key, PHP class of field is " . get_class($value) . ", and name attribute is " . $value->getAttribute("name") . "<br>";
}
$fieldsets = $form->getFieldsets();
echo "Fieldset label for mainFieldset is " . $fieldsets['mainFieldset']->label . "<br>";
if ($_SERVER['REQUEST_METHOD'] === 'POST')
{
$app = Factory::getApplication();
$data = $app->input->post->get('myform', array(), "array");
echo "<h2>POST data</h2>";
echo "Message was " . $data["message"] .
", email was " . $data["details"]["email"] .
", and time was " . $data["time"] . "<br>";
// добавление пути правила для валидации
Form::addRulePath(__DIR__ . "/extra");
$filteredData = $form->filter($data);
$result = $form->validate($filteredData);
if ($result)
{
echo "Validation passed ok<br>";
}
else
{
echo "Validation failed<br>";
$errors = $form->getErrors();
foreach ($errors as $error)
{
echo $error->getMessage() . "<br>";
}
// в повторно отображаемой форме показывать то, что ввел пользователь (после фильтрации данных)
$prefillData = $filteredData;
}
}
$form->bind($prefillData);
$data = $form->getData();
echo "<br>Form data is: $data<br>";
?>
<form action="<?php echo JRoute::_('index.php?option=com_sample_form3'); ?>"
method="post" name="sampleForm" id="adminForm" enctype="multipart/form-data">
<h2>mainFieldset fieldset</h2>
<?php echo $form->renderFieldset('mainFieldset'); ?>
<h2>time field</h2>
<?php echo $form->renderField('time'); ?>
<h2>ignored field - should be blank</h2>
<?php echo $form->renderField('ignored'); ?>
<button type="submit">Заслать! =D</button>
</form>
extra/extra_form.xml Определение формы для дополнительных полей формы, которые будут добавлены в форму.
Код компонента включает только поле time из этого определения формы, и это пользовательское поле типа mytime.
<?xml version="1.0" encoding="utf-8"?>
<form>
<field
name="ignored"
type="number"
label="Number"
step="0.1"
class="inputbox"
required="true" />
<field name="time"
type="mytime"
label="Enter time"
required="true"
class="inputbox" />
</form>
extra/Mytime.php Здесь содержится HTML-элемент <input> для пользовательского поля mytime.
Поскольку здесь нет метода getLabel(), метка label будет взята из XML определения поля формы.
<?php
defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\FormField;
class JFormFieldMytime extends FormField
{
protected $type = 'Mytime';
protected function getInput()
{
// Установить атрибуты - здесь только CSS класс для элемента input, если он указан.
$attr = !empty($this->class) ? ' class="' . $this->class . '"' : '';
// настройка html, включая value и другие атрибуты
$html = '<input type="time" name="' . $this->name . '" value="' . $this->value . '"' . $attr . '/>';
return $html;
}
}
extra/noasterisk.php Здесь содержится правило валидации для пользовательской валидации noasterisk для поля сообщения.
<?php
defined('_JEXEC') or die('Restricted access');
class JFormRuleNoasterisk extends JFormRule
{
// регулярное выражение, чтобы разрешить все, кроме звездочки
protected $regex = '^[^\*]+$';
}
Перевод с английского официальной документации Joomla:
https://docs.joomla.org/Advanced_form_guide