Содержание
Control Panel Tables
InstantCMS 2 provides an automatic output of table entries (lists) in the component's control panel.
The easiest way to consider the work of automatic tables is to consider them with the help of an example.
Let the «Guestbook» (guestbook) component, which allows to post messages on the site, be an illustrative example. Each message has three fields: author's name (author), publication date (date_pub) and message text (content). We want to output all messages in one table in the component's control panel.
Suppose, we have already created a control panel's controller (backend.php) and the index action is defined in it:
class backendGuestbook extends cmsBackend { public function actionIndex(){ } }
The messages are stored in the guestbook_messages table in the database. The table has fields: id, author, date_pub, content.
The component's Model has the getMessages() method returning a list of messages and the getMessagesCount() method returning message count:
class modelGuestbook extends cmsModel { public function getMessages(){ return $this->get('guestbook_messages'); } public function getMessagesCount(){ return $this->getCount('guestbook_messages'); } }
Now we want the index action of the component's control panel to get all messages from the database and output them in the form of a table.
Creating Table Description
The system should know what columns it will contain to generate a table. For this reason a description file is created. Such files are stored in the /system/controllers/{component}/backend/grids folder. Each file is called grid_{table_title}.php. Inside the file, the grid_{table_title}() function returning an array with three fields is defined :
options | Table options |
columns | Table column (field) description |
actions | Each row's options (edit, delete, etc.) |
In our example, the table can be called messages and be located in the /system/controllers/guestbook/backend/grids/grid_messages.php file. The following function is declared inside:
function grid_messages($controller){ $options = array(); $columns = array(); $actions = array(); return array( 'options' => $options, 'columns' => $columns, 'actions' => $actions ); }
The incoming $controller parameter contains a link to the controller's object (backendGuestbook).
Table options
The $options array that is returned above should contain a list of table options. Available options:
Title | Description | Possible values |
---|---|---|
is_sortable | You can sort the table by columns | true or false |
is_filter | You can filter the table by certain columns | true or false |
is_pagination | Paginated table view | true or false |
is_draggable | Use your mouse to drag and drop table rows | true or false |
order_by | A field by which table rows are sorted by default | field title |
order_to | Default sorting order | asc or desc |
show_id | Show column with the id field in the table | true or false |
In our example, we want to output a sortable paginated table with filters by fields. By default, messages are sorted by date, ascendingly. Let's fill the $options array:
$options = array( 'is_sortable' => true, // sort by columns 'is_filter' => true, // filter by certain columns 'is_pagination' => true, // paginated view 'is_draggable' => false, // disable drag and drop 'order_by' => 'date_pub',// sort by publication date 'order_to' => 'desc', // descending (new first) 'show_id' => true // show the id column );
Table fields
In the table description, the $columns array will contain the description of each column (field). Each $columns element corresponds to one field, while the element key corresponds to the field title in the incoming data (taken from a model).
For example, let's add a column with the id field:
$columns = array( 'id' => array( 'title' => 'id', // Column name 'width' => 30, // Column width, in pixels 'filter' => 'exact' // Filter by column - exact match search ) );
Each field may have several options:
Title | Description |
---|---|
title | Column name in a table |
width | Column width, in pixels and percent (in this case, it is specified as a string, for example, «40%») |
href | Field URL. Allows to convert fields into URLs. For example, if you need a field value to refer to the relevant entry's customization form. See more below |
href_handler | For InstantCMS 2.7.0 and newer. function($row){} anonymous function. Gets the entire entry (row) as an array. Should return true or false. If it returns true - the field will become a link (if the href option is secified). If it returns false, the field will not become a link even if the href function is specified. |
flag | If true is specified, it allows to output the field value in the form of a check. If the field contains one or is not empty, a green check will be displayed, if 0 or empty – grey check |
flag_on | Allows to enter a precise value with which the field is considered to be filled. In this case, the green check will be displayed only if the field value coinsides with the specified one |
flag_toggle | Allows to enter a URL to send a message to and notify about clicks on a checkbox in the field. This can be used, for example, to quickly switch the field value at one click. A processor at the specified URL should return a JSON-object with two fields: error – Error (true or false), is_on – Flag's new state (true or false). You can use a Universal URL |
filter | Enables the column filter. It is the filter type that is passed as a value – exact (exact match) or like (partial match). If the filter type is specified, the column will contain a text field to enter the filter condition. |
handler | An anonymous function with two incoming parameters: function($field, $row){ }, where $field is the current field's value, $row – entire row. The function should return the processed $field value. It is used to format the values, which are output in the table (for example, to add a currency sign to a price in numeric fields) |
editable | Enables inline customization of the field. The option contains an array that may contain two cells: table (mandatory) and save_action (optional). The table cell will contain the title of the table, into which this field's changes will be entered; an action that will get data for saving is specified in the save_action cell. If the save_action cell is not specified, a system action is used, it is enough in most cases. Please note that the table table should contain an increment id field with a unique entry identifier. |
As for our guestbook example, the array of columns will look as follows:
$columns = array( 'id' => array( 'title' => 'id', 'width' => 30, 'filter' => 'exact' ), 'author' => array( 'title' => LANG_AUTHOR, 'filter' => 'like' ), 'date_pub' => array( 'title' => LANG_DATE, 'filter' => 'like', 'handler' => function ($field, $row){ return date('d.m.Y', strtotime($field)); } ), 'content' => array( 'title' => LANG_MESSAGE, 'filter' => 'like', 'handler' => function ($field, $row){ return mb_strlen($field) > 100 ? mb_substr($field, 0, 100) : $field; } ) );
We additionally process the date_pub and content fields with the help of the handler option. In the first case, we bring data to a proper format, in the second, we truncate messages that are more than 100 symbols long. We enable a strict filter for the id field, for the rest – filters by partial match.
URLs from table fields
When outputting fields in a table, you can turn any field into an active link. For this the href option is used in the field (column) description. The link's URL can be this option's value. You can substitute any current table entry's (row's) field values inside the URL. This is done with the help of the {field_title} expression.
For example, each guestbook message is available at http://site/guestbook/message/123, where 123 is a message ID in the database. We want each message text of our message table in the control panel be a link to the message page. Then we can add the href option for the content column:
'content' => array( 'title' => LANG_MESSAGE, 'filter' => 'like', 'handler' => function ($field, $row){ return mb_strlen($field) > 100 ? mb_substr($field, 0, 100) : $field; }, 'href' => href_to('guestbook', 'message', '{id}') )
To generate a URL, we use the href_to() function returning the URL of the required action with parameters. We specify {id} as a parameter, in other words, the value of a corresponding field for this row.
Table actions
The icons of actions applicable to each row (such as editing, deleting, etc.) are usually located in the last table column:
The $actions array in the table description contains a list of available actions for rows. Each action is represented by a nested array that may have the following fields:
title | An action's caption appears when pointing a mouse cursor over an icon |
class | a class, in which an action icon is described. For more information see below |
href | A link (URL) to an action performing this action |
confirm | A confirmation text that will appear at an attempt to perform an action. For example, «Are you sure?». You can substitute the fields with the help of the {field_title} expression in the text. |
handler | function($row){} anonymous function. Gets the entire entry (row) as an array. Should return true or false. If it returns false, the action will not be displayed for this row. It is used, for example, to forbid the deletion of certain rows. |
In our guestbook example, each message can have 3 actions – «view on site», «edit», «delete»:
$actions = array( array( 'title' => LANG_VIEW, 'class' => 'view', 'href' => href_to('guestbook', 'message', '{id}') ), array( 'title' => LANG_EDIT, 'class' => 'edit', 'href' => href_to($controller->root_url, 'edit', '{id}') ), array( 'title' => LANG_DELETE, 'class' => 'delete', 'href' => href_to($controller->root_url, 'delete', '{id}'), 'confirm' => LANG_GUESTBOOK_MESSAGE_DELETE_CONFIRM, ), );
The first action refers to the /guestbook/message frontend action, while the next too – to control panel actions. In this case, the $controller→root_url property is inserted at the beginning of a URL replacing the controller title that contains the root path for the component's backend-controller in the control panel.
A confirmation that is specified by a constant in the component's Language File is used for the «Delete» action:
define('LANG_GUESTBOOK_MESSAGE_DELETE_CONFIRM', 'Delete message №{id}?');
In the message, the {id} expression will be replaced with the ID of a deleted row.
Action icons
CSS-class is used to output an action icon. Standard classes:
Class → | Icon | Class → | Icon |
---|---|---|---|
view | accept | ||
edit | play | ||
config | rss | ||
labels | user | ||
fields | unbind | ||
filter | delete | ||
permissions | relations |
You can define your own my_class class in the /templates/default/controllers/{component}/backend/styles.css file:
.datagrid .actions a.my_class { background-image: url("../../images/icons/my_icon.png"); }
In this example, the /templates/default/images/icons/my_icon.png icon will be used.
Table output
Once the file with table structure description is ready, you need to output the table and fill it with data. This process has several stages:
- Table body is output in the template of a proper action;
- An AJAX-request is sent to get data;
- Received data is added to the table;
- When editing a page, sorting or table filters, go back to s.2.
Table output in a template
To output a table in the template, upload its description first. This is done in the action code with the help of custom loadDataGrid($grid_name) method:
$grid = $this->loadDataGrid('my_grid');
To save the sorting in your table (if there is sorting), use the following code to load the table description when outputting it:
$grid = $this->loadDataGrid('my_grid', false, 'my_component.my_key');
where my_component is your component's title (for example, guestbook), my_key - some key explicitly identifying this table.
Then the received $grid array is passed to a template to be output with the help of the renderGrid($data_url, $grid) method:
<?php $this->renderGrid($this->href_to('action'), $grid); ?>
The first parameter of this method contains an action URL that will return data to output it in the table (rows). This data is returned in the JSON format by an AJAX-request. The second parameter passes the table description object received earlier in the action.
As a result, a table body is formed on the page, i.e. an empty table that has only column captions. In this form, the table is sent to a user. The data is loaded once the page is displayed to a user, asynchronously.
Let's go back to our guestbook example. Let's add an ability to load and output a table in a controller, control panel and template.
/system/controllers/guestbook/backend.php controller :
class backendGuestbook extends cmsBackend { public function actionIndex(){ // we upload the table description $grid = $this->loadDataGrid('messages'); // we link the tepmplate and pass description into it return cmsTemplate::getInstance()->render('backend/index', array( 'grid' => $grid )); } }
/templates/default/controllers/guestbook/backend/index.tpl.php template:
<h3>Message list</h3> <?php $this->renderGrid($this->href_to('messages_ajax'), $grid); ?>
Uploading data to a table
A separate action, which outputs nothing to a browser and returns a JSON-array with table rows, is usually used to load data.
Inside the action, the following issues takes place:
- Uploading table description;
- Linking component model;
- Receiving data about filters from a request;
- If there is a before saved filter, we get it or save the filter received above;
- Applying filters to a model;
- Receiving selected data from a model;
- Forming and outputting the result in the form of JSON.
In the example above, in the template, we have specified the messages_ajax action as data source when generating a table.
We add this action to a controller:
public function actionMessagesAjax(){ // we check if it is an AJAX-request if (!$this->request->isAjax()) { cmsCore::error404(); } // we upload the table description $grid = $this->loadDataGrid('messages'); // we get a custom model $model = cmsCore::getModel($this->name); // we specify the default number of rows on one table page $model->setPerPage(admin::perpage); // we prepare an array that will contain // filtering conditions (including page number and sorting) $filter = array(); // getting filter description from a request $filter_str = $this->request->get('filter', ''); // if there is nothing in $filter_str, we load an earlier saved filter, if there is one // or if there is data in $filter_str, we refresh the filter $filter_str = cmsUser::getUPSActual('my_component.my_key', $filter_str); if ($filter_str){ // if there are filters, // we parse the received row with conditions and fill the $filter array parse_str($filter_str, $filter); // we apply table filters to a model $model->applyGridFilter($grid, $filter); } // we get an overall number of rows, taking into account all filters $total = $model->getMessagesCount(); // we get the number of rows on a page or the default value $perpage = isset($filter['perpage']) ? $filter['perpage'] : admin::perpage; // we calculate the number of pages $pages = ceil($total / $perpage); // from a model, we get entries (strings) meeting filtering conditions $messages = $model->getMessages(); // we link the template object $template = cmsTemplate::getInstance(); // we ask the template to render the JSON array with table rows $template->renderGridRowsJSON($grid, $messages, $total, $pages); $this->halt(); // finish }
Please note that analyzing the filters and applying them to a model is reasonable only if your table has pagination, sorting or filters (i.e. if any of the options is enabled – is_sortable, is_filter or is_pagination).
We have considered the simplest case when all information is stored in one DB table.
Of course, in real practice, you may need to output data from several linked tables. This is what you need to do following the method described in the «Model» section. Moreover, you can create a table that outputs data not from the Database or another site's DB and so on. In the following string:
$template->renderGridRowsJSON($grid, $data, $total, $pages);
the table structure in the $grid
parameter must correspond to the $data
array structure. Everything will be correct then.
Back to Controllers