mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-01 03:26:36 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			400 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			400 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| Data Providers
 | |
| ==============
 | |
| 
 | |
| In the [Pagination](output-pagination.md) and [Sorting](output-sorting.md) sections, we have described how to
 | |
| allow end users to choose a particular page of data to display and sort them by some columns. Because the task
 | |
| of paginating and sorting data is very common, Yii provides a set of *data provider* classes to encapsulate it.
 | |
| 
 | |
| A data provider is a class implementing [[yii\data\DataProviderInterface]]. It mainly supports retrieving paginated
 | |
| and sorted data. It is usually used to work with [data widgets](output-data-widgets.md) so that end users can 
 | |
| interactively paginate and sort data. 
 | |
| 
 | |
| The following data provider classes are included in the Yii releases:
 | |
| 
 | |
| * [[yii\data\ActiveDataProvider]]: uses [[yii\db\Query]] or [[yii\db\ActiveQuery]] to query data from databases
 | |
|   and return them in terms of arrays or [Active Record](db-active-record.md) instances.
 | |
| * [[yii\data\SqlDataProvider]]: executes a SQL statement and returns database data as arrays.
 | |
| * [[yii\data\ArrayDataProvider]]: takes a big array and returns a slice of it based on the paginating and sorting
 | |
|   specifications.
 | |
| 
 | |
| The usage of all these data providers share the following common pattern:
 | |
| 
 | |
| ```php
 | |
| // 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();
 | |
| ```
 | |
| 
 | |
| You specify the pagination and sorting behaviors of a data provider by configuring its 
 | |
| [[yii\data\BaseDataProvider::pagination|pagination]] and [[yii\data\BaseDataProvider::sort|sort]] properties
 | |
| which correspond to the configurations for [[yii\data\Pagination]] and [[yii\data\Sort]], respectively.
 | |
| You may also configure them to be `false` to disable pagination and/or sorting features.
 | |
| 
 | |
| [Data widgets](output-data-widgets.md), such as [[yii\grid\GridView]], have a property named `dataProvider` which
 | |
| can take a data provider instance and display the data it provides. For example,
 | |
| 
 | |
| ```php
 | |
| echo yii\grid\GridView::widget([
 | |
|     'dataProvider' => $dataProvider,
 | |
| ]);
 | |
| ```
 | |
| 
 | |
| These data providers mainly vary in the way how the data source is specified. In the following subsections,
 | |
| we will explain the detailed usage of each of these data providers.
 | |
| 
 | |
| 
 | |
| ## Active Data Provider <span id="active-data-provider"></span> 
 | |
| 
 | |
| To use [[yii\data\ActiveDataProvider]], you should configure its [[yii\data\ActiveDataProvider::query|query]] property.
 | |
| It can take either a [[yii\db\Query]] or [[yii\db\ActiveQuery]] object. If the former, the data returned will be arrays;
 | |
| if the latter, the data returned can be either arrays or [Active Record](db-active-record.md) instances.
 | |
| For example,
 | |
| 
 | |
| ```php
 | |
| 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();
 | |
| ```
 | |
| 
 | |
| If `$query` in the above example is created using the following code, then the data provider will return raw arrays.
 | |
| 
 | |
| ```php
 | |
| use yii\db\Query;
 | |
| 
 | |
| $query = (new Query())->from('post')->where(['status' => 1]); 
 | |
| ```
 | |
| 
 | |
| > Note: If a query already specifies the `orderBy` clause, the new ordering instructions given by end users
 | |
|   (through the `sort` configuration) will be appended to the existing `orderBy` clause. Any existing `limit`
 | |
|   and `offset` clauses will be overwritten by the pagination request from end users (through the `pagination` configuration). 
 | |
| 
 | |
| By default, [[yii\data\ActiveDataProvider]] uses the `db` application component as the database connection. You may
 | |
| use a different database connection by configuring the [[yii\data\ActiveDataProvider::db]] property.
 | |
| 
 | |
| 
 | |
| ## SQL Data Provider <span id="sql-data-provider"></span>
 | |
| 
 | |
| [[yii\data\SqlDataProvider]] works with a raw SQL statement which is used to fetch the needed
 | |
| data. Based on the specifications of [[yii\data\SqlDataProvider::sort|sort]] and 
 | |
| [[yii\data\SqlDataProvider::pagination|pagination]], the provider will adjust the `ORDER BY` and `LIMIT`
 | |
| clauses of the SQL statement accordingly to fetch only the requested page of data in the desired order.
 | |
| 
 | |
| To use [[yii\data\SqlDataProvider]], you should specify the [[yii\data\SqlDataProvider::sql|sql]] property as well
 | |
| as the [[yii\data\SqlDataProvider::totalCount|totalCount]] property. For example,
 | |
| 
 | |
| ```php
 | |
| 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();
 | |
| ```
 | |
| 
 | |
| > Info: The [[yii\data\SqlDataProvider::totalCount|totalCount]] property is required only if you need to
 | |
|   paginate the data. This is because the SQL statement specified via [[yii\data\SqlDataProvider::sql|sql]]
 | |
|   will be modified by the provider to return only the currently requested page of data. The provider still
 | |
|   needs to know the total number of data items in order to correctly calculate the number of pages available.
 | |
| 
 | |
| 
 | |
| ## Array Data Provider <span id="array-data-provider"></span>
 | |
| 
 | |
| [[yii\data\ArrayDataProvider]] is best used when working with a big array. The provider allows you to return
 | |
| a page of the array data sorted by one or multiple columns. To use [[yii\data\ArrayDataProvider]], you should
 | |
| specify the [[yii\data\ArrayDataProvider::allModels|allModels]] property as the big array.
 | |
| Elements in the big array can be either associative arrays
 | |
| (e.g. query results of [DAO](db-dao.md)) or objects (e.g. [Active Record](db-active-record.md) instances).
 | |
| For example,
 | |
| 
 | |
| ```php
 | |
| 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();
 | |
| ``` 
 | |
| 
 | |
| > Note: Compared to [Active Data Provider](#active-data-provider) and [SQL Data Provider](#sql-data-provider),
 | |
|   array data provider is less efficient because it requires loading *all* data into the memory.
 | |
| 
 | |
| 
 | |
| ## Working with Data Keys <span id="working-with-keys"></span>
 | |
| 
 | |
| When using the data items returned by a data provider, you often need to identify each data item with a unique key.
 | |
| For example, if the data items represent customer information, you may want to use the customer ID as the key
 | |
| for each customer data. Data providers can return a list of such keys corresponding with the data items returned 
 | |
| by [[yii\data\DataProviderInterface::getModels()]]. For example,
 | |
| 
 | |
| ```php
 | |
| 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();
 | |
| ```
 | |
| 
 | |
| In the above example, because you provide to [[yii\data\ActiveDataProvider]] an [[yii\db\ActiveQuery]] object,
 | |
| it is intelligent enough to return primary key values as the keys. You may also explicitly specify how the key
 | |
| values should be calculated by configuring [[yii\data\ActiveDataProvider::key]] with a column name or
 | |
| a callable calculating key values. For example,
 | |
| 
 | |
| ```php
 | |
| // 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);
 | |
|     }
 | |
| ]);
 | |
| ```
 | |
| 
 | |
| 
 | |
| ## Creating Custom Data Provider <span id="custom-data-provider"></span>
 | |
| 
 | |
| To create your own custom data provider classes, you should implement [[yii\data\DataProviderInterface]].
 | |
| An easier way is to extend from [[yii\data\BaseDataProvider]] which allows you to focus on the core data provider
 | |
| logic. In particular, you mainly need to implement the following methods:
 | |
|                                                    
 | |
| - [[yii\data\BaseDataProvider::prepareModels()|prepareModels()]]: prepares the data models that will be made 
 | |
|   available in the current page and returns them as an array.
 | |
| - [[yii\data\BaseDataProvider::prepareKeys()|prepareKeys()]]: accepts an array of currently available data models
 | |
|   and returns keys associated with them.
 | |
| - [[yii\data\BaseDataProvider::prepareTotalCount()|prepareTotalCount]]: returns a value indicating the total number 
 | |
|   of data models in the data provider.
 | |
| 
 | |
| Below is an example of a data provider that reads CSV data efficiently:
 | |
| 
 | |
| ```php
 | |
| <?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;
 | |
|         }
 | |
| 
 | |
|         return array_keys($models);
 | |
|     }
 | |
|  
 | |
|     /**
 | |
|      * {@inheritdoc}
 | |
|      */
 | |
|     protected function prepareTotalCount()
 | |
|     {
 | |
|         $count = 0;
 | |
|  
 | |
|         while (!$this->fileObject->eof()) {
 | |
|             $this->fileObject->next();
 | |
|             ++$count;
 | |
|         }
 | |
|  
 | |
|         return $count;
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Filtering Data Providers using Data Filters <span id="filtering-data-providers-using-data-filters"></span>
 | |
| 
 | |
| While you can build conditions for active data provider manually as described in
 | |
| [Filtering Data](output-data-widgets.md#filtering-data) and [Separate Filter Form](output-data-widgets.md#separate-filter-form)
 | |
| sections of data widgets guide, Yii has data filters that are very useful if you need flexible filter conditions.
 | |
| Data filters could be used as follows:
 | |
| 
 | |
| ```php
 | |
| $filter = new ActiveDataFilter([
 | |
|     'searchModel' => 'app\models\PostSearch'
 | |
| ]);
 | |
| 
 | |
| $filterCondition = null;
 | |
| 
 | |
| // You may load filters from any source. For example,
 | |
| // if you prefer JSON in request body,
 | |
| // use Yii::$app->request->getBodyParams() below:
 | |
| if ($filter->load(\Yii::$app->request->get())) { 
 | |
|     $filterCondition = $filter->build();
 | |
|     if ($filterCondition === false) {
 | |
|         // Serializer would get errors out of it
 | |
|         return $filter;
 | |
|     }
 | |
| }
 | |
| 
 | |
| $query = Post::find();
 | |
| if ($filterCondition !== null) {
 | |
|     $query->andWhere($filterCondition);
 | |
| }
 | |
| 
 | |
| return new ActiveDataProvider([
 | |
|     'query' => $query,
 | |
| ]);
 | |
| ```
 | |
| 
 | |
| `PostSearch` model serves the purpose of defining which properties and values are allowed for filtering:
 | |
| 
 | |
| ```php
 | |
| use yii\base\Model;
 | |
| 
 | |
| class PostSearch extends Model 
 | |
| {
 | |
|     public $id;
 | |
|     public $title;
 | |
|     
 | |
|     public function rules()
 | |
|     {
 | |
|         return [
 | |
|             ['id', 'integer'],
 | |
|             ['title', 'string', 'min' => 2, 'max' => 200],            
 | |
|         ];
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| Data filters are quite flexible. You may customize how conditions are built and which operators are allowed.
 | |
| For details check API docs on [[\yii\data\DataFilter]].
 | 
