Введение

Это одно из серии руководств по API, цель которых - помочь вам понять, как использовать API Joomla, предоставляя подробные объяснения и примеры кода, которые вы можете легко установить и запустить.

Это руководство описывает более продвинутые функции Joomla Form API, чем описано в базовом руководстве по формам, и включает в себя следующие аспекты:

  • установка пути к файлу, чтобы Joomla могла найти определения вашей формы, поля формы и проверку полей формы в случае, если вы не соблюдаете стандарты Joomla.
  • определение группировок полей. Joomla предоставляет два типа: наборы полей (fieldsets) и группы полей (field groups).
  • динамическое изменение вашей формы (после ее загрузки из XML-файла)
  • reflection - подобные методы, которые позволяют извлекать информацию из структуры и данных вашей формы.

Руководство завершается образцом компонента с примерами вышеуказанных аспектов.

Пути к файлам

По умолчанию Joomla будет искать в папке …/models/forms вашего компонента XML-определение вашей формы, в файлах сайта, если ваша форма отображается во внешнем интерфейсе, или в файлах администратора, если ваша форма отображается на задней панели. Статическая функция addFormPath() позволяет вам добавить другой каталог в список каталогов, в которых будет выполняться поиск Joomla.

Аналогичным образом addFieldPath() позволяет вам определить другой каталог для любых определений пользовательских полей формы (по умолчанию — …/models/fields), а addRulePath() позволяет вам определить другой каталог для любых пользовательских правил проверки (по умолчанию — …/models/rules).).

Примеры всех трех компонентов приведены в примере кода компонента в конце этого руководства.

Наборы полей (Fieldsets)

Наборы полей связаны с элементом <fieldset name="myfieldset"> в определении XML формы. Преимущество использования наборов полей заключается в том, что в файле макета вы можете использовать

$form->renderFieldset("myfieldset");

для визуализации всех полей, которые имеют элементы <field> внутри открывающего и закрывающего тегов <fieldset>. Это вместо того, чтобы вызывать renderField() для каждого поля в наборе полей.

Набор полей можно рассматривать как набор полей, которые должны отображаться вместе в форме и, таким образом, по своей концепции аналогичны элементу HTML <fieldset>. Однако обратите внимание, что renderFieldset() не выводит HTML <fieldset> или связанные с ним теги.

Группы полей

Группы полей связаны с элементом <fields name="mygroup"> в определении XML формы. Это влияет на атрибут имени HTML, который назначается элементам ввода 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>. Если вы назовете тег полей так, чтобы он соответствовал имени вашего столбца, вы можете использовать функциональность таблицы Joomla, чтобы легко преобразовать ассоциативный массив PHP, возникающий из параметров POST, в строку json для хранения в базе данных.

Параметр $group, который появляется в нескольких методах API форм, ссылается на атрибут имени тега <fields> в XML-определении формы.

Примеры этого подхода можно увидеть в руководстве Joomla MVC  В разделеt  «Добавление изображения» и многих основных компонентах Joomla.

Обратите внимание, что наборы полей и группы полей независимы. В определении XML формы вы можете использовать элементы <fields> внутри элементов <fieldset>, а также элементы <fieldset> внутри элементов <fields>.

Динамическое изменение формы

Если вы определили свою форму статически в XML-файле, то после ее загрузки вы можете изменить ее динамически в своем PHP-коде с помощью API форм.

  • добавление дополнительных полей в форму путем загрузки другого определения XML формы
  • изменение существующего поля или полей,
  • удаление поля или группы полей.

Добавление полей через определение формы

Чтобы включить дополнительные определения из файла в вашу форму, выполните

$form->loadFile($filename);

передача $filename XML-файла, структурированного так же, как и файл определения основной формы.

В качестве альтернативы вы можете создать SimpleXMLElement (скажем, $xml) в своем коде, который содержит тот же XML, а затем вызвать

$form->load($xml);

(Код Joomla для loadFile() просто считывает данные из файла в переменную SimpleXMLElement, а затем вызывает load()).

С помощью обеих этих функций вы можете передавать дополнительные параметры:

  • $replace (в методе load() ) / $reset (в методе loadFile()) - оба они имеют тот же эффект, и относятся к случаю, когда поле в загружаемом XML имеет имя, которое совпадает с именем в форме. Если установлено true, новое поле заменяет старое. Если установлено false, новое поле игнорируется.
  • $xpath - если вы хотите, чтобы учитывалась только часть загружаемой структуры XML, то вы можете указать xpath для выбора части или частей XML, которые вы хотите включить.

В дополнение к примеру в примере ниже, вы можете найти примеры загрузки дополнительных XML файлов в коде ядра Joomla administrator com_menu, связанных с тем, когда администратор устанавливает параметры меню сайта. Основные параметры menuitem сайта указаны в файле item.xml в administrator/com_menus/models/forms, но в коде com_menu model item.php это дополняется параметрами, определенными в XML-файле, который находится в каталоге макета, относящемся к странице сайта, которая будет отображаться.

Пример создания и загрузки XML-структуры приведен в руководстве по разработке Joomla MVC Шаг добавления ассоциаций, где поля ассоциаций добавляются динамически таким образом, поскольку добавляемые ассоциации зависят от языка записи.

Динамическая настройка полей

Вы можете использовать SetField(), чтобы добавить или заменить одно поле в экземпляре формы, и setFields(), чтобы добавить или заменить несколько полей. Чтобы использовать их, вы создаете XML, относящийся к полю, затем передаете это в SetField()

$xml = new SimpleXMLElement('<field name="newfield" … />');
setField($xml);

 

Аналогично можно определить массив таких элементов XML и передать его setFields(), что эквивалентно вызову setField() для каждого отдельного элемента.

Укажите параметры $group и $fieldset, чтобы включить новое поле в определенную группу полей и набор полей.

Если параметру $replace присвоено значение true, то при обнаружении существующего поля с той же группой полей и тем же именем оно будет заменено.

Если параметру $replace присвоено значение false и найдено существующее поле с той же группой полей и тем же именем, то новое поле будет проигнорировано.

Настройка атрибутов и значений полей

Функция setFieldAttribute() позволяет вам установить / изменить атрибут, связанный с полем. Обратите внимание, что атрибут относится к атрибуту поля Joomla, а не к атрибуту HTML элемента ввода. Например, чтобы установить атрибут HTML placeholder, вы должны установить атрибут поля подсказки Joomla, и это работает только в том случае, если тип поля формы поддерживает этот атрибут.

Атрибут HTML value обрабатывается несколько иначе, чем другие атрибуты HTML. Как указано в Общем руководстве по формам, в экземпляре формы Joomla структура формы XML (определяемая файлом определения формы XML) хранится отдельно от данных предварительного заполнения формы (передаваемых в методе bind()). То, что выводится в атрибуте value элемента ввода HTML, в основном является атрибутом поля формы Joomla по умолчанию (если поддерживается для этого типа поля), но это переопределяется любым значением, указанным для этого поля в вызове bind().

Таким образом, вы можете установить атрибут по умолчанию с помощью setFieldAttribute(), но для установки значения поля непосредственно в данных предварительного заполнения используйте setValue().

Удаление полей

Вы можете удалить поля из определения формы, вызвав RemoveField() для удаления определенного поля или removeGroup() для удаления всех полей в пределах указанной группы полей.

Методы отражения

Существует ряд методов, которые позволяют вам получить доступ к различным аспектам данных экземпляра формы. В основном они довольно просты для понимания, и ниже описаны только случаи, когда это может быть не совсем понятно.

Функция getData() возвращает в качестве объекта Registry данные предварительного заполнения, которые были заданы с помощью вызова Form bind().

Методы getField(), getFieldset() и getGroup() возвращают все поля как объекты Joomla FormField, а не как они хранятся внутри экземпляра формы.

Функция getFieldsets() возвращает массив объектов Fieldset со свойствами, которые отражают тег <fieldset> в файле определения формы. Итак, если у вас есть

<fieldset name="myfieldset" label="myfieldsetLabel" description="myfieldsetDescription">

тогда вы можете сделать

$fieldsets = $form->getFieldsets();
echo $fieldsets['myfieldset']->label;   // outputs "myfieldsetLabel"

Функция getFormControl() возвращает строку из вашего параметра $options, переданного при создании экземпляра формы. Если вы использовали стандарт Joomla "control" => "jform" в этом массиве $options, то функция getFormControl() вернет строку "jform".

Функции и getLabel() возвращают HTML-код для тега <input> и <label> соответственно поля, которое было передано в качестве параметра. Однако обратите внимание, что ни один из них не работает, если у вас есть пользовательское поле. Также обратите внимание, что эти методы формы отличаются от методов getInput() и getLabel() FormField, которые вы должны предоставить при настройке некоторых типов пользовательских полей.

Функция getValue() возвращает значение поля, которое вы передаете в качестве параметра, считывая его из данных, переданных в вызове bind(). При этом не учитывается атрибут по умолчанию, установленный для поля, который будет преобразован в атрибут значения поля HTML, если для этого поля не предоставлены данные предварительного заполнения.

Пример кода компонента

Ниже приведены 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 форм максимально понятным.

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>Sample 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 для определения формы. 3 поля заключены в набор полей "mainFieldset", а поля электронной почты и номера телефона находятся в группе полей "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('Restricted access');

use Joomla\CMS\Form\Form;
use Joomla\CMS\Factory;

// определение базовой формы
$form = Form::getInstance("sample", __DIR__ . "/sample_form.xml", array("control" => "myform"));

// поскольку электронная почта и телефон находятся в группе "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>";
	// add rule path for validation 
	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">Submit</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(), метка будет взята из определения поля формы 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 для входного элемента, если он указан
		$attr = !empty($this->class) ? ' class="' . $this->class . '"' : '';

		// настроить html, включая значение и другие атрибуты
		$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 = '^[^\*]+$';
}