Модели, ресурсные модели и коллекции
Чтобы работать с таблицей из кода, в Magento пишут три класса. Модель представляет одну запись, ресурсная модель выполняет запросы к базе, а коллекция выбирает сразу много записей. Команды для их создания нет — файлы создают вручную.
app/code/Acme/Hello/
└── Model/
├── Review.php модель
└── ResourceModel/
├── Review.php ресурсная модель
└── Review/
└── Collection.php коллекцияРесурсная модель
// app/code/Acme/Hello/Model/ResourceModel/Review.php
namespace Acme\Hello\Model\ResourceModel;
use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
class Review extends AbstractDb
{
protected function _construct(): void
{
$this->_init('acme_hello_review', 'review_id');
}
}Метод _init() называет таблицу и столбец первичного ключа. Обратите внимание: _construct() с одним подчёркиванием — это метод Magento, а не конструктор PHP.
Модель
// app/code/Acme/Hello/Model/Review.php
namespace Acme\Hello\Model;
use Magento\Framework\Model\AbstractModel;
class Review extends AbstractModel
{
protected function _construct(): void
{
$this->_init(ResourceModel\Review::class);
}
}Модель указывает свою ресурсную модель. Значения столбцов она хранит во внутреннем массиве: их читают через getData('author') или через «волшебный» метод getAuthor(), а записывают через setData('author', 'Анна') или setAuthor('Анна').
Коллекция
// app/code/Acme/Hello/Model/ResourceModel/Review/Collection.php
namespace Acme\Hello\Model\ResourceModel\Review;
use Acme\Hello\Model\ResourceModel\Review as ReviewResource;
use Acme\Hello\Model\Review;
use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
class Collection extends AbstractCollection
{
protected function _construct(): void
{
$this->_init(Review::class, ReviewResource::class);
}
}Коллекция знает, какие объекты создавать для строк таблицы и через какую ресурсную модель их читать.
Создание записи
Новую модель получают из фабрики, а сохраняет её ресурсная модель:
// app/code/Acme/Hello/Model/ReviewService.php
namespace Acme\Hello\Model;
use Acme\Hello\Model\ResourceModel\Review as ReviewResource;
class ReviewService
{
public function __construct(
private ReviewFactory $reviewFactory,
private ReviewResource $reviewResource,
) {}
public function add(int $productId, string $author, int $rating): int
{
$review = $this->reviewFactory->create();
$review->setData(['product_id' => $productId, 'author' => $author, 'rating' => $rating]);
$this->reviewResource->save($review);
return (int) $review->getId();
}
}Метод добавляет строку в таблицу и возвращает её номер. Загрузить запись по номеру можно вызовом $this->reviewResource->load($review, 5), удалить — $this->reviewResource->delete($review).
Чтение списка
Выведем последние отзывы на странице /hello. Модель представления получает фабрику коллекции:
// app/code/Acme/Hello/ViewModel/LatestReviews.php
namespace Acme\Hello\ViewModel;
use Acme\Hello\Model\ResourceModel\Review\CollectionFactory;
use Magento\Framework\View\Element\Block\ArgumentInterface;
class LatestReviews implements ArgumentInterface
{
public function __construct(private CollectionFactory $collectionFactory) {}
public function getReviews(): array
{
$collection = $this->collectionFactory->create();
$collection->addFieldToFilter('rating', ['gteq' => 4])
->setOrder('created_at', 'DESC')
->setPageSize(5);
return $collection->getItems();
}
}Метод возвращает до пяти последних отзывов с оценкой не ниже четырёх. Запрос к базе выполняется только при вызове getItems().
<!-- app/code/Acme/Hello/view/frontend/templates/reviews.phtml -->
<?php $viewModel = $block->getData('view_model'); ?>
<ul>
<?php foreach ($viewModel->getReviews() as $review): ?>
<li><?= $escaper->escapeHtml($review->getAuthor()) ?>: <?= (int) $review->getRating() ?></li>
<?php endforeach; ?>
</ul>Добавьте в макет hello_index_index.xml блок с этим шаблоном и моделью представления, выполните bin/magento cache:clean — на странице появится список авторов с оценками. Пока таблица пуста, список пуст; первые записи добавит урок о заплатках.
Условия фильтра
| Условие | Значение |
|---|---|
eq, neq | Равно, не равно |
gt, gteq, lt, lteq | Больше, не меньше, меньше, не больше |
like | Совпадение по шаблону |
in, nin | Входит в список, не входит |
null, notnull | Пусто, не пусто |
Частая ошибка
Модель создают словом new Review() или просят в конструкторе сам класс Review. В первом случае объект не получит своих зависимостей, во втором все классы будут делить один и тот же экземпляр с чужими данными. Новую запись всегда берите из ReviewFactory, а список — из CollectionFactory.