Rendering Data in Yii 2 with GridView and ListView

Share this article

In my previous article about Yii 2.0, I introduced the ActiveRecord implementation. Once you have some data in your database you’ll want to be able to show it. Yii uses DataProviders to interact with data sources and it provides some widgets to output the data. Of these, the ListView and GridView provide the most functionality.

Yii logo

The example

I’m going to work with the database example that I introduced in my previous article again. Not very original, but quite useful as an example; a blog like SitePoint.

SitePoint Yii 2.0 blog example

A small wrap-up of this database design and its Yii models which I’ll be using in this example:

  • The Authors model has one relation method call getArticles() that provides an Articles[] attribute to the model.
  • The Articles model has one relation method call getAuthor() that provides the Author and a method getTags() that provides the Tags[].
  • The Tags model has one relation method call getArticles that provides Articles[].

I won’t be using the ArticlesTags table since it is just used for an N:M relation. Of course, there is a model available for it which is used in the relation definition of the other models.

DataProviders

There are three different types:

  • The ActiveDataProvider is fed through an ActiveQuery instance and usually holds an array of models. You’ll normally use it to hold and render data that can be built from normal ActiveRecord instances with their relations.
  • The ArrayDataProvider is built using an array of data. This is quite useful when building all sorts of reports based on multiple models and/or custom aggregations.
  • The SqlDataProvider is a very useful one for the SQL masters among us. It is an easy way to get the data of those complex queries.

They don’t have huge differences, except slightly in views. The data provider itself is a base component that holds data and handles pagination and sorting.

ActiveDataProvider

I already explained that this is populated with an ActiveQuery object. Any model::find() will return an ActiveQuery object so creating one is quite simple.

/* A dataprovider with all articles */
$dataProvider = new ActiveDataProvider([
    'query' => Articles::find()->with('tags'),
]);

/* Get all the articles for one author by using the author relation define in Articles */
$dataProvider = new ActiveDataProvider([
    'query' => Articles::find()->with('author')->where(['Name'=>'Arno Slatius']),
]);

/* Be careful with this! */
$dataProvider = new ActiveDataProvider([
    'query' => Articles::find()->with('author')->where(['Name'=>'Arno Slatius'])->orderBy('Published'),
]);

The last example is one to be careful with, you’ll limit the abilities of your DataProvider since it can do sorting on its own. Keep this in mind, I’ll address it later on.

SqlDataProvider

This one will gather your data based on a SQL statement. My example doesn’t really justify the use of SQL but I’ll do it anyway to show something and throw in some aggregation;

$dataProvider = new SqlDataProvider([
    'sql' => 'SELECT Name, Title, COUNT(ArticleTags.ID) AS TagCount ' . 
             'FROM Authors ' .
             'INNER JOIN Articles ON (Authors.ID = Articles.AuthorID) ' .
             'INNER JOIN ArticleTags ON (Articles.ID = ArticleTags.ID) ' .
             'WHERE Name=:author' .
             'GROUP BY ArticleID',
    'params' => [':author' => 'Arno Slatius'],
]);

There are good arguments to use an SQL query as input for your data provider. ActiveRecord is a very nice way to work with databases, but you might find that defining the criteria for larger databases gets somewhat complex. If you are good at writing SQL and you need a lot of joins, unions and/or aggregations you might find it easier to build an SQL query first and simply use that as an input to your DataProvider.

ArrayDataProvider

This might be the most useful component of the bunch. Anything that you’d normally put in a table that requires a bit more work to compose is well suited for an array data provider.
The documentation uses quite a simple example inserting a few models in an array like so:

$dataProvider = new ArrayDataProvider([
    'allModels' => Authors::find()->all(),
]);

That’ll fill the provider with all the authors. Very simple indeed, but let’s make it more practical and do something that this component is quite useful for; I want to output a list of all authors with their article count and last published date. I’m going to do this by extending the ArrayDataProvider and creating my own. I use the init() function which is available on almost all components. This is called when the class is created and used (in this case) to prepare the data in the provider.

namespace app\data;

use \app\models\Authors;
use \app\models\Articles;

class AuthorsReportProvider extends \yii\data\ArrayDataProvider
{
	/**
     * Initialize the dataprovider by filling allModels
     */
    public function init()
    {
        //Get all all authors with their articles
	    $query = Authors::find()->with('articles');
		foreach($query->all() as $author) {
			
			//Get the last published date if there are articles for the author
			if (count($author->articles)) {
				$lastPublished = $query->max('Published');
			} else {
				$lastPublished = null;
			}

			//Add rows with the Author, # of articles and last publishing date
			$this->allModels[] = [
				'name' => $authors->Name,
				'articles' => count($author->articles),
				'last' => $lastPublished,
			];
		}
	}
}

You can see that the variable containing all rows for the table to be shown, $this->allModels, is filled with the data we need.

Visualizing the data

We’ve got a data provider but now we want to render what’s in there, right? Yii provides two components to visualize the data put into the data provider.

  • The GridView will put the data in an HTML table. If properly configured, it’ll automatically add headers that you can click to change the sorting, pagination to limit the number of items shown at once to the user and a summary showing the total number of results available.
  • The ListView allows you to specify a partial view with which you can render each of the models in the data provider in a specific way. A very common way to go about this is using this to render the content of <li> elements of an ordered or unordered list.

It’s probably best illustrated with a few examples. Let’s assume the following data provider:

/* A dataprovider with all articles */
$dataProvider = new ActiveDataProvider([
    'query' => Articles::find()->with('tags','authors'),
]);

Note that I immediately make ActiveRecord execute a join of the Articles on the ArticleTags and Tags table by specifying the with() method. This is known as ‘eager loading’, making the related data available immediately. When done without the with() you’d still be able to use the data available from the author and tags relation in your views, but it would be loaded run time, also known as ‘lazy loading’. If you know you’re going to use related data, use with(). Eager loading performs a lot better than lazy loading.

In the view, we’d render a table with a GridView. I’m showing a few columns in increasing complexity, a simple list of the article first:

<?= GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        'ID',
        'Author',
        'Title',
        'Published:date',
    ],
]); ?>

Which will look like this:

Yii 2.0 GridView Blog Articles Example

I do admit that I cheated slightly by defining a page size of 6 and three columns by which it can sort; more on that later. Note the summary at the top, the sort links in the column headers and the pager at the bottom. Everything is formatted in a localized format (Dutch for me) where applicable. I needed no additional code to get the handling of links for the sorting or next pages working. The links are all automatically generated by the widget and actions are handled by the DataProvider. This component makes life really easy out of the box.

Since I joined the articles with other tables, we can use that data as well in a very easy table.column manner. Formatting of the data can also be customized using anonymous functions:

<?= GridView::widget([
		'dataProvider' => $dataProvider,
        'columns' => [
	        'Title',
	        'author.Name',
	        [
	            'attribute' => 'author.Twitter',
	            'type' => 'html',
	            'value' => function($model) {
		            return Html::link('@' . $model->author->Twitter, 'https://twitter.com/' . $model->author->Twitter);
	            }
	        ],
	        [
		        'attribute' => 'tags',
		        'value' => function($model) {
			        return implode(', ', ArrayHelper::map($model->tags, 'ID', 'Tag'));
			    }
			],
			'Published:date',
		],
	]); ?>

The key, when defining the output for the columns, is to remember that each extends from yii\grid\Column so always look back at that class to see all the attributes and functions you can use. For instance, yii\grid\DataColumn has a $footer attribute. Add that to the configuration array of a column to set the footer text, and don’t forget to set showFooter to true in the GridView.

The GridView actually has quite a few extra attributes through which you can configure it. I’ll use another example:

<?= GridView::widget([
		'dataProvider' => $dataProvider,
        'columns' => [
        ...
        ],
        'layout' => '{summary}{items}{pager}',
		'filterPosition' => FILTER_POS_FOOTER,
		'rowOptions' => function ($model, $key, $index, $grid) {
		},
		'emptyText' => '-',
		'showFooter' => true,
    ]); ?>

The layout attribute is a very useful one. It allows you to change the way the basic elements appear. You can add {summary}, {errors}, {items}, {sorter}, {pager} some even multiple times; you might want a pager above and below the items list, the same might go for the summary which basically shows the total number of results and the number of currently shown results. Errors are a list of errors that the filter outputs (if any).
filterPosition allows you to move the filter to the bottom, or inside the body. Normally it would be included in the header part of the table, etc.

Customizing Sorting and Paging

The GridView will do sorting and pagination for you by default. Its functionality is actually provided through configuration of the yii\data\BaseDataProvider which can be initialized by supplying additional parameters during the class’ initialization. Let’s start with simple pagination; by default, it is set to defaultPageSize which normally gives 20 results per page. Change it by supplying a pageSize, or use it to display everything and turn off pagination:

/* show 80 items per page */
$dataProvider = new ActiveDataProvider([
    'query' => Articles::find()->with('tags'),
    'pagination' => [
	    'pagesize' => 80,
    ],
]);

/* disable pagination, shows all items at once */
...
    'pagination' => false,
...
    'pagination' => [
	    'pagesize' => -1,    //Alternate method of disabling paging
    ],
...

Sorting has quite a few more options which depend on the data you supply and display. First, you can customize the sorting. You can supply multiple attributes to sort on for each column. When you use the GridView you might find that the default sort handling sometimes fails on more complex attributes or on values that you output using unnamed functions. In these situations, supplying sort attributes also helps.

Let’s go over some code:

$dataProvider = new ActiveDataProvider([
    'query' => Articles::find()->with('authors'),
    'sort' => [
	    'defaultOrder' => [
	        'Published' => SORT_DESC,
	        'LastEdited' => SORT_DESC,
	    ],
	    'attributes' => [
		    'name' => [
                'asc' => [
                    'author.Name' => SORT_ASC, 
                    'Published' => SORT_ASC,
                    'Title' => SORT_ASC,
                ],
                'desc' => [
                    'author.Name' => SORT_DESC
                    'Published' => SORT_DESC,
                    'Title' => SORT_DESC,
                ],
            ],
            ... etc ...
	    ],
    ],
]);

The defaultOrder changed, as you might guess, the initial ordering of the GridView, should it be rendered. In this example the ordering will be on ‘Published’ decending and ‘LastEdited’ descending next.

attributes will contain a list of all sortable attributes from the GridView’s columns (the attribute names here should match the attribute names in the GridView). You can independently define how the ascending and descending result will be sorted – again, this can be done on multiple columns in your DB.

One thing to note that for pagination to work the DataProvider needs to know the total number of results available. The ActiveDataProvider and SqlDataProvider are able to do it by default using database queries. If you use the ArrayDataProvider, you might need to do some additional work depending on how you fill the provider.

Conclusion

The ListView and GridView are very practical widgets which allow you to quickly show the data that you want. If you want even more from your GridView, then be sure to check out the ‘GridView on steroids’ made by Kartik Visweswaran. This adds a lot of additional functionality to your grids allowing the user to fully customize the way the data is shown. Be sure to check out all the other components from Kartik as well!

Are there more Yii 2.0 topics that you would like to see covered? Suggestions are welcome!

Frequently Asked Questions (FAQs) about Rendering Data in Yii 2 with GridView and ListView

How can I customize the GridView in Yii 2?

Customizing the GridView in Yii 2 is quite straightforward. You can customize the columns displayed in the GridView by modifying the ‘columns’ attribute in the GridView widget. Each column can be a string representing the attribute name, or an array for more complex definitions. For instance, you can use the ‘value’ property to determine what data will be displayed for a particular column. You can also use the ‘format’ property to specify the format of the data. For example, ‘format’ => ‘html’, ‘format’ => ‘text’, etc.

How can I add pagination to ListView in Yii 2?

Pagination in ListView can be achieved by using the Pagination class in Yii 2. You need to set the ‘pagination’ property of your data provider. This property is an array that specifies the configuration for creating the pagination object. For example, ‘pagination’ => [‘pageSize’ => 10], will display 10 items per page. The ListView widget will automatically display the pagination links at the bottom of the page.

How can I sort data in GridView in Yii 2?

Sorting data in GridView can be done by using the Sort class in Yii 2. You need to set the ‘sort’ property of your data provider. This property is an array that specifies the configuration for creating the sort object. For example, ‘sort’ => [‘defaultOrder’ => [‘name’ => SORT_ASC]], will sort the data by the ‘name’ attribute in ascending order by default. The GridView widget will automatically display the sort links in the header of each column.

How can I filter data in GridView in Yii 2?

Filtering data in GridView can be done by using the ActiveDataProvider and the Search model in Yii 2. You need to create a Search model that extends your original model and adds some rules for the attributes you want to filter. Then, in your controller, you create an instance of the ActiveDataProvider and the Search model, and you set the ‘query’ property of the data provider to the ‘search’ method of the Search model. The GridView widget will automatically display the filter inputs in the header of each column.

How can I customize the layout of ListView in Yii 2?

Customizing the layout of ListView can be done by modifying the ‘layout’ property of the ListView widget. This property is a string that specifies how different parts of the ListView should be arranged. For example, ‘layout’ => “{summary}\n{items}\n{pager}”, will display the summary text, the items, and the pager in this order. You can also use the ‘itemView’ property to specify a view file that will be used to render each item.

How can I handle empty data in GridView and ListView in Yii 2?

Handling empty data in GridView and ListView can be done by using the ’emptyText’ property of the GridView and ListView widgets. This property is a string that specifies the text to be displayed when the data provider does not have any data. For example, ’emptyText’ => ‘No data found.’, will display this message when there is no data.

How can I use AJAX in GridView and ListView in Yii 2?

Using AJAX in GridView and ListView can be done by using the Pjax widget in Yii 2. You need to wrap your GridView or ListView widget with the Pjax widget. This will enable AJAX pagination, sorting, and filtering without page refresh. You can also use the ‘clientOptions’ property of the Pjax widget to customize the AJAX behavior.

How can I use GridView and ListView in a form in Yii 2?

Using GridView and ListView in a form can be done by using the ActiveForm widget in Yii 2. You need to create an instance of the ActiveForm widget, and then you can use the GridView or ListView widget inside the form. The GridView or ListView will display the data, and the form will handle the input and submission.

How can I use a model relation in GridView and ListView in Yii 2?

Using a model relation in GridView and ListView can be done by using the relation name in the ‘columns’ property of the GridView widget or the ‘attributes’ property of the ListView widget. For example, ‘columns’ => [‘relationName.attributeName’], will display the attribute of the related model.

How can I use a custom data provider in GridView and ListView in Yii 2?

Using a custom data provider in GridView and ListView can be done by creating a class that extends the BaseDataProvider or implements the DataProviderInterface in Yii 2. You need to implement the ‘prepareModels’, ‘prepareKeys’, ‘prepareTotalCount’, and ‘prepare’ methods. Then, you can use your custom data provider in the ‘dataProvider’ property of the GridView or ListView widget.

Arno SlatiusArno Slatius
View Author

Arno Slatius is a web developer from the Netherlands. He has a background in electronics and embedded systems and started web development over a decade ago, He works with Yii professionally and loves digging into new things. Arno loves clean, commented and simple code. When he's not coding, he spends most of his time with his family or flying his model helies, planes and quads.

BrunoSdata visualizationframeworkgridsOOPHPPHPvisualizationvisualizationsyiiyii2
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week