Поставщики данных
В разделах Пагинация и Сортировка мы описали, как позволить конечным пользователям выбирать конкретную страницу данных для отображения и сортировать их по некоторым столбцам. Поскольку задача пагинации и сортировки данных очень распространённая, Yii предоставляет набор классов поставщиков данных для инкапсуляции этой задачи.
Поставщик данных — это класс, реализующий yii\data\DataProviderInterface. Он в основном поддерживает получение постраничных и отсортированных данных. Обычно он используется для работы с виджетами данных, чтобы конечные пользователи могли интерактивно постраничивать и сортировать данные.
В выпусках Yii включены следующие классы поставщиков данных:
- yii\data\ActiveDataProvider: использует yii\db\Query или yii\db\ActiveQuery для запроса данных из баз данных и возвращает их в виде массивов или экземпляров Active Record.
- yii\data\SqlDataProvider: выполняет SQL-запрос и возвращает данные базы данных в виде массивов.
- yii\data\ArrayDataProvider: принимает большой массив и возвращает его часть, основываясь на параметрах пагинации и сортировки.
Использование всех этих поставщиков данных имеет следующую общую структуру:
// create the data provider by configuring its pagination and sort properties
$provider = new XyzDataProvider([
'pagination' => [...],
'sort' => [...],
]);
// retrieves paginated and sorted data
$models = $provider->getModels();
// get the number of data items in the current page
$count = $provider->getCount();
// get the total number of data items across all pages
$totalCount = $provider->getTotalCount();
Вы задаёте поведение пагинации и сортировки поставщика данных, настраивая его свойства pagination и sort, которые соответствуют настройкам yii\data\Pagination и yii\data\Sort соответственно. Вы также можете настроить их на false чтобы отключить функции пагинации и/или сортировки.
Виджеты данных, такие как yii\grid\GridView, имеют свойство, названное dataProvider , которое может принимать экземпляр поставщика данных и отображать предоставляемые им данные. Например,
echo yii\grid\GridView::widget([
'dataProvider' => $dataProvider,
]);
Эти поставщики данных в основном различаются способом указания источника данных. В следующих подразделах мы объясним подробное использование каждого из этих поставщиков данных.
Активный поставщик данных
Для использования yii\data\ActiveDataProvider необходимо настроить его свойство query. Оно может принимать объект yii\db\Query или yii\db\ActiveQuery. Если первый, возвращаемые данные будут массивами; если второй, возвращаемые данные могут быть либо массивами, либо экземплярами Active Record.
use yii\data\ActiveDataProvider;
$query = Post::find()->where(['status' => 1]);
$provider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 10,
],
'sort' => [
'defaultOrder' => [
'created_at' => SORT_DESC,
'title' => SORT_ASC,
]
],
]);
// returns an array of Post objects
$posts = $provider->getModels();
Если $query в приведённом выше примере создаётся с помощью следующего кода, то поставщик данных будет возвращать обычные массивы.
use yii\db\Query;
$query = (new Query())->from('post')->where(['status' => 1]);
Примечание: Если запрос уже содержит
orderByусловие, новые инструкции по сортировке, заданные конечными пользователями (через настройкуsort), будут добавлены к существующемуorderByусловию. Любые существующиеlimitиoffsetусловия будут перезаписаны запросом на пагинацию от конечных пользователей (через настройкуpagination).
По умолчанию yii\data\ActiveDataProvider использует компонент db приложения в качестве соединения с базой данных. Вы можете использовать другое соединение с базой данных, настроив свойство yii\data\ActiveDataProvider::$db.
Поставщик данных SQL
yii\data\SqlDataProvider работает с исходным SQL-запросом, который используется для получения необходимых данных. Основываясь на параметрах sort и pagination, поставщик будет изменять ORDER BY и LIMIT условия SQL-запроса соответственно, чтобы получить только запрошенную страницу данных в нужном порядке.
Для использования yii\data\SqlDataProvider, вы должны указать свойство sql, а также свойство totalCount. Например,
use yii\data\SqlDataProvider;
$count = Yii::$app->db->createCommand('
SELECT COUNT(*) FROM post WHERE status=:status
', [':status' => 1])->queryScalar();
$provider = new SqlDataProvider([
'sql' => 'SELECT * FROM post WHERE status=:status',
'params' => [':status' => 1],
'totalCount' => $count,
'pagination' => [
'pageSize' => 10,
],
'sort' => [
'attributes' => [
'title',
'view_count',
'created_at',
],
],
]);
// returns an array of data rows
$models = $provider->getModels();
Информация: Свойство totalCount требуется только если вам нужно постранично отображать данные. Это связано с тем, что SQL-запрос, указанный через sql, будет изменён поставщиком, чтобы вернуть только текущую запрошенную страницу данных. Поставщик по-прежнему должен знать общее количество элементов данных, чтобы правильно рассчитать количество доступных страниц.
Поставщик данных массивов
yii\data\ArrayDataProvider лучше всего использовать при работе с большим массивом. Поставщик позволяет вернуть страницу данных массива, отсортированную по одному или нескольким столбцам. Чтобы использовать yii\data\ArrayDataProvider, вы должны указать свойство allModels как большой массив. Элементы в большом массиве могут быть либо ассоциативными массивами (например, результатами запросов DAO), либо объектами (например, экземплярами Active Record).
use yii\data\ArrayDataProvider;
$data = [
['id' => 1, 'name' => 'name 1', ...],
['id' => 2, 'name' => 'name 2', ...],
...
['id' => 100, 'name' => 'name 100', ...],
];
$provider = new ArrayDataProvider([
'allModels' => $data,
'pagination' => [
'pageSize' => 10,
],
'sort' => [
'attributes' => ['id', 'name'],
],
]);
// get the rows in the currently requested page
$rows = $provider->getModels();
Примечание: По сравнению с Активным поставщиком данных и SQL-поставщиком данных, поставщик данных массивов менее эффективен, потому что требует загрузки всех данных в память.
Работа с ключами данных
При использовании элементов данных, возвращаемых поставщиком данных, часто необходимо идентифицировать каждый элемент данных уникальным ключом. Например, если элементы данных представляют информацию о клиентах, вы можете использовать идентификатор клиента в качестве ключа для каждого набора данных клиента. Поставщики данных могут возвращать список таких ключей, соответствующих элементам данных, возвращаемым yii\data\DataProviderInterface::getModels(). Например,
use yii\data\ActiveDataProvider;
$query = Post::find()->where(['status' => 1]);
$provider = new ActiveDataProvider([
'query' => $query,
]);
// returns an array of Post objects
$posts = $provider->getModels();
// returns the primary key values corresponding to $posts
$ids = $provider->getKeys();
В примере выше, так как вы предоставляете yii\data\ActiveDataProvider объект yii\db\ActiveQuery, он достаточно умён, чтобы вернуть значения первичных ключей в качестве ключей. Вы также можете явно указать, как должны рассчитываться значения ключей, настроив yii\data\ActiveDataProvider::$key с именем столбца или вызываемым объектом, вычисляющим значения ключей. Например,
// use "slug" column as key values
$provider = new ActiveDataProvider([
'query' => Post::find(),
'key' => 'slug',
]);
// use the result of md5(id) as key values
$provider = new ActiveDataProvider([
'query' => Post::find(),
'key' => function ($model) {
return md5($model->id);
}
]);
Создание пользовательского поставщика данных
Для создания собственных классов поставщиков данных необходимо реализовать yii\data\DataProviderInterface. Более простой способ — расширить yii\data\BaseDataProvider, что позволит сосредоточиться на основной логике поставщика данных. В частности, вам нужно в основном реализовать следующие методы:
- prepareModels(): подготавливает модели данных, которые будут доступны на текущей странице, и возвращает их в виде массива.
- prepareKeys(): принимает массив доступных моделей данных и возвращает ключи, связанные с ними.
- prepareTotalCount: возвращает значение, указывающее общее количество моделей данных в поставщике данных.
Ниже приведён пример поставщика данных, эффективно считывающего данные CSV:
<?php
use yii\data\BaseDataProvider;
class CsvDataProvider extends BaseDataProvider
{
/**
* @var string name of the CSV file to read
*/
public $filename;
/**
* @var string|callable name of the key column or a callable returning it
*/
public $key;
/**
* @var SplFileObject
*/
protected $fileObject; // SplFileObject is very convenient for seeking to particular line in a file
/**
* @inheritdoc
*/
public function init()
{
parent::init();
// open file
$this->fileObject = new SplFileObject($this->filename);
}
/**
* @inheritdoc
*/
protected function prepareModels()
{
$models = [];
$pagination = $this->getPagination();
if ($pagination === false) {
// in case there's no pagination, read all lines
while (!$this->fileObject->eof()) {
$models[] = $this->fileObject->fgetcsv();
$this->fileObject->next();
}
} else {
// in case there's pagination, read only a single page
$pagination->totalCount = $this->getTotalCount();
$this->fileObject->seek($pagination->getOffset());
$limit = $pagination->getLimit();
for ($count = 0; $count < $limit; ++$count) {
$models[] = $this->fileObject->fgetcsv();
$this->fileObject->next();
}
}
return $models;
}
/**
* @inheritdoc
*/
protected function prepareKeys($models)
{
if ($this->key !== null) {
$keys = [];
foreach ($models as $model) {
if (is_string($this->key)) {
$keys[] = $model[$this->key];
} else {
$keys[] = call_user_func($this->key, $model);
}
}
return $keys;
} else {
return array_keys($models);
}
}
/**
* @inheritdoc
*/
protected function prepareTotalCount()
{
$count = 0;
while (!$this->fileObject->eof()) {
$this->fileObject->next();
++$count;
}
return $count;
}
}
© 2008–2017 by Yii Software LLC
Licensed under the three clause BSD license.
http://www.yiiframework.com/doc-2.0/guide-output-data-providers.html