En las secciones sobre paginación y ordenación se describe como permitir a los usuarios finales elegir que se muestre una página de datos en particular, y ordenar los datos por algunas columnas. Como la tarea de paginar y ordenar datos es muy común, Yii proporciona un conjunto de clases proveedoras de datos para encapsularla.
Un proveedor de datos es una clase que implementa la interfaz yii\data\DataProviderInterface. Básicamente se encarga de obtener datos paginados y ordenados. Normalmente se usa junto con widgets de datos para que los usuarios finales puedan paginar y ordenar datos de forma interactiva.
Yii incluye las siguientes clases proveedoras de datos:
El uso de todos estos proveedores de datos comparte el siguiente patrón común:
// Crear el proveedor de datos configurando sus propiedades de paginación y ordenación
$provider = new XyzDataProvider([
'pagination' => [...],
'sort' => [...],
]);
// Obtener los datos paginados y ordenados
$models = $provider->getModels();
// Obtener el número de elementos de la página actual
$count = $provider->getCount();
// Obtener el número total de elementos entre todas las páginas
$totalCount = $provider->getTotalCount();
Se puede especificar los comportamientos de paginación y ordenación de un proveedor de datos
configurando sus propiedades pagination y
sort, que corresponden a las configuraciones para
yii\data\Pagination y yii\data\Sort respectivamente. También se pueden configurar a
false
para inhabilitar las funciones de paginación y/u ordenación.
Los widgets de datos, como yii\grid\GridView, tienen una
propiedad llamada dataProvider
que puede tomar una instancia de un proveedor de datos y
mostrar los datos que proporciona. Por ejemplo,
echo yii\grid\GridView::widget([
'dataProvider' => $dataProvider,
]);
Estos proveedores de datos varían principalmente en la manera en que se especifica la fuente de datos. En las siguientes secciones se explica el uso detallado de cada uno de estos proveedores de datos.
Para usar yii\data\ActiveDataProvider, hay que configurar su propiedad query. Puede tomar un objeto [[yii\db\Query] o yii\db\ActiveQuery. En el primer caso, los datos devueltos serán arrays. En el segundo, los datos devueltos pueden ser arrays o instancias de Active Record. Por ejemplo:
use yii\data\ActiveDataProvider;
$query = Post::find()->where(['state_id' => 1]);
$provider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 10,
],
'sort' => [
'defaultOrder' => [
'created_at' => SORT_DESC,
'title' => SORT_ASC,
]
],
]);
// Devuelve un array de objetos Post
$posts = $provider->getModels();
En el ejemplo anterior, si $query
se crea el siguiente código, el proveedor de datos
devolverá arrays en bruto.
use yii\db\Query;
$query = (new Query())->from('post')->where(['state' => 1]);
Nota: Si una consulta ya tiene la cláusula
orderBy
, las nuevas instrucciones de ordenación dadas por los usuarios finales (mediante la configuración desort
) se añadirán a la cláusulaorderBy
previa. Las cláusulaslimit
yoffset
que pueda haber se sobrescribirán por la petición de paginación de los usuarios finales (mediante la configuración depagination
).
Por omisión, yii\data\ActiveDataProvider usa el componente db
de la aplicación como
conexión con la base de datos. Se puede indicar una conexión con base de datos diferente
configurando la propiedad yii\data\ActiveDataProvider::$db.
yii\data\SqlDataProvider funciona con una sentencia SQL en bruto, que se usa para obtener
los datos requeridos.
Basándose en las especificaciones de sort y
pagination, el proveedor ajustará las cláusulas
ORDER BY
y LIMIT
de la sentencia SQL acordemente para obtener sólo la página de datos
solicitados en el orden deseado.
Para usar yii\data\SqlDataProvider, hay que especificar las propiedades sql y totalCount. Por ejemplo:
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',
],
],
]);
// Devuelve un array de filas de datos
$models = $provider->getModels();
Información: La propiedad totalCount se requiere sólo si se necesita paginar los datos. Esto es porque el proveedor modificará la sentencia SQL especificada vía sql para que devuelva sólo la pagina de datos solicitada. El proveedor sigue necesitando saber el número total de elementos de datos para calcular correctamente el número de páginas.
Se recomienda usar yii\data\ArrayDataProvider cuando se trabaja con un array grande. El proveedor permite devolver una página de los datos del array ordenados por una o varias columnas. Para usar yii\data\ArrayDataProvider, hay que especificar la propiedad allModels como el array grande. Los elementos del array grande pueden ser arrays asociativos (por ejemplo resultados de consultas de DAO u objetos (por ejemplo instancias de Active Record. Por ejemplo:
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'],
],
]);
// Obtener las filas de la página solicitada
$rows = $provider->getModels();
Nota: En comparación con Active Data Provider y SQL Data Provider, Array Data Provider es menos eficiente porque requiere cargar todos los datos en memoria.
Al utilizar los elementos de datos devueltos por un proveedor de datos, con frecuencia necesita identificar cada elemento de datos con una clave única. Por ejemplo, si los elementos de datos representan información de los clientes, puede querer usar el ID de cliente como la clave de cada conjunto de datos de un cliente. Los proveedores de datos pueden devolver una lista de estas claves correspondientes a los elementos de datos devueltos por yii\data\DataProviderInterface::getModels(). Por ejemplo:
use yii\data\ActiveDataProvider;
$query = Post::find()->where(['status' => 1]);
$provider = new ActiveDataProvider([
'query' => $query,
]);
// Devuelve un array de objetos Post
$posts = $provider->getModels();
// Devuelve los valores de las claves primarias correspondientes a $posts
$ids = $provider->getKeys();
En el ejemplo superior, como se le proporciona a yii\data\ActiveDataProvider un objeto yii\db\ActiveQuery, es lo suficientemente inteligente como para devolver los valores de las claves primarias como las claves. También puede indicar explícitamente cómo se deben calcular los valores de la clave configurando yii\data\ActiveDataProvider::$key con un nombre de columna o un invocable que calcule los valores de la clave. Por ejemplo:
// Utiliza la columna «slug» como valores de la clave
$provider = new ActiveDataProvider([
'query' => Post::find(),
'key' => 'slug',
]);
// Utiliza el resultado de md5(id) como valores de la clave
$provider = new ActiveDataProvider([
'query' => Post::find(),
'key' => function ($model) {
return md5($model->id);
}
]);
Para crear su propio proveedor de datos personalizado, debe implementar yii\data\DataProviderInterface. Una manera más fácil es extender yii\data\BaseDataProvider, que le permite centrarse en la lógica central del proveedor de datos. En particular, esencialmente necesita implementar los siguientes métodos:
Debajo se muestra un ejemplo de un proveedor de datos que lee datos CSV eficientemente:
<?php
use yii\data\BaseDataProvider;
class CsvDataProvider extends BaseDataProvider
{
/**
* @var string nombre del fichero CSV a leer
*/
public $filename;
/**
* @var string|callable nombre de la columna clave o un invocable que la devuelva
*/
public $key;
/**
* @var SplFileObject
*/
protected $fileObject; // SplFileObject es muy práctico para buscar una línea concreta en un fichero
/**
* {@inheritdoc}
*/
public function init()
{
parent::init();
// Abrir el fichero
$this->fileObject = new SplFileObject($this->filename);
}
/**
* {@inheritdoc}
*/
protected function prepareModels()
{
$models = [];
$pagination = $this->getPagination();
if ($pagination === false) {
// En caso de que no haya paginación, leer todas las líneas
while (!$this->fileObject->eof()) {
$models[] = $this->fileObject->fgetcsv();
$this->fileObject->next();
}
} else {
// En caso de que haya paginación, leer sólo una única página
$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;
}
return array_keys($models);
}
/**
* {@inheritdoc}
*/
protected function prepareTotalCount()
{
$count = 0;
while (!$this->fileObject->eof()) {
$this->fileObject->next();
++$count;
}
return $count;
}
}
Si bien puede construir condiciones para un proveedor de datos activo manualmente tal y como se describe en las secciones Filtering Data y Separate Filter Form de la guía de widgets de datos, Yii tiene filtros de datos que son muy útiles si necesita condiciones de filtro flexibles. Los filtros de datos se pueden usar así:
$filter = new ActiveDataFilter([
'searchModel' => 'app\models\PostSearch'
]);
$filterCondition = null;
// Puede cargar los filtros de datos de cualquier fuente.
// Por ejemplo, si prefiere JSON en el cuerpo de la petición,
// use Yii::$app->request->getBodyParams() aquí abajo:
if ($filter->load(\Yii::$app->request->get())) {
$filterCondition = $filter->build();
if ($filterCondition === false) {
// Serializer recibiría errores
return $filter;
}
}
$query = Post::find();
if ($filterCondition !== null) {
$query->andWhere($filterCondition);
}
return new ActiveDataProvider([
'query' => $query,
]);
El propósito del modelo PostSearch
es definir por qué propiedades y valores se permite filtrar:
use yii\base\Model;
class PostSearch extends Model
{
public $id;
public $title;
public function rules()
{
return [
['id', 'integer'],
['title', 'string', 'min' => 2, 'max' => 200],
];
}
}
Los filtros de datos son bastante flexibles. Puede personalizar cómo se construyen las condiciones y qué operadores se permiten. Para más detalles consulte la documentación de la API en yii\data\DataFilter.
Found a typo or you think this page needs improvement?
Edit it on github !
Signup or Login in order to comment.