Модели
Модель — класс, который получает данные из базы и отдаёт их представлению. В Joomla есть готовые базовые модели: для списка достаточно написать один запрос, а постраничный вывод модель сделает сама. Заменим записи, заданные в коде, настоящими записями из базы.
Таблица
Компоненту нужна своя таблица. Пока создадим её вручную: откройте phpMyAdmin и выполните запрос, заменив #__ приставкой таблиц вашего сайта (она видна в именах остальных таблиц):
CREATE TABLE `#__hello_posts` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`title` VARCHAR(255) NOT NULL,
`body` TEXT NOT NULL,
`published` TINYINT NOT NULL DEFAULT 1,
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `#__hello_posts` (`title`, `body`) VALUES
('Первая запись', 'Текст первой записи'),
('Вторая запись', 'Текст второй записи');Как создавать таблицу автоматически при установке компонента, показано в главе о выпуске расширения.
Где лежат модели
components/com_hello/src/
├── Controller/DisplayController.php
├── Model/
│ ├── PostsModel.php список записей
│ └── PostModel.php одна запись
└── View/
├── Posts/HtmlView.php
└── Post/HtmlView.phpИмя модели совпадает с именем представления: для view=posts Joomla сама создаёт PostsModel и передаёт её представлению.
Модель списка
// components/com_hello/src/Model/PostsModel.php
namespace My\Component\Hello\Site\Model;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\Model\ListModel;
use Joomla\Database\QueryInterface;
class PostsModel extends ListModel
{
protected function getListQuery(): QueryInterface
{
$db = $this->getDatabase();
return $db->getQuery(true)
->select($db->quoteName(['id', 'title', 'created']))
->from($db->quoteName('#__hello_posts'))
->where($db->quoteName('published') . ' = 1')
->order($db->quoteName('created') . ' DESC');
}
}Вы описываете только запрос. Методы getItems() (записи текущей страницы) и getPagination() (ссылки на страницы) уже есть в ListModel.
Вызов из представления
// components/com_hello/src/View/Posts/HtmlView.php
public function display($tpl = null): void
{
$model = $this->getModel();
$this->posts = $model->getItems();
$this->pagination = $model->getPagination();
parent::display($tpl);
}Объявите в классе ещё одно свойство — public $pagination;. Теперь $this->posts — массив объектов, поэтому макет меняется:
<?php
// components/com_hello/tmpl/posts/default.php
defined('_JEXEC') or die;
use Joomla\CMS\Router\Route;
?>
<h1>Блог</h1>
<ul>
<?php foreach ($this->posts as $post) : ?>
<li>
<a href="<?php echo Route::_('index.php?option=com_hello&view=post&id=' . (int) $post->id); ?>">
<?php echo $this->escape($post->title); ?>
</a>
</li>
<?php endforeach; ?>
</ul>
<?php echo $this->pagination->getListFooter(); ?>Откройте index.php?option=com_hello&view=posts — выведены записи из базы, каждая со ссылкой.
Модель одной записи
// components/com_hello/src/Model/PostModel.php
namespace My\Component\Hello\Site\Model;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\ItemModel;
use Joomla\Database\ParameterType;
class PostModel extends ItemModel
{
public function getItem($pk = null)
{
$id = (int) ($pk ?: Factory::getApplication()->getInput()->getInt('id'));
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select('*')
->from($db->quoteName('#__hello_posts'))
->where($db->quoteName('id') . ' = :id')
->where($db->quoteName('published') . ' = 1')
->bind(':id', $id, ParameterType::INTEGER);
$post = $db->setQuery($query)->loadObject();
if (!$post) {
throw new \Exception('Запись не найдена', 404);
}
return $post;
}
}Номер записи берётся из параметра id адреса. Если записи нет, исключение с кодом 404 покажет страницу «Не найдено».
Страница записи
// components/com_hello/src/View/Post/HtmlView.php
namespace My\Component\Hello\Site\View\Post;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
class HtmlView extends BaseHtmlView
{
public $post;
public function display($tpl = null): void
{
$this->post = $this->getModel()->getItem();
parent::display($tpl);
}
}<?php
// components/com_hello/tmpl/post/default.php
defined('_JEXEC') or die;
?>
<h1><?php echo $this->escape($this->post->title); ?></h1>
<p><?php echo nl2br($this->escape($this->post->body)); ?></p>Щёлкните по записи в списке — откроется страница index.php?option=com_hello&view=post&id=1 с заголовком и текстом.
Частая ошибка
Модель называют не так, как представление: представление posts, а модель PostListModel. Joomla ищет модель по имени представления, не находит, и $this->getModel() возвращает пустое значение — вызов getItems() заканчивается ошибкой. Для View\Posts модель должна называться PostsModel, для View\Post — PostModel.