List Constructor

The purpose of creating and using a list constructor is to minimize code duplication, speed up development, and simplify the creation of lists and entity forms for modules or add-ons.

The list consists of the following elements:

  • tabs
  • list filters
  • list columns
  • a list of records in tabular form
  • pagination
  • an actions column with list items: edit, delete record, and others

Let's look at a simple example of creating a list. We will consider a simplified entity called publications (blog posts) presented in the database as follows:

CREATE TABLE `bff_posts` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(200) NOT NULL DEFAULT '', -- title
  `enabled` tinyint(1) unsigned NOT NULL DEFAULT '1', -- whether the publication is enabled
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, -- creation date
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

To create a list, you can follow several logical steps:

  1. Create a list object $list and pass the controller object $this to it.
$list = \tplAdmin::blockList($this);
  1. Set list columns using $list->column(Column ID, Title, Additional parameters).
$list->column('id', 'ID')
     ->column('title', 'Title')
     ->column('created', 'Creation Date');
  1. Add a handler for the record toggle event onToggle().
    This is necessary to indicate whether the record is published directly from the list.
$list->onToggle(function($id, $type){
    # requires a necessary method in the model 
    # that changes the value of the `enabled` field for the record by its $id
    $this->model->postToggle($id, $type);
});

The state is determined by the enabled field, which must be present in the record data obtained from the model when rendering the list (explained in step 6).

In the record toggle event handler $list->onToggle(callback), we pass a callback function as a parameter. The parameters of the callback function are:

  • id the identifier of the record
  • type the name of the field that toggles the record state (in our case, it is 'enabled' by default): published 1 / unpublished 0
  1. Add event handlers for record deletion, to enable deleting a record directly from the list.
$list->onDelete(function($id) {
    # delete the record by calling the corresponding model method
    $res = $this->model->postDelete($id);
    if ( ! $res) {
        $this->errors->impossible();
        return;
    }
});

Similarly, pass a callback function to the record deletion event handler, which is responsible for deleting the record. A second callback function can be passed as a parameter, which performs the logic before deleting the record.

  1. Set the parameters for pagination in the list. The total(count) method allows you to use default pagination with a specified number of records per page (20 items). To do this, you just need to inform the list of the total number of records in the database.
$count = $this->model->postsListingCount(); // get this count from the model
$list->total($count);

You can set the desired number of records per pagination page as follows:

$list->perPage(10);
  1. Retrieve the entries and add them to the list.
    In our example, the data for each entry should contain the fields: id, title, created, enabled
    If the field in the database that stores the publication status has a different name, for example publicated, then when forming the selection, use the alias 'publicated as enabled'.
$rows = $this->model->postsListing($filter); // get the data from the model
$list->rows($rows);
  1. Use the view method to display the list, which will result in the final HTML page of the list.
    The controller method should return this result to the application.
return $list->view();

As an example of usage, all logical steps are combined in the admin controller method responsible for creating the list:

/** 
 * Example method of the admin controller of the module/add-on
 * generating a list of publications
 * @return string HTML
 */
public function listing()
{
    # create list object
    $list = \tplAdmin::blockList($this);

    # set tabs for the list
    # for the possibility of filtering by publication status
    $list->tabs()
         ->add(0, 'All')
         ->add(1, 'Published')
         ->add(2, 'Unpublished');

    # set list columns
    $list
        ->column('id', 'ID', 80)
        ->column('title', 'Title', false, [
            'align' => $list::COLUMN_ALIGN_LEFT, # align to the left
        ])
        ->column('created', 'Creation Date', 130)
    ;

    # add toggle state action
    $list->onToggle(function($id, $type){
        $this->model->postToggle($id, $type);
    });

    # add delete action
    $list->onDelete(function($id) {
        if ( ! $id) {
            $this->errors->impossible();
            return;
        }
        # get data about the entry by $id
        $data = $this->model->postData($id);
        if (empty($data)) {
            $this->errors->impossible();
            return;
        }
        # delete entry
        $res = $this->model->postDelete($id);
        if ( ! $res) {
            $this->errors->impossible();
            return;
        }
    });

    # generate selection filter and sort order by tab
    $orderBy = 'id';
    $filter = [];
    switch ($list->tab()) {
        case 0: # All
            # Default sorting
            break;
        case 1: # Published
            $filter['enabled'] = 1;
            break;
        case 2: # Unpublished
            $filter['enabled'] = 0;
            break;
    }

    # pagination (default 20 records per page)
    $list->perPage(10); # set the number of records per page to 10
    $list->perPageRange([10 => '10', -1 => 'All']); # generate available options for choosing the number
    $list->total($this->model->postsListingCount($filter)); # request the total number from the model

    # get data about the entries
    $rows = $this->model->postsListing($filter, $orderBy);
    $list->rows($rows);
    
    # render the list and return it to the application
    return $list->view();
}

Creating a list in a separate template

Usually, the list creation is extracted into a separate template because there can be more tabs and columns, and filters can also be added to the list.
In this case, when creating a list object, the second parameter is used:

$list = \tplAdmin::blockList($objectController, 'listing');

Where 'tpl/listing.php' is the name of the php template of the module/add-on controller where the initialization of all list elements is performed. Inside the template, the list object will be available as a local variable $list, and the controller object as $this.

<?php
/**
 * @var $list \bff\tpl\admin\BlockList
 */

$list->column('id', 'ID', 80)
     ->column('title', 'Title');

// ...

List and Forms

To handle adding and editing records events, use the Forms component. For more information on how to apply forms in lists, read the article.

Rotation

The rotation feature (moving records up/down by dragging) is handled by the rotation event handler onRotate().

Usually, records sorting is performed in a specific list tab. To perform this sorting, you need to:

  1. Have a field in the database that determines the position of the record in the overall list. Usually, this is a numeric field in the database:
`num` int(11) unsigned DEFAULT '0',  
  1. When setting tabs, specify an additional rotation attribute in the tab settings like this:
# set tabs for the list
$list->tabs()
     ->add(0, 'All')
     ->add(1, 'Favorites', true, ['rotate' => true]); # tab with rotation enabled
  1. Add the rotation event handler $list->onRotate(callback), which takes a callback responsible for rotating the records:
$list->onRotate(function($tab_id){
    $this->model->db->rotateTablednd(`bff_posts`, '', 'id', 'num');
});
  1. You can perform the sorting of records using the standard rotateTablednd() method provided for this purpose. The parameters of this method are as follows: the table name - bff_posts, additional SQL query parameters - '', the name of the id field of the record - id, the name of the field used for sorting - num.
  2. Set the sorting parameters when outputting fields for the necessary tab:
switch ($list->tab()) {
    case 0: # All
        # Default sorting
        break;
    case 1: # Favorites
        $orderBy = 'num'; # sorting by the num field
        break;
}
  1. If you need to perform rotation within a specific selection, you need to specify additional query parameters for the rotateTablednd() function as SQL conditions:
$this->model->db->rotateTablednd(`bff_posts`, 'AND status = 1', 'id', 'num');

In this case, it is also important to consider that the num field in the table must be assigned when creating a new record taking into account this selection condition.

Favorite Records

The onToggle() method can take 'fav' as the second parameter. In this case, the toggle will be displayed as favorite.
The field in the database should also be named 'fav'.

$list->onToggle(function($id, $type){
   # call the model method to change the value of the `fav` field of the record with the given $id
   $this->model->postToggle($id, $type);
}, 'fav');

Dynamic Properties

In lists, you can include the dynamic properties component.
Include it using the onDynprops(dynpropsObject) method, where the parameter is the object of the dynamic properties component. The button to manage the dynamic properties of a record appears in the "Action" column.