WoltLab Suite 6.2 Handbuch
Deutsch/PHP-Version: Die Website-Struktur, Navigation und Überschriften sind auf Deutsch vorbereitet. Code-Beispiele und technische Namen bleiben unverändert.

List Views

List views are a generic solution for the creation of listings that are ubiquitous in the software.

In contrast to grid views, list views do not specify a particular layout.

The developer must specify a custom template that takes care of the rendering of the entries.

A list view takes care of sorting, filtering and pagination, and ensure that a lot of boilerplating becomes obsolete.

The implementation essentially offers the following advantages:

1. A uniform appearance and usability for the user.

2. An easy way for developers to create their own list views.

3. An easy way for developers to extend existing list views using plugins.

Usage

AbstractListView

List views obtain their data from a database object list and display it using custom template.

Example:

<?php

namespace wcf\system\listView\user;

use wcf\data\DatabaseObjectList;
use wcf\system\listView\AbstractListView;
use wcf\system\WCF;

/**
 * @extends AbstractListView<Example, ExampleList>
 */
class ExampleListView extends AbstractListView
{
    #[\Override]
    protected function createObjectList(): DatabaseObjectList
    {
        return new ExampleList();
    }

    #[\Override]
    public function isAccessible(): bool
    {
        return true;
    }

    #[\Override]
    public function renderItems(): string
    {
        return WCF::getTPL()->render('wcf', 'exampleListItems', ['view' => $this]);
    }
}

Example exampleListItems.tpl:

{foreach from=$view->getItems() item='item'}
	<div class="listView__item" data-object-id="{$item->getObjectID()}">
		<h2>{$item->getTitle()}</h2>
	</div>
{/foreach}

AbstractListViewPage

A list view can be displayed on a page by inheriting from AbstractListViewPage.

Example:

<?php

namespace wcf\page;

/**
 * @extends AbstractListViewPage<ExampleListView>
 */
class ExampleListPage extends AbstractListViewPage
{
    #[\Override]
    protected function createListView(): ExampleListView
    {
        return new ExampleListView();
    }
}
{include file='header'}

<div class="section">
	{unsafe:$listView->render()}
</div>

{include file='footer'}

Sorting

The addAvailableSortFields method allows you to define columns that the user can use to sort the list.

The columns must exist in the linked database object list.

class ExampleListView extends AbstractListView
{
    public function __construct() {
        $this->addAvailableSortFields([
            new ListViewSortField('time', 'wcf.global.date'),
            new ListViewSortField('title', 'wcf.global.title'),
        ]);
    }
}

By default, sorting is based on the id (first parameter) of the specified sort field.

Optionally, you can specify the name of an alternative database column to be used for sorting instead:

new ListViewSortField('title', 'wcf.global.title', 'table_alias.columnName'),

The default sorting can be defined after the configuration of the sort fields has been defined:

class ExampleListView extends AbstractListView
{
    public function __construct()
    {
        $this->addAvailableSortFields([
            new ListViewSortField('time', 'wcf.global.date'),
            new ListViewSortField('title', 'wcf.global.title'),
        ]);
        
        $this->setDefaultSortField('title');
        $this->setDefaultSortOrder('ASC');
    }
}

Filtering

Filters can be defined for columns so that the user has the option to filter by the content of a column.

class ExampleListView extends AbstractListView
{
    public function __construct()
    {
        $this->addAvailableFilters([
            new TextFilter('title', 'wcf.global.title'),
        ]);
    }
}

BooleanFilter

BooleanFilter is a filter for columns that contain boolean values (1 or 0).

CategoryFilter

CategoryFilter is a filter for columns that contain category ids.

class ExampleListView extends AbstractListView
{
    public function __construct()
    {
        $this->addAvailableFilters([
            new CategoryFilter((new CategoryNodeTree('identifier'))->getIterator()), 'categoryID'),
        ]);
    }
}

DateFilter

DateFilter is a filter for columns that contain unix timestamps.

FloatFilter

FloatFilter is a filter for columns that contain float values.

FormOptionFilter

FormOptionFilter is a filter for columns that are based on IFormOption.

I18nTextFilter

I18nTextFilter is a filter for text columns that are using i18n phrases.

IntegerFilter

IntegerFilter is a filter for columns that contain integer values.

IpAddressFilter

IpAddressFilter is a filter for columns that contain IPv6 addresses, allowing the user to enter addresses in the IPv4 format too.

LabelFilter

LabelFilter allows to filter a list view by labels.

class ExampleListView extends AbstractListView
{
    public function __construct()
    {
        $objectTypeID = ObjectTypeCache::getInstance()->getObjectTypeIDByName(
            'com.woltlab.wcf.label.object',
            'example.identifier'
        );

        foreach (ExampleCategory::getAccessibleLabelGroups('canViewLabel') as $groupID => $categoryIDs) {
            $this->addAvailableFilters([
                new LabelFilter(
                    LabelHandler::getInstance()->getLabelGroup($groupID),
                    $objectTypeID,
                    'labelIDs' . $groupID
                )
            ]);
        }
    }
}

MultipleSelectFilter

MultipleSelectFilter allows a column to be filtered on the basis of a multi-select.

class ExampleListView extends AbstractListView
{
    public function __construct()
    {
        $this->addAvailableFilters([
            new MultipleSelectFilter([
                1 => 'value 1',
                0 => 'value 0',
            ], 'id', 'language.item'),
        ]);
    }
}

ObjectIdFilter

ObjectIdFilter is a filter for columns that contain object ids.

SelectFilter

SelectFilter allows a column to be filtered on the basis of a select dropdown.

class ExampleListView extends AbstractListView
{
    public function __construct()
    {
        $this->addAvailableFilters([
            new SelectFilter([
                1 => 'value 1',
                0 => 'value 0',
            ], 'id', 'language.item'),
        ]);
    }
}

TextFilter

TextFilter is a filter for text columns.

TimeFilter

TimeFilter is a filter for columns that contain unix timestamps.

In contrast to DateFilter, this filter also allows filtering by a specific time.

UserFilter

UserFilter is a filter for columns that contain user ids.

Customization

Number of Items

By default, list views use a pagination that shows 20 items per page. You can set a custom number of items per page:

class ExampleListView extends AbstractListView
{
    public function __construct()
    {
        $this->setItemsPerPage(50);
    }
}

There are some cases where only a list with a fixed number of items is required, for example, showcasing the 10 latests items.

class ExampleListView extends AbstractListView
{
    public function __construct()
    {
        $this->fixedNumberOfItems(10);
    }
}

CSS Class Names

Optionally, a CSS class can be set on the surrounding HTML element:

class ExampleListView extends AbstractListView
{
    public function __construct()
    {
        $this->setCssClassName('exampleList');
    }
}

Additional Parameters

A list view can be provided with additional parameters, e.g. to filter them by a specific category:

class ExampleListView extends AbstractListView
{
    public function __construct(public readonly int $categoryID)
    {
        parent::__construct();
    }

    #[\Override]
    protected function createObjectList(): DatabaseObjectList
    {
        $list = new ExampleList();
        $list->getConditionBuilder()->add('categoryID = ?', [$this->categoryID]);

        return $list;
    }

    #[\Override]
    public function getParameters(): array
    {
        return ['categoryID' => $this->categoryID];
    }
}
class ExampleListPage extends AbstractListViewPage
{
    public int $categoryID = 0;

    #[\Override]
    public function readParameters()
    {
        parent::readParameters();
        
        if (isset($_REQUEST['categoryID'])) {
            $this->categoryID = \intval($_REQUEST['categoryID']);
        }
    }

    #[\Override]
    protected function createListView(): AbstractListView
    {
        return new ExampleListView($this->categoryID);
    }

    #[\Override]
    protected function getBaseUrlParameters(): array
    {
        return [
            'categoryID' => $this->categoryID,
        ];
    }
}

Events

Existing list views can be modified using events.

Example of adding an additional sort field:

$eventHandler->register(
    \wcf\event\listView\user\ArticleListViewInitialized::class,
    static function (\wcf\event\listView\user\ArticleListViewInitialized $event) {
         $event->listView->addAvailableSortField(
            new ListViewSortField('example', 'wcf.global.example'),
        );
    }
);

Interactions

Interaction providers can be specified using the methods setInteractionProvider() and setBulkInteractionProvider() (for bulk interactions).

Example:

final class ExampleGridView extends AbstractListView
{
    public function __construct()
    {
        ...

        $this->setInteractionProvider(new ExampleInteractions());
        $this->setBulkInteractionProvider(new ExampleBulkInteractions());
    }
}

The following template code must be included in the template for rendering of the items so that the buttons for the interactions are displayed.

{if $view->hasBulkInteractions()}
    <label class="listView__selectItem__label jsTooltip" title="{lang}wcf.clipboard.item.mark{/lang}">
        <input type="checkbox" class="listView__selectItem" aria-label="{lang}wcf.clipboard.item.mark{/lang}">
    </label>
{/if}

{unsafe:$view->renderInteractionContextMenuButton($article)}

Mark as Read

If your objects can be marked as read, list views provide an abstract implementation for this.

The setMarkAsReadEndpoints(string $endpoint): void method allows you to configure the corresponding RPC endpoint.

final class ExampleGridView extends AbstractListView
{
    public function __construct()
    {
        ...

        $this->setMarkAsReadEndpoints('core/example/%s/mark-as-read');
    }
}

In the template code, the corresponding button can be created for each object using the renderMarkAsReadButton(DatabaseObject $object): string method.

{foreach from=$view->getItems() item='item'}
    ...
    {if $item->isNew()}
        {unsafe:$view->renderMarkAsReadButton($item)}
    {/if}
    ...
{/foreach}