output-data-widgets.md 16.9 KB
Newer Older
Qiang Xue committed
1 2 3
Data widgets
============

4
> Note: This section is under development.
Qiang Xue committed
5

Qiang Xue committed
6 7 8 9 10 11 12 13 14 15 16 17 18
ListView
--------



DetailView
----------

DetailView displays the detail of a single data [[yii\widgets\DetailView::$model|model]].
 
It is best used for displaying a model in a regular format (e.g. each model attribute is displayed as a row in a table).
The model can be either an instance of [[\yii\base\Model]] or an associative array.
 
19
DetailView uses the [[yii\widgets\DetailView::$attributes]] property to determine which model attributes should be displayed and how they
Qiang Xue committed
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
should be formatted.
 
A typical usage of DetailView is as follows:
 
```php
echo DetailView::widget([
    'model' => $model,
    'attributes' => [
        'title',             // title attribute (in plain text)
        'description:html',  // description attribute in HTML
        [                    // the owner name of the model
            'label' => 'Owner',
            'value' => $model->owner->name,
        ],
    ],
]);
```


GridView
--------
41

42
Data grid or GridView is one of the most powerful Yii widgets. It is extremely useful if you need to quickly build the admin
43
section of the system. It takes data from [data provider](output-data-providers.md) and renders each row using a set of columns
44
presenting data in the form of a table.
45 46

Each row of the table represents the data of a single data item, and a column usually represents an attribute of
47
the item (some columns may correspond to complex expressions of attributes or static text).
48 49

Grid view supports both sorting and pagination of the data items. The sorting and pagination can be done in AJAX mode
50
or as a normal page request. A benefit of using GridView is that when the user disables JavaScript, the sorting and pagination automatically degrade to normal page requests and still function as expected.
51

Anderson Müller committed
52
The minimal code needed to use GridView is as follows:
53 54

```php
55
use yii\grid\GridView;
Alexander Makarov committed
56 57 58
use yii\data\ActiveDataProvider;

$dataProvider = new ActiveDataProvider([
59 60 61 62
    'query' => Post::find(),
    'pagination' => [
        'pageSize' => 20,
    ],
Alexander Makarov committed
63 64
]);
echo GridView::widget([
65
    'dataProvider' => $dataProvider,
Alexander Makarov committed
66
]);
67 68 69
```

The above code first creates a data provider and then uses GridView to display every attribute in every row taken from
70
the data provider. The displayed table is equipped with sorting and pagination functionality.
71

Qiang Xue committed
72
### Grid columns
73 74 75

Yii grid consists of a number of columns. Depending on column type and settings these are able to present data differently.

76
These are defined in the columns part of GridView configuration like the following:
77 78

```php
79
echo GridView::widget([
80 81 82 83
    'dataProvider' => $dataProvider,
    'columns' => [
        ['class' => 'yii\grid\SerialColumn'],
        // A simple column defined by the data contained in $dataProvider.
84
        // Data from the model's column1 will be used.
85 86 87 88 89 90
        'id',
        'username',
        // More complex one.
        [
            'class' => 'yii\grid\DataColumn', // can be omitted, default
            'value' => function ($data) {
91
                return $data->name; //$data['name'] for array data, e.g. using SqlDataProvider.
92 93 94
            },
        ],
    ],
95
]);
96 97
```

98
Note that if the columns part of the configuration isn't specified, Yii tries to show all possible data provider model columns.
99 100 101

### Column classes

Alexander Makarov committed
102 103 104 105
Grid columns could be customized by using different column classes:

```php
echo GridView::widget([
106 107 108 109 110 111
    'dataProvider' => $dataProvider,
    'columns' => [
        [
            'class' => 'yii\grid\SerialColumn', // <-- here
            // you may configure additional properties here
        ],
Alexander Makarov committed
112 113
```

114
In addition to column classes provided by Yii that we'll review below you can create your own column classes.
Alexander Makarov committed
115

116
Each column class extends from [[\yii\grid\Column]] so there are some common options you can set while configuring
Alexander Makarov committed
117 118 119 120
grid columns.

- `header` allows to set content for header row.
- `footer` allows to set content for footer row.
121
- `visible` defines if the column should be visible.
Alexander Makarov committed
122 123
- `content` allows you to pass a valid PHP callback that will return data for a row. The format is the following:

124 125 126 127 128
  ```php
  function ($model, $key, $index, $column) {
      return 'a string';
  }
  ```
Alexander Makarov committed
129

130
You may specify various container HTML options by passing arrays to:
Alexander Makarov committed
131 132 133 134

- `headerOptions`
- `footerOptions`
- `filterOptions`
135
- `contentOptions`
136

137
#### Data column <a name="data-column"></a>
138

139
Data column is used for displaying and sorting data. It is the default column type so the specifying class could be omitted when
Alexander Makarov committed
140 141
using it.

142 143
The main setting of the data column is its format. It could be specified via `format` attribute. Its values
correspond to methods in the `formatter` [application component](structure-application-components.md) that is [[\yii\i18n\Formatter|Formatter]] by default:
144 145

```php
146
echo GridView::widget([
147 148 149 150
    'columns' => [
        [
            'attribute' => 'name',
            'format' => 'text'
151
        ],
152 153 154 155 156
        [
            'attribute' => 'birthday',
            'format' => ['date', 'Y-m-d']
        ],
    ],
157
]); 
158
```
159

160 161
In the above, `text` corresponds to [[\yii\i18n\Formatter::asText()]]. The value of the column is passed as the first
argument. In the second column definition, `date` corresponds to [[\yii\i18n\Formatter::asDate()]]. The value of the
162 163
column is, again, passed as the first argument while 'Y-m-d' is used as the second argument value.

164 165
For a list of available formatters see the [section about Data Formatting](output-formatter.md).

Alexander Makarov committed
166

167 168
#### Action column

Alexander Makarov committed
169 170 171 172
Action column displays action buttons such as update or delete for each row.

```php
echo GridView::widget([
173 174 175 176 177 178
    'dataProvider' => $dataProvider,
    'columns' => [
        [
            'class' => 'yii\grid\ActionColumn',
            // you may configure additional properties here
        ],
Alexander Makarov committed
179 180 181 182 183 184
```

Available properties you can configure are:

- `controller` is the ID of the controller that should handle the actions. If not set, it will use the currently active
  controller.
185
- `template` defines the template used for composing each cell in the action column. Tokens enclosed within curly brackets are
Alexander Makarov committed
186
  treated as controller action IDs (also called *button names* in the context of action column). They will be replaced
187
  by the corresponding button rendering callbacks specified in [[yii\grid\ActionColumn::$buttons|buttons]]. For example, the token `{view}` will be
Alexander Makarov committed
188
  replaced by the result of the callback `buttons['view']`. If a callback cannot be found, the token will be replaced
189
  with an empty string. The default tokens are `{view} {update} {delete}`.
Alexander Makarov committed
190 191 192 193
- `buttons` is an array of button rendering callbacks. The array keys are the button names (without curly brackets),
  and the values are the corresponding button rendering callbacks. The callbacks should use the following signature:

```php
194
function ($url, $model, $key) {
195
    // return the button HTML code
Alexander Makarov committed
196 197 198
}
```

199
In the code above, `$url` is the URL that the column creates for the button, `$model` is the model object being
200
rendered for the current row, and `$key` is the key of the model in the data provider array.
Alexander Makarov committed
201 202

- `urlCreator` is a callback that creates a button URL using the specified model information. The signature of
MarsuBoss committed
203 204
  the callback should be the same as that of [[yii\grid\ActionColumn::createUrl()]]. If this property is not set,
  button URLs will be created using [[yii\grid\ActionColumn::createUrl()]].
Alexander Makarov committed
205

206 207
#### Checkbox column

Alexander Makarov committed
208
CheckboxColumn displays a column of checkboxes.
Qiang Xue committed
209

210
To add a CheckboxColumn to the [[yii\grid\GridView]], add it to the [[yii\grid\GridView::$columns|columns]] configuration as follows:
Qiang Xue committed
211

Alexander Makarov committed
212 213
```php
echo GridView::widget([
214 215 216 217 218 219 220 221
    'dataProvider' => $dataProvider,
    'columns' => [
        // ...
        [
            'class' => 'yii\grid\CheckboxColumn',
            // you may configure additional properties here
        ],
    ],
Alexander Makarov committed
222 223 224 225 226 227 228 229 230 231
```

Users may click on the checkboxes to select rows of the grid. The selected rows may be obtained by calling the following
JavaScript code:

```javascript
var keys = $('#grid').yiiGridView('getSelectedRows');
// keys is an array consisting of the keys associated with the selected rows
```

232 233
#### Serial column

Alexander Makarov committed
234 235 236 237 238 239
Serial column renders row numbers starting with `1` and going forward.

Usage is as simple as the following:

```php
echo GridView::widget([
240 241 242
    'dataProvider' => $dataProvider,
    'columns' => [
        ['class' => 'yii\grid\SerialColumn'], // <-- here
243
        // ...
Alexander Makarov committed
244 245
```

246 247

### Sorting data
248 249 250

- https://github.com/yiisoft/yii2/issues/1576

251
### Filtering data
252

253
For filtering data the GridView needs a [model](structure-models.md) that takes the input from the filtering
254
form and adjusts the query of the dataProvider to respect the search criteria.
255
A common practice when using [active records](db-active-record.md) is to create a search Model class
Mark committed
256 257
that provides needed functionality (it can be generated for you by Gii). This class defines the validation 
rules for the search and provides a `search()` method that will return the data provider.
258

259
To add the search capability for the `Post` model, we can create `PostSearch` like in the following example:
260 261 262 263 264 265 266 267 268 269

```php
<?php

namespace app\models;

use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;

270
class PostSearch extends Post
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
{
    public function rules()
    {
        // only fields in rules() are searchable
        return [
            [['id'], 'integer'],
            [['title', 'creation_date'], 'safe'],
        ];
    }

    public function scenarios()
    {
        // bypass scenarios() implementation in the parent class
        return Model::scenarios();
    }

    public function search($params)
    {
        $query = Post::find();

        $dataProvider = new ActiveDataProvider([
            'query' => $query,
        ]);

        // load the seach form data and validate
        if (!($this->load($params) && $this->validate())) {
            return $dataProvider;
        }

        // adjust the query by adding the filters
        $query->andFilterWhere(['id' => $this->id]);
302
        $query->andFilterWhere(['like', 'title', $this->title])
303 304 305 306 307 308 309 310
              ->andFilterWhere(['like', 'creation_date', $this->creation_date]);

        return $dataProvider;
    }
}

```

Carsten Brandt committed
311 312 313 314
You can use this function in the controller to get the dataProvider for the GridView:

```php
$searchModel = new PostSearch();
Mark committed
315
$dataProvider = $searchModel->search(Yii::$app->request->get());
Carsten Brandt committed
316 317

return $this->render('myview', [
318 319
    'dataProvider' => $dataProvider,
    'searchModel' => $searchModel,
Carsten Brandt committed
320 321 322 323 324 325 326 327
]);
```

And in the view you then assign the `$dataProvider` and `$searchModel` to the GridView:

```php
echo GridView::widget([
    'dataProvider' => $dataProvider,
328
    'filterModel' => $searchModel,
Carsten Brandt committed
329 330 331 332
]);
```


333
### Working with model relations
334

335
When displaying active records in a GridView you might encounter the case where you display values of related
336
columns such as the post author's name instead of just his `id`.
Carsten Brandt committed
337 338 339
You do this by defining the attribute name in columns as `author.name` when the `Post` model
has a relation named `author` and the author model has an attribute `name`.
The GridView will then display the name of the author but sorting and filtering are not enabled by default.
340
You have to adjust the `PostSearch` model that has been introduced in the last section to add this functionality.
Carsten Brandt committed
341

342
To enable sorting on a related column you have to join the related table and add the sorting rule
Carsten Brandt committed
343
to the Sort component of the data provider:
344 345 346 347 348 349 350 351 352

```php
$query = Post::find();
$dataProvider = new ActiveDataProvider([
    'query' => $query,
]);

// join with relation `author` that is a relation to the table `users`
// and set the table alias to be `author`
353
$query->joinWith(['author' => function($query) { $query->from(['author' => 'users']); }]);
354 355 356 357 358 359 360 361 362 363
// enable sorting for the related column
$dataProvider->sort->attributes['author.name'] = [
    'asc' => ['author.name' => SORT_ASC],
    'desc' => ['author.name' => SORT_DESC],
];

// ...
```

Filtering also needs the joinWith call as above. You also need to define the searchable column in attributes and rules like this:
Carsten Brandt committed
364

365 366 367
```php
public function attributes()
{
Carsten Brandt committed
368 369
    // add related fields to searchable attributes
    return array_merge(parent::attributes(), ['author.name']);
370 371 372 373 374 375 376 377 378 379
}

public function rules()
{
    return [
        [['id'], 'integer'],
        [['title', 'creation_date', 'author.name'], 'safe'],
    ];
}
```
Carsten Brandt committed
380

381 382 383 384 385 386
In `search()` you then just add another filter condition with:

```php
$query->andFilterWhere(['LIKE', 'author.name', $this->getAttribute('author.name')]);
```

387 388
> Info: In the above we use the same string for the relation name and the table alias; however, when your alias and relation name
> differ, you have to pay attention to where you use the alias and where you use the relation name.
389
> A simple rule for this is to use the alias in every place that is used to build the database query and the
390
> relation name in all other definitions such as `attributes()` and `rules()` etc.
391
>
392
> For example, if you use the alias `au` for the author relation table, the joinWith statement looks like the following:
393 394 395 396 397 398 399 400 401 402 403 404
>
> ```php
> $query->joinWith(['author' => function($query) { $query->from(['au' => 'users']); }]);
> ```
> It is also possible to just call `$query->joinWith(['author']);` when the alias is defined in the relation definition.
>
> The alias has to be used in the filter condition but the attribute name stays the same:
>
> ```php
> $query->andFilterWhere(['LIKE', 'au.name', $this->getAttribute('author.name')]);
> ```
>
405
> The same is true for the sorting definition:
406
>
407 408 409 410 411 412
> ```php
> $dataProvider->sort->attributes['author.name'] = [
>      'asc' => ['au.name' => SORT_ASC],
>      'desc' => ['au.name' => SORT_DESC],
> ];
> ```
413
>
414
> Also, when specifying the [[yii\data\Sort::defaultOrder|defaultOrder]] for sorting, you need to use the relation name
415 416 417 418 419
> instead of the alias:
>
> ```php
> $dataProvider->sort->defaultOrder = ['author.name' => SORT_ASC];
> ```
420 421

> Info: For more information on `joinWith` and the queries performed in the background, check the
422
> [active record docs on eager and lazy loading](db-active-record.md#lazy-and-eager-loading).
Qiang Xue committed
423

Mark committed
424 425
#### Using sql views for filtering, sorting and displaying data

426 427
There is also another approach that can be faster and more useful - sql views. For example, if we need to show the gridview 
with users and their profiles, we can do so in this way:
428 429 430 431

```php
CREATE OR REPLACE VIEW vw_user_info AS
    SELECT user.*, user_profile.lastname, user_profile.firstname
Mark committed
432
    FROM user, user_profile
433 434 435
    WHERE user.id = user_profile.user_id
```

436
Then you need to create the ActiveRecord that will be representing this view:
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459

```php

namespace app\models\views\grid;

use yii\db\ActiveRecord;

class UserView extends ActiveRecord
{

    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'vw_user_info';
    }

    public static function primaryKey()
    {
        return ['id'];
    }

Mark committed
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            // define here your rules
        ];
    }

    /**
     * @inheritdoc
     */
    public static function attributeLabels()
    {
        return [
            // define here your attribute labels
        ];
    }


481 482 483
}
```

484
After that you can use this UserView active record with search models, without additional specification of sorting and filtering attributes.
485 486
All attributes will be working out of the box. Note that this approach has several pros and cons:

487 488 489 490
- you don't need to specify different sorting and filtering conditions. Everything works out of the box;
- it can be much faster because of the data size, count of sql queries performed (for each relation you will need an additional query);
- since this is just a simple mapping UI on the sql view it lacks some domain logic that is in your entities, so if you have some methods like `isActive`,
`isDeleted` or others that will influence the UI, you will need to duplicate them in this class too.
491 492


493 494 495
### Multiple GridViews on one page

You can use more than one GridView on a single page but some additional configuration is needed so that
496
they do not interfere with each other.
497
When using multiple instances of GridView you have to configure different parameter names for
498
the generated sort and pagination links so that each GridView has its own individual sorting and pagination.
499
You do so by setting the [[yii\data\Sort::sortParam|sortParam]] and [[yii\data\Pagination::pageParam|pageParam]]
500 501
of the dataProvider's [[yii\data\BaseDataProvider::$sort|sort]] and [[yii\data\BaseDataProvider::$pagination|pagination]]
instances.
502

503
Assume we want to list the `Post` and `User` models for which we have already prepared two data providers
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
in `$userProvider` and `$postProvider`:

```php
use yii\grid\GridView;

$userProvider->pagination->pageParam = 'user-page';
$userProvider->sort->sortParam = 'user-sort';

$postProvider->pagination->pageParam = 'post-page';
$postProvider->sort->sortParam = 'post-sort';

echo '<h1>Users</h1>';
echo GridView::widget([
    'dataProvider' => $userProvider,
]);

echo '<h1>Posts</h1>';
echo GridView::widget([
    'dataProvider' => $postProvider,
]);
```

### Using GridView with Pjax

528
TBD