Таблицы и сохранение данных
Чтобы создать или изменить запись из панели, нужны три готовых класса Joomla. Table представляет одну строку таблицы и умеет её загружать и сохранять. AdminModel проверяет данные формы и передаёт их в Table. FormController обрабатывает кнопки «Сохранить» и «Закрыть». От вас — по короткому наследнику каждого.
Файлы
administrator/components/com_hello/
├── forms/
│ └── post.xml описание формы
├── src/
│ ├── Controller/PostController.php
│ ├── Model/PostModel.php
│ ├── Table/PostTable.php
│ └── View/Post/HtmlView.php
└── tmpl/
└── post/
└── edit.phpСоздайте их вручную и добавьте строку <folder>forms</folder> в раздел files внутри administration манифеста.
Класс таблицы
// administrator/components/com_hello/src/Table/PostTable.php
namespace My\Component\Hello\Administrator\Table;
defined('_JEXEC') or die;
use Joomla\CMS\Table\Table;
use Joomla\Database\DatabaseDriver;
class PostTable extends Table
{
public function __construct(DatabaseDriver $db)
{
parent::__construct('#__hello_posts', 'id', $db);
}
}Конструктор называет таблицу и её первичный ключ. Столбцы Joomla узнаёт сама. У объекта появляются методы load($id), bind($data), check(), store() и delete($id). Метод check() можно переопределить, чтобы проверить данные перед записью.
Описание формы
<?xml version="1.0" encoding="UTF-8"?>
<!-- administrator/components/com_hello/forms/post.xml -->
<form>
<fieldset name="details">
<field name="id" type="hidden" />
<field name="title" type="text" label="Заголовок" required="true" />
<field name="body" type="textarea" label="Текст" rows="8" />
</fieldset>
</form>Имена полей совпадают со столбцами таблицы. Формам посвящён следующий урок.
Модель
// administrator/components/com_hello/src/Model/PostModel.php
namespace My\Component\Hello\Administrator\Model;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\AdminModel;
class PostModel extends AdminModel
{
public function getForm($data = [], $loadData = true)
{
return $this->loadForm('com_hello.post', 'post', ['control' => 'jform', 'load_data' => $loadData]);
}
protected function loadFormData()
{
$data = Factory::getApplication()->getUserState('com_hello.edit.post.data', []);
return $data ?: $this->getItem();
}
}getForm() загружает файл forms/post.xml. loadFormData() возвращает данные для полей: сначала те, что пользователь уже вводил, но не смог сохранить из-за ошибки, иначе — запись из базы. Методы getItem() и save() уже есть в AdminModel; класс таблицы PostTable он находит по своему имени.
Контроллер
// administrator/components/com_hello/src/Controller/PostController.php
namespace My\Component\Hello\Administrator\Controller;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\Controller\FormController;
class PostController extends FormController
{
}Класс пуст: действия add, edit, save, apply и cancel унаследованы.
Экран редактирования
// administrator/components/com_hello/src/View/Post/HtmlView.php
namespace My\Component\Hello\Administrator\View\Post;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\ToolbarHelper;
class HtmlView extends BaseHtmlView
{
public $form;
public $item;
public function display($tpl = null): void
{
$model = $this->getModel();
$this->form = $model->getForm();
$this->item = $model->getItem();
ToolbarHelper::title($this->item->id ? 'Изменить запись' : 'Новая запись');
ToolbarHelper::apply('post.apply');
ToolbarHelper::save('post.save');
ToolbarHelper::cancel('post.cancel');
parent::display($tpl);
}
}<?php
// administrator/components/com_hello/tmpl/post/edit.php
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Router\Route;
Factory::getApplication()->getDocument()->getWebAssetManager()
->useScript('keepalive')
->useScript('form.validate');
?>
<form action="<?php echo Route::_('index.php?option=com_hello&layout=edit&id=' . (int) $this->item->id); ?>"
method="post" name="adminForm" id="adminForm" class="form-validate">
<?php echo $this->form->renderFieldset('details'); ?>
<input type="hidden" name="task" value="">
<?php echo HTMLHelper::_('form.token'); ?>
</form>renderFieldset('details') выводит все поля группы с подписями.
Результат
Откройте «Компоненты → Hello» и нажмите «Создать». Появится форма с полями «Заголовок» и «Текст». Введите данные, нажмите «Сохранить и закрыть» — вы вернётесь к списку с сообщением об успехе и новой строкой. Щелчок по заголовку открывает ту же форму с данными записи.
Что происходит при сохранении
- Кнопка отправляет форму с
task=post.save. FormControllerпроверяет токен и права пользователя.AdminModelпроверяет данные по описанию формы.PostTableвыполняетbind(),check()иstore().- Контроллер перенаправляет на список — представление
posts.
Частая ошибка
Свои действия, меняющие данные, пишут без проверки токена. Такое действие можно вызвать ссылкой с чужого сайта: браузер вошедшего администратора сам приложит его cookie. В готовых контроллерах проверка уже есть, а в собственных методах первой строкой ставьте $this->checkToken(); — и не забывайте HTMLHelper::_('form.token') в форме.