EntityReader
является полезным инструментом для безопасной передачи select-запросов
из репозитория в пользовательскую среду выполнения.
Под select-запросом подразумевается экземпляр одного из
классов: \Cycle\ORM\Select
или \Cycle\Database\Query\SelectQuery
.
Что нужно знать о EntityReader
:
- Класс
EntityReader
реализует интерфейсIteratorAggregate
. Это позволяет использовать объектEntityReader
в циклеforeach
. - С помощью
EntityReader
вы можете корректировать переданный select-запрос:- Выставлять
Limit
иOffset
вручную или с помощьюOffsetPaginator
. - Задавать сортировку. Но учтите, что сортировка
EntityReader
не заменяет сортировку в исходном запросе, а лишь дополняет её. Однако каждый следующий вызов методаwithSort()
будет заменять настройки сортировки объектаEntityReader
. - Применять фильтр. Условия фильтрации
EntityReader
также не заменяют настройки фильтрации в исходном запросе, а дополняют её. Таким образом, фильтрацией в объектеEntityReader
вы можете только уточнить выборку, но не расширить.
- Выставлять
EntityReader
не вытягивает данные из БД сразу. Обращение к БД происходит только тогда, когда эти данные запрашиваются.- Если вы будете использовать методы
read()
иreadOne()
для чтения данных, тоEntityReader
сохранит их в кеше. Кешируется также и результат вызоваcount()
. - Метод
count()
возвращает кол-во всех элементов выборки без учёта ограниченийLimit
иOffset
. - Если вы не хотите, чтобы данные были записаны в кеш, то используйте метод
getIterator()
. Однако если кеш уже заполнен, тоgetIterator()
вернёт содержимое кеша.
Напишем свой репозиторий для работы с таблицей статей, в котором будет метод для получения
списка публичных статей findPublic()
. Но метод findPublic()
не будет
возвращать сразу готовую коллекцию статей или select-запрос.
Вместо этого будет возвращаться EntityReader
с select-запросом внутри:
use Yiisoft\Data\Cycle\Data\Reader\EntityReader;
use Yiisoft\Data\Reader\DataReaderInterface;
class ArticleRepository extends \Cycle\ORM\Select\Repository
{
public function findPublic(): DataReaderInterface
{
return new EntityReader($this->select()->where(['public' => true]));
}
}
Рассмотрим примеры, как мы можем использовать EntityReader
в постраничной разбивке.
/**
* @var ArticleRepository $repository
* @var \Yiisoft\Data\Cycle\Data\Reader\EntityReader $articles
*/
$articles = $repository->findPublic();
// Смещение и лимит можно задать вручную
// Третья страница:
$pageReader = $articles->withLimit(10)->withOffset(20);
// Использование пагинатора
$paginator = new \Yiisoft\Data\Paginator\OffsetPaginator($articles);
$paginator->withPageSize(10)->withCurrentPage(3);
// Обход статей из объекта EntityReader:
// С сохранением в кеше:
foreach ($pageReader->read() as $article) {
// ...
}
// Без сохранения в кеше:
foreach ($pageReader as $article) {
// ...
}
// Обход статей из пагинатора:
foreach ($paginator->read() as $article) {
// ...
}
А теперь сделаем запрос на 20 последних опубликованных статей, а потом на 20 первых.
/**
* @var \Yiisoft\Data\Cycle\Data\Reader\EntityReader $articles
*/
// Порядок указания параметров не важен, так что начнём с установки лимита
$lastPublicReader = $articles->withLimit(20);
// Правила сортировки описываются в объекте класса Sort:
$sort = \Yiisoft\Data\Reader\Sort::any()->withOrder(['published_at' => 'desc']);
// Учтите, что EntityReader НЕ БУДЕТ проверять правильность указанных в Sort полей!
// Указание несуществующих в таблице полей приведёт к ошибке в коде Cycle
// Применяем правила сортировки и не забываем об иммутабельности
$lastPublicReader = $lastPublicReader->withSort($sort);
printf(
"Последние %d опубликованных статей из %d:",
count($lastPublicReader->read()),
$lastPublicReader->count()
);
foreach ($lastPublicReader->read() as $article) {
// ...
}
// Теперь получим 20 первых опубликованных статей
$sort = $lastPublicReader->getSort()->withOrder(['published_at' => 'asc']);
// Ввиду своей иммутабельности, запрошенный объект Sort не будет изменён,
// и текущие настройки сортировки $lastPublicReader останутся без изменения.
// Для того, чтобы применить новые правила сортировки нужно снова вызвать метод withSort():
$lastPublicReader = $lastPublicReader->withSort($sort);
printf(
"Первые %d опубликованных статей из %d:",
count($lastPublicReader->read()),
$lastPublicReader->count()
);
foreach ($lastPublicReader->read() as $article) {
// ...
}
Одна из особенностей сортировки запроса через EntityReader
заключается в том, что
она не заменяет сортировку в исходном select-запросе, а лишь дополняет её.
Бывает так, что нужно задать сортировку по умолчанию в методе репозитория, но при этом
иметь возможность изменить её в коде контроллера. Добиться этого можно следующим образом:
use Yiisoft\Data\Cycle\Data\Reader\EntityReader;
use Yiisoft\Data\Reader\DataReaderInterface;
use Yiisoft\Data\Reader\Sort;
class ArticleRepository extends \Cycle\ORM\Select\Repository
{
public function findPublic(): DataReaderInterface
{
$sort = Sort::any()->withOrder(['published_at' => 'desc']);
// Параметры сортировки присваиваются объекту DataReader, а не \Cycle\ORM\Select
return (new EntityReader($this->select()->where(['public' => true])))->withSort($sort);
}
}
// class SiteController ... {
function index(ArticleRepository $repository)
{
$articlesReader = $repository
// Получаем объект EntityReader
->findPublic()
// Применяем новое правило сортировки
->withSort(Sort::any()->withOrder(['published_at' => 'asc']));
}
Уточнить условия выборки можно с помощью фильтров. Они также дополняют условия выборки запроса, а не заменяют их.
use Yiisoft\Data\Cycle\Data\Reader\EntityReader;
use Yiisoft\Data\Reader\DataReaderInterface;
use Yiisoft\Data\Reader\Filter\Equals;
class ArticleRepository extends \Cycle\ORM\Select\Repository
{
public function findUserArticles(int $userId): DataReaderInterface
{
return (new EntityReader($this->select()->where('user_id', $userId)))
// Добавим фильтр по умолчанию - только public статьи
->withFilter(new Equals('public', '1'));
// Условие `public` = "1" не заменит `user_id` = "$userId"
}
}
Используйте фильтры пакета yiisoft/data, либо любые другие, предварительно написав для них соответствующие обработчики (процессоры).