Руководство по базовым формам (Basic form guide)

  1. Введение
  2. Общее описание
    1. Шаг 1 Загрузка формы
    2. Шаг 2 Подготовка данных для предварительного заполнения
    3. Шаг 3 Вывод формы в HTML
    4. Шаг 4 Отправка формы пользователем
    5. Шаг 5 Обработка данных HTTP POST
  3. Образец формы 1
  4. MVC и другие соображения
    1. Реализация MVC в Joomla
    2. Шаблоны Post/Request/Get
    3. Отдельные контроллеры
    4. Классы MVC в Joomla
    5. Токен безопасности
    6. Валидация на стороне клиента
  5. Образец формы 2


Введение

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

В Joomla класс Form и API позволяют легко разрабатывать HTML формы и обрабатывать присылаемые пользователем данные.

Ниже описано общее взаимодействие с классом Joomla Form и приведен код простого компонента, который можно установить для демонстрации использования этого API. В сопутствующем руководстве по использования форм в Joomla (Advanced form guide) рассматриваются более продвинутые аспекты класса Joomla Form.

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

Общее описание

На диаграмме ниже показано основное использование класса Joomla Form.

основное использование класса Joomla Form

Во-первых, вам необходимо определить вашу форму в XML, используя стандартные типы полей формы Joomla. Для примера ниже приведён код компонента. В основных терминах, каждый элемент поля в вашем XML-файле соответствует элементу HTML (в основном "input") в форме, с атрибутами поля XML, соответствующими атрибутам элемента input. Многие из возможных атрибутов поля перечислены в разделе Текстовый тип поля формы.

Шаг 1 Загрузка формы

Пользователь переходит по URL вашей формы и Joomla направляет HTTP GET запрос к вашему коду компонента. Нужно вызвать Form::getInstance(), передав имя специального XML-файла с определением формы из которой идёт запрос. Код формы Joomla создает экземпляр формы, а затем (в Form::loadFile()) считывает файл в память (в качестве PHP SimpleXMLElement) и анализирует XML, чтобы убедиться в его корректности. Основными параметрами, передаваемыми в getInstance(), являются:

  • name - строка, определяющая идентификатор, который будет присвоен этой форме - он должен быть уникальным, чтобы не пересекаться с другими формами Joomla на той же веб-странице.
  • data - имя XML-файла, в котором содержится определение обрабатываемой формы
  • options - массив параметров, причем элемент control указывает имя массива, в котором будут храниться POST-параметры.
    • Например, в примере кода array("control"=>"myform") означает, что у элементов ввода HTML атрибуты name будут установлены в значения myform[message], myform[email] и т.д.. Таким образом, они будут переданы в POST-параметрах, и затем их очень легко получить в массиве PHP.

Шаг 2 Подготовка данных для предварительного заполнения

Получение значения для любого элемента формы по своему усмотрению. Например, если эта форма используется для редактирования записи в базе данных, то предварительно заполните ее значениями существующих полей из базы данных. Можно подготовить значения, создав ассоциативный массив $data и передав его в метод Form::bind($data), а класс Form Joomla затем сохранит эти данные локально в экземпляре формы.

Шаг 3 Вывод формы в HTML

Вызов renderField(fieldName) на экземпляре формы позволяет получить HTML, который можно вывести с помощью простой функции PHP echo. Joomla обрабатывает XML-представление формы, чтобы получить раздел, относящийся к переданному полю fieldName, генерирует HTML для этого HTML-элемента и включает атрибут value на основе предварительно заполненных данных, которые были переданы в шаге 2. При выводе формы также необходимо окружить элементы ввода элементом <form> и добавить кнопку отправки.

Шаг 4 Отправка формы пользователем

Пользователь вводит данные в подготовленную и выведенную на экран HTML-форму и нажимает на кнопку отправки. Браузер генерирует HTTP POST запрос на URL, указанный в элементе <form>, и передает серверу в POST параметрах значения, введенные пользователем; каждый параметр определяется атрибутом name HTML элемента input.

Joomla направляет этот POST в компонент на обработку. Поскольку это новый HTTP-запрос, предыдущий экземпляр формы больше не существует, поэтому нужно снова вызвать Form::getInstance(), передав имя XML-файла формы, и Joomla (как и раньше) создает экземпляр формы и считывает полученные данные в память.

Шаг 5 Обработка данных HTTP POST

На этом этапе происходит обработка полученных данных. Это включает в себя:

  1. Получение POST-данных с помощью Factory::getApplication->input->get(). Обычно атрибуты name элементов ввода определены так, что POST-параметры отображаются в виде массива, и можно использовать фильтр ARRAY из полученных данных запроса с помощью JInput, чтобы считать их непосредственно в массив PHP. Однако это означает, что отдельные элементы вообще не фильтруются.
  2. Фильтрация. Здесь применяется фильтрация к каждому из вводимых значений. Фильтр, применяемый к полю, определяется атрибутом filter=... для этого поля в XML-файле формы, а если он отсутствует, то фильтр по умолчанию будет удалять HTML-теги и т.д. из значений данных. Возможные фильтры находятся в методе filterField() класса Form (см. https://joomla.stackexchange.com/questions/5764/what-are-possible-filters-in-joomla-form-fields). Обратите внимание, что эти фильтры полей формы отличаются от фильтров jinput.
  3. Валидация. Проверка данных, которые ввел пользователь, происходит вызовом validate($data) в обрабатываемом экземпляре формы, путём передачи ассоциативного массива (отфильтрованных) данных пользователя. Joomla сравнивает введенные пользователем данные с проверкой, которая определена в XML-файле формы, и генерирует ошибки для полей, которые не прошли проверку.

Если есть ошибки валидации, то необходимо отобразить эти ошибки пользователю и заново отобразить форму, предварительно заполнив поля теми (отфильтрованными) данными, которые пользователь ввел ранее.

Если ошибок нет, то можно подтвердить успешное получение данных пользователю и показать ему соответствующее сообщение.

Образец формы 1

Ниже приведен код небольшого компонента, который можно установить для проверки и понимания работы базового использования форм Joomla. Для этого нужно скопировать следующие 3 файла в папку под названием com_sample_form1. Затем заархивировать папку, чтобы получить файл com_sample_form1.zip и установить его в качестве компонента на сайте с CMS Joomla.

com_sample_form1.xml Файл манифеста для компонента:

<?xml version="1.0" encoding="utf-8"?>
<extension type="component" version="3.1.0" method="upgrade">

	<name>com_sample_form1</name>
	<version>1.0.0</version>
	<description>Sample form 1</description>
	
	<administration>
	</administration>

	<files folder="site">
		<filename>sample_form1.php</filename>
		<filename>sample_form.xml</filename>
	</files>
</extension>

sample_form.xml Файл, содержащий XML для определения формы:

<?xml version="1.0" encoding="utf-8"?>
<form>
	<field
		name="message"
		type="text"
		label="Enter message"
		size="40"
		class="inputbox"
		required="true" />
	<field name="email" 
		type="email"
		label="Enter email"
		required="true"
		size="40"
		class="inputbox" />
	<field name="telephone" 
		type="tel"
		label="Enter telephone number"
		required="true"
		size="40"
		class="inputbox"
		validate="tel" />
</form>

sample_form1.php Код компонента:

<?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"));
$prefillData = array("email" => ".@.");

if ($_SERVER['REQUEST_METHOD'] === 'POST') 
{
	$app   = JFactory::getApplication();
	$data = $app->input->post->get('myform', array(), "array");
	echo "Message was " . $data["message"] . 
		", email was " . $data["email"] . 
		", and telephone was " . $data["telephone"] . "<br>";
	$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);
?>
<form action="<?php echo JRoute::_('index.php?option=com_sample_form1'); ?>"
    method="post" name="sampleForm" id="adminForm" enctype="multipart/form-data">

	<?php echo $form->renderField('message');  ?>
	
	<?php echo $form->renderField('email');  ?>
	
	<?php echo $form->renderField('telephone');  ?>
	
	<button type="submit">Submit</button>
</form>

После установки нужно перейти на сайт и добавить следующий параметр в URL: &option=com_sample_form1. После этого отобразится форма с 3 обязательными полями:

  • поле ввода общего текста для сообщения
  • поле ввода электронной почты, предварительно заполненное строкой .@.
  • поле ввода номера телефона.

и, используя средства разработки браузера, можно сравнить атрибуты html с атрибутами в определении формы XML.

Обратите внимание, что современные браузеры выполняют некоторую проверку вводимых пользователем значений, в частности, они проверяют адрес электронной почты и заставляют пользователя вводить что-то в поля с установленным атрибутом required, но не выполняют (в настоящее время) проверку полей телефонных номеров.

Как только пользователь введёт правильные данные в поля и нажмет кнопку Submit, данные будут отправлены на сервер и будет запущена обработка POST кода примера. Это запускает процедуры фильтрации и валидации. Если есть ошибки валидации, код выводит сообщения об ошибках и заполняет форму данными, которые ввел пользователь, после чего заново выводит ее на экран.

MVC и другие соображения

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

Реализация MVC в Joomla

В общих чертах Joomla разделяет компоненты на отдельные типы функциональности:

  • контроллер содержит "бизнес-логику" приложения, включая принятие решения о том, какое представление и модель использовать;
  • модель обеспечивает доступ к данным;
  • представление решает, какие данные необходимо вывести на веб-страницу, и получает эти данные из модели;
  • шаблон выводит HTML и включает в вывод данные, которые были собраны представлением. Шаблон работает в контексте представления и поэтому имеет прямой доступ к переменным кода представления.

Шаблоны Post/Request/Get

В Joomla весь вывод HTML (например, отображение формы) выполняется в ответ на HTTP GET, следуя паттерну программирования https://en.wikipedia.org/wiki/Post/Redirect/Get. Приведенный выше пример кода не следует этому паттерну программирования, а вместо этого выводит ошибки проверки и повторно отображает форму в ответ на запрос HTTP POST.

Чтобы следовать стилю программирования Joomla, в коде, который обрабатывает POST, необходимо включить HTTP GET перенаправление на URL формы. Поскольку этот GET будет новым HTTP запросом/ответом, необходимо сохранить в сессии пользователя данные, которые будут показаны при повторном отображении формы:

  • сообщения об ошибках валидации сохраняются и выводятся с помощью метода enqueueMessage() (который автоматически сохраняет данные в сессии пользователя)
  • данные, введенные пользователем, сохраняются с помощью setUserState() и извлекаются с помощью getUserState(), а ключом служит context, который должен быть уникальным для данной формы.
    • Код, предоставляющий данные для операции bind() формы, должен сначала проверить с помощью getUserState(), есть ли в сессии данные предварительного заполнения.
    • И если пользователь вводит данные, которые успешно проходят проверку, то необходимо вызвать setUserState(), чтобы очистить данные предварительного заполнения в сессии, иначе они будут появляться всякий раз, когда пользователь в следующий раз запросит вывод формы.

Отдельные контроллеры

В ответ на запросы GET или POST, Joomla всегда запускает один и тот же файл компонента, sample_form1.php в примере компонента выше и верхний уровень sample_form2.php в примере ниже. Код sample_form2.php ниже следует стилю основных компонентов Joomla, и содержит код, который передает управление различным методам в различных контроллерах на основе значения HTTP параметра task. Этот параметр устанавливается JavaScript ядра Joomla на основе кнопки Submit, например, в примере ниже:

onclick="Joomla.submitbutton('myform.submit')"

Параметр task в этом примере имеет значение myform.submit. В общем случае параметр task имеет вид firstpart.secondpart и для компонента с именем com_example Joomla попытается запустить метод экземпляра secondpart() класса контроллера ExampleControllerFirstpart в файле firstpart.php в каталоге controllers.

Если параметр task не задан, то Joomla попытается запустить метод display() класса ExampleController, который она ожидает найти в файле controller.php.

Что касается кода:

$controller = JControllerLegacy::getInstance('Sample_form2');

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

$controller->execute($input->getCmd('task'));

выполняется соответствующий метод (secondpart() - или, в приведенном ниже случае, submit() - или display()) этого класса.

Таким образом, функциональность контроллера, обрабатывающая GET (обычно в файле controller.php), отделена от функциональности, обрабатывающей POST (обычно в файлах в папке controllers).

Классы MVC в Joomla

Joomla предоставляет многофункциональные классы контроллеров, представлений и моделей, от которых могут наследоваться контроллеры, представления и модели создаваемых компонентов. Приведенный ниже код модели наследуется от FormModel, который в некоторой степени защищает API форм Joomla. В данном случае наша модель вызывает FormModel::loadForm(), которая затем выполняет обратный вызов нашей loadFormData(), чтобы предоставить данные для bind() формы.

Также можно использовать классы FormController и AdminController, но они предполагают, что разрабатываемый компонент использует таблицу базы данных для хранения данных. В руководстве по разработке компонентов Joomla MVC этот подход рассматривается в J3.x:Developing an MVC Component/Adding backend actions.

Токен безопасности

Joomla использует маркер безопасности в формах для предотвращения CSRF-атак (см. https://en.wikipedia.org/wiki/Cross-site_request_forgery). Токен выводится в файле шаблона

<?php echo JHtml::_('form.token'); ?>

и проверяется в контроллере, обрабатывающем POST:

$this->checkToken();

Если токен оказывается недействительным, то checkToken() выводит предупреждение и перенаправляет пользователя на предыдущую страницу.

Валидация на стороне клиента

В дополнение к проверке на стороне сервера, включенной через XML-определение формы, Joomla также предоставляет способ включения JavaScript, который выполняет проверку на стороне клиента в браузере. Это не включено ниже, так как выходит за рамки данного руководства, но вы можете найти подробности об этом в J3.x:Developing an MVC Component/Adding verifications и Client-side form validation.

Образец формы 2

Этот второй пример компонента включает в себя описанные выше дизайнерские решения и структурирует код в соответствии с парадигмой Joomla. Общая структура файлов показана на картинке ниже.

второй пример компонента включает в себя описанные выше дизайнерские решения и структурирует код в соответствии с парадигмой Joomla

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

com_sample_form2/sample_form2.php Точка входа для компонента. Это первый файл, который запускает Joomla:

<?php
defined('_JEXEC') or die('Restricted access');
// Загрузка соответствующего класса контроллера
$controller = JControllerLegacy::getInstance('Sample_form2');

$input = JFactory::getApplication()->input;
// Запуск метода task или display(), если параметр task отсутствует
$controller->execute($input->getCmd('task'));
 
$controller->redirect();

com_sample_form2/controller.php Контроллер для обработки HTTP GET запросов:

<?php
defined('_JEXEC') or die('Restricted access');
use Joomla\CMS\MVC\Controller\BaseController;

class Sample_form2Controller extends BaseController
{
	// Joomla будет искать этот класс в файле controller.php
	// Она (по умолчанию) вызовет метод display(), и обнаружит, что он находится в классе BaseController
}

com_sample_form2/sample_form2.xml Файл манифеста для компонента:

<?xml version="1.0" encoding="utf-8"?>
<extension type="component" version="3.1.0" method="upgrade">

	<name>com_sample_form2</name>
	<version>1.0.0</version>
	<description>Пример работы форм №2 в Joomla</description>
	
	<administration>
	</administration>

	<files folder="site">
		<filename>sample_form2.php</filename>
		<filename>controller.php</filename>
		<folder>controllers</folder>
        <folder>views</folder>
        <folder>models</folder>
	</files>
</extension>

com_sample_form2/views/form/view.html.php Файл представления для отображения формы:

Функция display() контроллера создаст экземпляры модели и представления и вызовет функцию display() представления ниже.

<?php
defined('_JEXEC') or die('В доступе отказано');

use Joomla\CMS\MVC\View\HtmlView;
use Joomla\CMS\Factory;

class Sample_form2ViewForm extends HtmlView
{
	public function display($tpl = null)
	{
		if (!$this->form = $this->get('form'))
		{
			echo "Не могу загрузить форму<br>";
			return;
		}
		parent::display($tpl);	// это включит файл макета edit.php
	}
}

com_sample_form2/views/form/tmpl/edit.php Файл шаблона для отображения формы:

<?php
defined('_JEXEC') or die('Restricted access');
?>
<form action="<?php echo JRoute::_('index.php?option=com_sample_form2&view=form&layout=edit'); ?>"
    method="post" name="adminForm" id="adminForm" enctype="multipart/form-data">

	<?php echo $this->form->renderField('message');  ?>
	
	<?php echo $this->form->renderField('email');  ?>
	
	<?php echo $this->form->renderField('telephone');  ?>

	<button type="button" class="btn btn-primary" onclick="Joomla.submitbutton('myform.submit')">Submit</button>
	
	<input type="hidden" name="task" />
	<?php echo JHtml::_('form.token'); ?>
</form>

com_sample_form2/models/form.php Модель, связанная с отображением формы:

Представление get('form') сопоставляется с вызовом метода модели getForm().

<?php
defined('_JEXEC') or die('Restricted access');

use Joomla\CMS\MVC\Model\FormModel;

class Sample_form2ModelForm extends FormModel
{

	public function getForm($data = array(), $loadData = true)
	{
		$form = $this->loadForm(
			'com_sample_form2.sample',  // просто уникальное имя для идентификации формы
			'sample_form',				// имя файла определения формы XML
										// Joomla будет искать этот файл в папке models/forms
			array(
				'control' => 'jform',	// имя массива для POST параметров
				'load_data' => $loadData	// требуется TRUE
			)
		);

		if (empty($form))
		{
            $errors = $this->getErrors();
			throw new Exception(implode("\n", $errors), 500);
		}

		return $form;
	}

    protected function loadFormData()
	{
		// Провка сессии на наличие ранее введенных данных формы.
		$data = JFactory::getApplication()->getUserState(
			'com_sample_form2.sample',	// уникальное имя для идентификации данных в сессии
			array("telephone" => "0")	// предварительное заполнение данных, если в сессии не найдено никаких данных
		);

		return $data;
	}
	
}

com_sample_form2/models/forms/sample_form.xml XML-файл, содержащий определение формы:

<?xml version="1.0" encoding="utf-8"?>
<form>
	<field
		name="message"
		type="text"
		label="Введите сообщение"
		size="40"
		class="inputbox"
		required="true" />
	<field name="email" 
		type="email"
		label="Введите email"
		required="true"
		size="40"
		class="inputbox" />
	<field name="telephone" 
		type="tel"
		label="И телефончик оставьте!"
		required="true"
		size="40"
		class="inputbox"
		validate="tel" />
</form>

com_sample_form2/controllers/Myform.php Контроллер, который обрабатывает HTTP POST:

<?php
defined('_JEXEC') or die('Restricted access');

use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Factory;

class Sample_form2ControllerMyform extends BaseController
{   
	public function submit($key = null, $urlVar = null)
	{
		$this->checkToken();
		
		$app   = JFactory::getApplication();
		$model = $this->getModel('form');
		$form = $model->getForm($data, false);
		if (!$form)
		{
			$app->enqueueMessage($model->getError(), 'error');
			return false;
		}
		
		// имя массива 'jform' должно соответствовать строке 'control' => 'jform' в коде модели
		$data  = $this->input->post->get('jform', array(), 'array');
		
		// Это validate() из класса FormModel, а не из класса Form
		// Form FormModel::validate() вызывает методы Form::filter() и Form::validate()
		$validData = $model->validate($form, $data);

		if ($validData === false)
		{
			$errors = $model->getErrors();

			foreach ($errors as $error)
			{
				if ($error instanceof \Exception)
				{
					$app->enqueueMessage($error->getMessage(), 'warning');
				}
				else
				{
					$app->enqueueMessage($error, 'warning');
				}
			}

			// Сохранить данные формы в сессии, используя уникальный идентификатор
			$app->setUserState('com_sample_form2.sample', $data);
		}
		else
		{
			$app->enqueueMessage("Data successfully validated", 'notice');
			// Очистить данные формы в сессии
			$app->setUserState('com_sample_form2.sample', null);
		}
		
		// Перенаправление обратно на форму во всех случаях
		$this->setRedirect(JRoute::_('index.php?option=com_sample_form2&view=form&layout=edit', false));
	}
}

После создания файлов, как описано выше, нужно заархивировать папку com_sample_form2, чтобы создать com_sample_form2.zip и установить этот компонент стандартным установщиком расширений Joomla (в админке сайта). Затем нужно перейти на сайт Joomla и добавить в URL параметры &option=com_sample_form2&view=form&layout=edit. Это должно отобразить форму и работать аналогично предыдущему образцу формы, за исключением того, что ошибки и т.д. будут появляться в сообщениях предусмотренных в Joomla.

 

Перевод с английского официальной документации Joomla:
https://docs.joomla.org/Basic_form_guide

Заберите ссылку на статью к себе, чтобы потом легко её найти!
Раз уж досюда дочитали, то может может есть желание рассказать об этом месте своим друзьям, знакомым и просто мимо проходящим?
Не надо себя сдерживать! ;)

Старт! Горячий старт на просторы интернета
Старт! Горячий старт на просторы интернета
Старт! Меню