02 - MVC и другие соображения
Введение
Способ написания кода из предыдущего раздела - не лучший подход, если вы разрабатываете настоящий компонент Joomla. Вместо этого вы должны следовать тому, как разработан основной код Joomla, в частности, разбивая ваш компонент на MVC - модель, представление, шаблон контроллера. Пересмотренный код компонента com_sample_form2
в конце этого раздела приводится следующий подход.
В этом разделе описываются MVC и другие шаблоны проектирования, и следование этим шаблонам обычно упрощает работу с кодом компонента, особенно в больших компонентах.
Разделение Joomla MVC
В общих чертах Joomla разбивает компоненты на отдельные типы функциональности:
- контроллер содержит логику для принятия решения о том, как реагировать на HTTP-запрос - в частности, о том, какой вид и Модель использовать
- представление определяет, какие элементы данных должны быть показаны на веб-странице, которая должна быть представлена, и выполняет вызовы Модели для получения этих элементов данных
- модель обеспечивает доступ к данным
- файл tmpl является расширением представления (он выполняется в контексте экземпляра класса View и, таким образом, имеет прямой доступ к
$this
переменная объекта View). Он выводит HTML-код для компонента и включает в выходные данные, которые были сопоставлены представлением. Он отделен от кода представления, так что выходные данные HTML могут быть легко переопределены с помощью переопределения шаблона.
Шаблон отправки / Запроса/Получения
В Joomla весь вывод HTML (например, отображение формы) выполняется в ответ на HTTP GET, следуя шаблону Post / Redirect/Get . Пример кода из предыдущего раздела не следует этому шаблону, но вместо этого выводит ошибки проверки и повторно отображает форму в ответе на запрос HTTP POST.
Чтобы следовать шаблону Joomla, в коде, который обрабатывает POST, мы должны включить перенаправление HTTP GET на URL формы. Поскольку этот GET будет новым HTTP-запросом / ответом, мы должны сохранить в сеансе пользователя данные, которые будут показаны при повторном воспроизведении формы, которые включают следующие 2 элемента:
- сообщения об ошибках проверки сохраняются и выводятся с помощью
enqueueMessage()
метод (который автоматически сохраняет данные в пользовательском сеансе для нас)
$app = Factory::getApplication(); $app->enqueueMessage('some message text');
- введенные пользователем данные хранятся с помощью
setUserState()
и извлекается с помощьюgetUserState()
, и вводится контекстом, который должен быть уникальным для этой формы. Например:
$app->setUserState('com_sample_form2.sample', $data);
Код, который предоставляет данные для формы bind()
операцию необходимо сначала проверить с помощью getUserState()
чтобы увидеть, есть ли какие-либо данные в сеансе, так как это будут данные, которые пользователь ввел ранее, и их следует воспроизвести повторно.
Кроме того, если пользователь вводит данные, которые успешно проходят проверку, то setUserState()
затем должно быть вызвано прохождение null
для того, чтобы очистить эти данные предварительного заполнения в сеансе; в противном случае они будут появляться всякий раз, когда пользователь в следующий раз отобразит форму.
Конечно, хотя Joomla работает по этому шаблону, вам не всегда нужно ему следовать. Например, если у вас есть большой объем подтверждающих данных, которые вы хотите вывести после успешной отправки формы, то вы можете вместо этого вывести это просто как ответ на HTTP POST.
Отдельные контроллеры
Joomla направляет HTTP-запросы к отдельным контроллерам на основе значения параметра URL-адреса задачи , отправляемого в запросе. Этот параметр часто устанавливается Joomla core javascript на основе кнопки отправки, например, в примере ниже:
onclick="Joomla.submitbutton('myform.submit')"
Когда submit
нажимается кнопка, после чего прослушиватель onclick вызывает функцию javascript Joomla.submitbutton
передаю параметр 'myform.submit', и это приводит к тому, что параметру task присваивается значение "myform.submit".
В общем случае параметр task имеет вид <controller type>.<method>
итак, в этом случае HTTP-сообщение, содержащее данные формы, будет обработано MyformController и его submit
метод.
Классы Joomla MVC
Joomla предоставляет многофункциональные классы контроллеров, представлений и моделей, от которых могут наследоваться контроллеры, представления и модели ваших компонентов. Код модели в com_sample_form2
наследуется от FormModel, который несколько защищает Joomla Form API, описанный в предыдущем разделе. В этом случае наша модель вызывает FormModel loadForm()
метод, и этот метод затем выполняет обратный вызов нашего loadFormData()
предоставить данные для bind()
к форме. Таким образом, в коде нет отдельного вызова bind()
.
Токен Безопасности
Joomla использует токен безопасности в формах для предотвращения CSRF-атак . Токен выводится в файле макета.
<?php echo HTMLHelper::_('form.token'); ?>
и проверяется в контроллере, обрабатывающем ПОЧТУ:
$this->checkToken();
Если токен будет признан недействительным, то checkToken()
выводит предупреждение и перенаправляет пользователя обратно на предыдущую страницу.
Проверка
Об этом говорится в следующих разделах.
Пример кода
Для этого раздела вы можете загрузить этот zip-файл компонента и установить его. Он имеет в основном ту же функциональность, что и com_sample_form1
в предыдущем разделе, но он был переработан в соответствии с приведенными выше принципами. Хотя на первый взгляд это может показаться более сложным, распределение функциональности таким образом значительно упрощает понимание кода, когда компонент становится больше.
После того как вы установили файл, перейдите на домашнюю страницу вашего сайта и добавьте параметр запроса ?option=com_sample_form2
чтобы запустить компонент.
Файлы в пакете показаны ниже.
Вот описание функциональности в каждом файле.
admin/services/provider.php
Это всего лишь стандартный исходный файл, связанный с Joomla внедрением зависимостей .
site/src/Controller/DisplayController.php
Тот самый display()
метод этого класса - это то, что запускается при первоначальном переходе к com_sample_form2
компонент.
$model = $this->getModel('sample'); $view = $this->getView('sample', 'html'); $view->setModel($model, true); $view->display();
Тот самый $model
и $view
создается, и передаваемый параметр 'sample' указывает Полное имя (FQN), которое должно быть у Модели и Представления. Другими словами, Joomla использует эту строку "sample" как часть определения того, каковы полные данные классов Model и View. (Эти два экземпляра класса фактически создаются объектом класса MVCFactory, который был включен через файл services/provider.php.)
Тогда setModel
вызывается так, чтобы Модель была доступна для просмотра кода, с true
передается, чтобы указать, что это модель по умолчанию для Представления.
Наконец - То Открылся Вид display
вызывается метод.
site/src/View/Sample/HtmlView.php
В поле зрения display
функция:
$this->form = $this->getModel()->getForm(); parent::display($tpl);
это вызывает getForm
метод модели (по умолчанию), затем вызывает parent::display()
который в основном запускает файл tmpl/default.php.
site/src/Model/SampleModel.php
В Модели getForm
функция, которую мы имеем:
$form = $this->loadForm( 'com_sample_form2.sample', // просто уникальное имя, чтобы определить форму 'sample_form', // имени XML формы определения // Joomla будет искать в папке сайта/ форм для этого файла array( 'control' => 'jform', // имя массива для параметров POST, 'load_data' => $loadData // если установлено значение true, то будет обратный // вызов для loadFormData для передачи данных ) );
Тот самый loadForm
метод находится в libraries/src/MVC/Model/FormModel.php, через FormBehaviorTrait (в библиотеках / src / MVC / Model /FormBehaviorTrait), который он использует.
Тот самый loadForm
функция получит Joomla Form
экземпляр, настроенный для использования 'control' => 'jform'
а потом позвонит loadFile
на этом Form
пример для чтения определения формы в site/forms/sample_form.xml.
В качестве load_data
в этом случае устанавливается значение true
будет произведен обратный вызов по адресу loadFormData
который имеет
$data = Factory::getApplication()->getUserState( 'com_sample_form2.sample', // уникальное имя для идентификации данных в сессии array("email" => ".@.") // предварительное заполнение данных, если в сессии данные не найдены );
Тот самый setUserState
и getUserState
функции хранения данных в Joomla Session
, используя ключ, который здесь установлен в 'com_sample_form2.sample'. (Вы можете увидеть, что находится в Session
установив для параметра "Debug System" значение Yes в параметрах глобальной конфигурации, затем на веб-странице щелкните по символу Joomla в левом нижнем углу страницы.)
site/tmpl/sample/default.php
<form action="<?php echo Route::_('index.php?option=com_sample_form2'); ?>" 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 HtmlHelper::_('form.token'); ?> </form>
Здесь следует отметить несколько моментов:
- в
action
атрибут на<form>
указывает, что СООБЩЕНИЕ должно быть возвращено на нашcom_sample_form2
компонент - каждое из полей отображается так же, как и до использования
renderField
- в
submit
кнопка имеет прослушиватель onclick, который приведет кtask
Параметру URL присвоено значение "myform.submit". Следовательно, Joomla направит это вMyformController
, и способsubmit()
внутри него. - нам все еще нужно явно включить скрытое поле с типом
task
- токен безопасности входит в комплект поставки
HtmlHelper::_('form.token')
. Это будет отправлено как один из параметров POST, и нам нужно проверить это, когда мы обрабатываем отправку формы.
site/src/Controller/MyformController.php
Когда HTTP-сообщение будет получено, Joomla направит его на этот контроллер и вызовет submit
метод.
$this->checkToken();
Это проверит маркер безопасности и приведет к отклонению отправки формы, если она недействительна.
$model = $this->getModel('sample'); $form = $model->getForm(null, false);
При этом извлекаются параметры, отправленные в HTTP POST-запросе.
// Это функция validate() из класса FormModel, а не класс формы // 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); }
Если данные неверны, то он выводит соответствующие сообщения об ошибках и использует setUserState
хранить в Session
то, что пользователь ввел в форму.
Если данные действительны, то он выводит сообщение об успешном завершении и очищает данные в Session
чтобы при следующем отображении формы в ней были просто обычные данные предварительного заполнения.
// Перенаправлять обратно на форму во всех случаях $this->setRedirect(Route::_('index.php?option=com_sample_form2', false));
Это следует шаблону Post / Request / Get, который использует Joomla.