|
| 1 | +# Grid Views |
| 2 | + |
| 3 | +Grid views are a generic solution for the creation of listings that are ubiquitous in the software. |
| 4 | +In addition to rendering, the grid view also take care of sorting, filtering and pagination, and ensure that a lot of boilerplating becomes obsolete. |
| 5 | + |
| 6 | +The implementation essentially offers the following advantages: |
| 7 | +1. A uniform appearance and usability for the user. |
| 8 | +2. An easy way for developers to create their own grid views. |
| 9 | +3. An easy way for developers to extend existing grid views using plugins. |
| 10 | + |
| 11 | +## Usage |
| 12 | + |
| 13 | +### AbstractGridView |
| 14 | + |
| 15 | +Grid views obtain their data from a database object list and display it using a defined column configuration. |
| 16 | + |
| 17 | +Example: |
| 18 | + |
| 19 | +```php |
| 20 | +<?php |
| 21 | + |
| 22 | +namespace wcf\system\gridView\admin; |
| 23 | + |
| 24 | +use wcf\data\DatabaseObjectList; |
| 25 | +use wcf\system\gridView\AbstractGridView; |
| 26 | +use wcf\system\gridView\GridViewColumn; |
| 27 | +use wcf\system\gridView\renderer\ObjectIdColumnRenderer; |
| 28 | + |
| 29 | +final class ExampleGridView extends AbstractGridView |
| 30 | +{ |
| 31 | + public function __construct() |
| 32 | + { |
| 33 | + $this->addColumns([ |
| 34 | + GridViewColumn::for('id') |
| 35 | + ->label('wcf.global.objectID') |
| 36 | + ->renderer(new ObjectIdColumnRenderer()) |
| 37 | + ->sortable(), |
| 38 | + GridViewColumn::for('title') |
| 39 | + ->label('wcf.global.title') |
| 40 | + ->sortable() |
| 41 | + ->titleColumn() |
| 42 | + ]); |
| 43 | + } |
| 44 | + |
| 45 | + #[\Override] |
| 46 | + public function isAccessible(): bool |
| 47 | + { |
| 48 | + return true; |
| 49 | + } |
| 50 | + |
| 51 | + #[\Override] |
| 52 | + protected function createObjectList(): DatabaseObjectList |
| 53 | + { |
| 54 | + return new ExampleDatabaseObjectList(); |
| 55 | + } |
| 56 | +} |
| 57 | +``` |
| 58 | + |
| 59 | +### AbstractGridViewPage |
| 60 | + |
| 61 | +A grid view can be displayed on a page by inheriting from `AbstractGridViewPage`. |
| 62 | + |
| 63 | +Example: |
| 64 | + |
| 65 | +```php |
| 66 | +<?php |
| 67 | + |
| 68 | +namespace wcf\acp\page; |
| 69 | + |
| 70 | +use wcf\page\AbstractGridViewPage; |
| 71 | + |
| 72 | +final class ExampleListPage extends AbstractGridViewPage |
| 73 | +{ |
| 74 | + #[\Override] |
| 75 | + protected function createGridView(): AbstractGridView |
| 76 | + { |
| 77 | + return new ExampleGridView(); |
| 78 | + } |
| 79 | +} |
| 80 | +``` |
| 81 | + |
| 82 | +```smarty |
| 83 | +{include file='header'} |
| 84 | +
|
| 85 | +<div class="section"> |
| 86 | + {unsafe:$gridView->render()} |
| 87 | +</div> |
| 88 | +
|
| 89 | +{include file='footer'} |
| 90 | +``` |
| 91 | + |
| 92 | +## Columns |
| 93 | + |
| 94 | +Columns can be created using the `GridViewColumn::for` method. This expects a unique string as a parameter, which is equivalent to the corresponding key in the data source. |
| 95 | + |
| 96 | +The `label` method can be used to give the column a human readable label. |
| 97 | + |
| 98 | +Example: |
| 99 | + |
| 100 | +```php |
| 101 | +final class FooGridView extends AbstractGridView |
| 102 | +{ |
| 103 | + public function __construct() |
| 104 | + { |
| 105 | + $this->addColumns([ |
| 106 | + GridViewColumn::for('id') |
| 107 | + ->label('wcf.global.objectID'), |
| 108 | + GridViewColumn::for('name') |
| 109 | + ->label('wcf.global.name'), |
| 110 | + ]); |
| 111 | + } |
| 112 | +} |
| 113 | +``` |
| 114 | + |
| 115 | +### Renderer |
| 116 | + |
| 117 | +Renderers can be applied to columns to format the output. A column can have multiple renderers. The renderers are applied in the order in which they were set. |
| 118 | + |
| 119 | +```php |
| 120 | +GridViewColumn::for('foo') |
| 121 | + ->renderer([ |
| 122 | + new FooColumnRenderer(), |
| 123 | + new BarColumnRenderer(), |
| 124 | + ]) |
| 125 | +``` |
| 126 | + |
| 127 | +#### CategoryColumnRenderer |
| 128 | + |
| 129 | +`CategoryColumnRenderer` can be set to columns that contain the ID of categories. This results in the name of the category being output. |
| 130 | + |
| 131 | +#### CurrencyColumnRenderer |
| 132 | + |
| 133 | +`CurrencyColumnRenderer` formats the content of a column as a currency. Expects the content of the column to be a decimal. |
| 134 | + |
| 135 | +Example: |
| 136 | + |
| 137 | +```php |
| 138 | +GridViewColumn::for('foo') |
| 139 | + ->renderer(new CurrencyColumnRenderer('EUR')) |
| 140 | +``` |
| 141 | + |
| 142 | +#### DefaultColumnRenderer |
| 143 | + |
| 144 | +The `DefaultColumnRenderer` is implicitly applied to all columns unless one ore more renderers have been explicitly set. |
| 145 | + |
| 146 | +#### EmailColumnRenderer |
| 147 | + |
| 148 | +`EmailColumnRenderer` formats the content of the column as an email address. |
| 149 | + |
| 150 | +#### FilesizeColumnRenderer |
| 151 | + |
| 152 | +`FilesizeColumnRenderer` formats the content of the column as a file size. |
| 153 | + |
| 154 | +#### IpAddressColumnRenderer |
| 155 | + |
| 156 | +`IpAddressColumnRenderer` outputs the value by attempting to interpret it as IPv4 if possible, otherwise shows the IPv6 address. |
| 157 | + |
| 158 | +#### LinkColumnRenderer |
| 159 | + |
| 160 | +`LinkColumnRenderer` allows the setting of a link to a column. |
| 161 | + |
| 162 | +Example: |
| 163 | + |
| 164 | +```php |
| 165 | +GridViewColumn::for('foo') |
| 166 | + ->renderer(new LinkColumnRenderer(FooEditForm::class)) |
| 167 | +``` |
| 168 | + |
| 169 | +#### NumberColumnRenderer |
| 170 | + |
| 171 | +`NumberColumnRenderer` formats the content of a column as a number using `StringUtil::formatNumeric()`. |
| 172 | + |
| 173 | +#### ObjectIdColumnRenderer |
| 174 | + |
| 175 | +`ObjectIdColumnRenderer` formats the content of a column as an object id. |
| 176 | + |
| 177 | +#### PhraseColumnRenderer |
| 178 | + |
| 179 | +`PhraseColumnRenderer` attempts to evaluate the value as a phrase and outputs it as plain text otherwise. |
| 180 | + |
| 181 | +#### TimeColumnRenderer |
| 182 | + |
| 183 | +`TimeColumnRenderer` renders a unix timestamp into a human readable format. |
| 184 | + |
| 185 | +#### TruncatedTextColumnRenderer |
| 186 | + |
| 187 | +`TruncatedTextColumnRenderer` truncates the content of a column to a length of 80 characters (default value). |
| 188 | + |
| 189 | +#### UserColumnRenderer |
| 190 | + |
| 191 | +`UserColumnRenderer` formats the content of a column as a user. |
| 192 | + |
| 193 | +#### UserLinkColumnRenderer |
| 194 | + |
| 195 | +`UserLinkColumnRenderer` is a combination of the `UserColumnRenderer` and the `LinkColumnRenderer`. |
| 196 | + |
| 197 | +Example: |
| 198 | + |
| 199 | +```php |
| 200 | +GridViewColumn::for('foo') |
| 201 | + ->renderer(new UserLinkColumnRenderer(FooEditForm::class)) |
| 202 | +``` |
| 203 | + |
| 204 | +### Custom Renderer |
| 205 | + |
| 206 | +If necessary, you can define your own renderers: |
| 207 | + |
| 208 | +```php |
| 209 | +GridViewColumn::for('id') |
| 210 | + ->renderer([ |
| 211 | + new class extends DefaultColumnRenderer { |
| 212 | + public function render(mixed $value, DatabaseObject $row): string |
| 213 | + { |
| 214 | + return 'foo: ' . $value; |
| 215 | + } |
| 216 | + }, |
| 217 | + ]), |
| 218 | +``` |
| 219 | + |
| 220 | +### Row Link |
| 221 | + |
| 222 | +A row link applies a link to every column in the grid. |
| 223 | + |
| 224 | +```php |
| 225 | +final class FooGridView extends AbstractGridView |
| 226 | +{ |
| 227 | + public function __construct() |
| 228 | + { |
| 229 | + $this->addRowLink(new GridViewRowLink(FooEditForm::class)); |
| 230 | + } |
| 231 | +} |
| 232 | +``` |
| 233 | + |
| 234 | +The constructor supports 3 optional parameters: |
| 235 | +1. `string $controllerClass`: The controller to which the link should refer. |
| 236 | +2. `array $parameters`: Additional parameters for the controller. |
| 237 | +3. `string $cssClass`: CSS class for the link. |
| 238 | + |
| 239 | +### Sorting |
| 240 | + |
| 241 | +Columns can be marked as sortable so that the user has the option of sorting according to the content of the column. |
| 242 | + |
| 243 | +```php |
| 244 | +GridViewColumn::for('foo') |
| 245 | + ->sortable() |
| 246 | +``` |
| 247 | + |
| 248 | +By default, sorting is based on the `id` of the column. |
| 249 | +Optionally, you can specify the name of an alternative database column to be used for sorting instead: |
| 250 | + |
| 251 | +```php |
| 252 | +GridViewColumn::for('foo') |
| 253 | + ->sortable(sortByDatabaseColumn: "table_alias.columnName") |
| 254 | +``` |
| 255 | + |
| 256 | +The default sorting can be defined after the column configuration has been defined: |
| 257 | + |
| 258 | +```php |
| 259 | +final class FooGridView extends AbstractGridView |
| 260 | +{ |
| 261 | + public function __construct() |
| 262 | + { |
| 263 | + GridViewColumn::for('title') |
| 264 | + ->sortable(); |
| 265 | + |
| 266 | + $this->setSortField('title'); |
| 267 | + $this->setSortOrder('ASC'); |
| 268 | + } |
| 269 | +} |
| 270 | +``` |
| 271 | + |
| 272 | +### Filtering |
| 273 | + |
| 274 | +Filters can be defined for columns so that the user has the option to filter by the content of a column. |
| 275 | + |
| 276 | +```php |
| 277 | +GridViewColumn::for('foo') |
| 278 | + ->filter(new FooFilter()) |
| 279 | +``` |
| 280 | + |
| 281 | +#### BooleanFilter |
| 282 | + |
| 283 | +`BooleanFilter` is a filter for columns that contain boolean values (`1` or `0`). |
| 284 | + |
| 285 | +#### CategoryFilter |
| 286 | + |
| 287 | +`CategoryFilter` is a filter for columns that contain category ids. |
| 288 | + |
| 289 | +```php |
| 290 | +GridViewColumn::for('categoryID') |
| 291 | + ->filter(new CategoryFilter((new CategoryNodeTree('identifier'))->getIterator())) |
| 292 | +``` |
| 293 | + |
| 294 | +#### I18nTextFilter |
| 295 | + |
| 296 | +`I18nTextFilter` is a filter for text columns that are using i18n phrases. |
| 297 | + |
| 298 | +#### IpAddressFilter |
| 299 | + |
| 300 | +`IpAddressFilter` is a filter for columns that contain IPv6 addresses, allowing the user to enter addresses in the IPv4 format too. |
| 301 | + |
| 302 | +#### NumericFilter |
| 303 | + |
| 304 | +`NumericFilter` is a filter for columns that contain numeric values. |
| 305 | + |
| 306 | +#### ObjectIdFilter |
| 307 | + |
| 308 | +`ObjectIdFilter` is a filter for columns that contain object ids. |
| 309 | + |
| 310 | +#### SelectFilter |
| 311 | + |
| 312 | +`SelectFilter` allows a column to be filtered on the basis of a select dropdown. |
| 313 | + |
| 314 | +```php |
| 315 | +GridViewColumn::for('foo') |
| 316 | + ->filter(new SelectFilter([ |
| 317 | + 1 => 'value 1', |
| 318 | + 0 => 'value 0', |
| 319 | + ])); |
| 320 | +``` |
| 321 | + |
| 322 | +#### TextFilter |
| 323 | + |
| 324 | +`TextFilter` is a filter for text columns. |
| 325 | + |
| 326 | +#### TimeFilter |
| 327 | + |
| 328 | +`TimeFilter` is a filter for columns that contain unix timestamps. |
| 329 | + |
| 330 | +#### UserFilter |
| 331 | + |
| 332 | +`UserFilter` is a filter for columns that contain user ids. |
| 333 | + |
| 334 | +### Events |
| 335 | + |
| 336 | +Existing grid views can be modified using events. |
| 337 | + |
| 338 | +Example of adding an additional column: |
| 339 | + |
| 340 | +```php |
| 341 | +$eventHandler->register( |
| 342 | + \wcf\event\gridView\UserRankGridViewInitialized::class, |
| 343 | + static function (\wcf\event\gridView\UserRankGridViewInitialized $event) { |
| 344 | + $event->gridView->addColumnBefore( |
| 345 | + GridViewColumn::for('hideTitle') |
| 346 | + ->label('hideTitle') |
| 347 | + ->renderer(new NumberColumnRenderer()) |
| 348 | + ->sortable(), |
| 349 | + 'requiredPoints' |
| 350 | + ); |
| 351 | + } |
| 352 | +); |
| 353 | +``` |
0 commit comments