Table

The table tag describes a table, but it's important not to confuse a physical database table with what is described in XML, as an XML table can describe one table that consists of multiple different columns from different database tables. However, in any XML table, there is a main database table, and it is specified by the name attribute in the table tag.

Attributes

name

name is the table identifier. In most cases, it matches the physical name of the main table in the database, but sometimes you need the table name in XML and database to be different. In such cases, use the alias attribute to specify the physical table name in the database.

name="products"
alias="products_table"

groupBy

In this attribute, you should list the fields by which you want to group, separated by commas.

defaultOrderField

The field that sorts the list when the user selects no other field. defaultOrderDirection gives the direction, ASC or DESC.

A MySQL or PostgreSQL store always adds the primary key to the end of the sort, in the same direction. Rows with equal sort values then keep one place, so a row shows on one page only. A store with no sort field sorts by the primary key. MssqlProxy and CassandraProxy keep their own order, because CQL sorts only by a clustering column.

paging

paging is used to specify the pagination method for the list of records in the table. If set to ajax, rows should load dynamically when scrolling through the record list. If multiple DGS are displayed on one page, it's not recommended to use this for all DGS except the last one.

paging="ajax"

exceptionMode

If exceptionMode equals true, then errors will not be displayed through a dialog, but will throw a StoreException. If you need to set exceptionMode for all Stores, you need to pass the option storeExceptionMode = true to the core.

mode

If mode equals api, then XML will support RESTful.

onBeforeConnection

Using this attribute, you can influence and change connection parameters. It helps us override the connection in DGS.

<table  charset="UTF-8"
        name="games"
        primaryKey="id"
        defaultOrderField="id"
        defaultOrderDirection="DESC"
        onBeforeConnection="onFacebookConnection"
        plugin="Games"
        rowsForPage="50">
public function onFacebookConnection(IDataAccessObject &$connection, Store &$store)
{
    $connection = DataAccessObject::getInstance('Facebook', $this->core->db);

    return true;
}

For example, by implementing the IDataAccessObject interface in FacebookObject, you can override the logic for working with Facebook API or GraphQL

proxy

By default, SQL proxy is used for working with the database.

For API work, use proxy="json".

errorMessage

When a storage error occurs, you can specify a standard message to display to the user.

grideditor

When enabled, allows editing cell values in the table by double-clicking on them. grideditor="true"

DGS Grid Editor

When editing, all events from the Store::ACTION_EDIT action are available

join

You can specify expressions for join:

<table charset="UTF-8"
            name="chats_rooms"
            primaryKey="id"
            defaultOrderField="id"
            defaultOrderDirection="DESC"
            rowsForPage="50"
            refreshDataTimeout="30"
            join="LEFT JOIN chats_rooms2users ON (chats_rooms2users.id_chat_room = chats_rooms.id)"
            groupBy="chats_rooms.id">
...
</table>

additionalWhere

You can write additional conditions for data selection. However, it's better not to use this option.

customFrom

If the real table name differs from the DGS name, it should be specified in the customFrom attribute.

userTimezone

If you need to display dates in the user's timezone, you can specify it in the userTimezone attribute.

sourceTimezone

If you need to display dates in the source timezone, you can specify it in the sourceTimezone attribute.

Describing the Model Using an Array

Creating storage doesn't necessarily require using XML format. Example of creation using an associative array:

$options = [];

$options['model'] = [
    'table' => [
        'name'       => 'settings',
        'primaryKey' => 'id'
    ],

    'fields' => [
         [
            'type' => 'image',
            'name' => 'value',
            'caption' => 'Value',
            'fileName' => '__ID__.__EXT__',
            'thumb' => '120x80',
            'isnull' => 'true',
            //'uploadDirPath' => ,
            //'httpPath' =>
        ],
        [
            'type' => 'datetime',
            'name' => 'cdate',
            'caption' => __('Date'),
            'format' => Utils::getShortDateFormat(),
            'html5' => true,
            'onlyList' => true,
            'width' => '15%'
        ]
    ],

    'actions' => [
        Store::ACTION_LIST => [
            'type' => Store::ACTION_LIST,
            'caption' => __('List')
        ],
        Store::ACTION_EDIT => [
            'type' => Store::ACTION_EDIT,
            'caption' => __('Edit')
        ]
    ]
];

$store = Core::getInstance()->createStoreInstance('settings', $options);

To pass section attributes, use the @attributes key, for example:

'actions' => [
      '@attributes' => [
          'mode' => 'buttons'
      ],
      Store::ACTION_EDIT => [
          'type' => Store::ACTION_EDIT,
          'caption' => 'Edit'
      ]
 ]

Describing the Model Using a PHP Class

For storages that benefit from IDE navigation, static analysis, and reusable composition, declare the schema with a PHP class that extends ClassSchema and configures the store via fluent builders.

namespace Plugins\SchoolUsers\Store;

use Festi\Store\Schema\ClassSchema;
use Festi\Store\Schema\Builder\ITableBuilder;
use Festi\Store\Schema\Builder\IFieldsBuilder;
use Festi\Store\Schema\Builder\IActionsBuilder;
use Store;

class UsersSchema extends ClassSchema
{
    protected function table(ITableBuilder $table): ITableBuilder
    {
        return $table->name('users')
            ->primaryKey('id')
            ->rowsForPage(50);
    }

    protected function fields(IFieldsBuilder $fields): IFieldsBuilder
    {
        $fields->add('id')
            ->type(ReadonlyField::class)
            ->caption(__('ID'))
            ->width('5%');
        $fields->add('email')
            ->type(TextField::class)
            ->caption(__('Email'))
            ->required();

        return $fields;
    }

    protected function actions(IActionsBuilder $actions): IActionsBuilder
    {
        $actions->add(Store::ACTION_LIST)->caption(__('Customers'));
        $actions->add(Store::ACTION_EDIT)->caption(__('Edit'));

        return $actions;
    }
}

Wire the schema in — the schema-class shorthand is the recommended form. The first argument may itself be the FQN of a ClassSchema subclass; the framework routes it to the model option and derives the store ident from the class short name:

$store = Core::getInstance()->createStoreInstance(UsersSchema::class);

When you need a custom store ident, pass the schema explicitly via Store::OPTION_MODEL:

$store = Core::getInstance()->createStoreInstance(
    'users',
    [Store::OPTION_MODEL => UsersSchema::class]
);

For Store subclasses with custom constructors, declare the schema on the store itself by implementing Festi\Store\Schema\IStoreSchemaProvider — the resolver then picks up the schema with no model option needed. See DGS Class Schema → Provider Interface.

The full reference — every builder, every section method, the resolver order, and a 1-to-1 mapping table to XML/Array forms — lives in DGS Class Schema.

filter

Controls the placement of search fields in the DGS list:

filter="top"

Adds a button at the top to open the advanced search window

Values:

  • top - Advanced search form will open in a modal window
  • right - Advanced search form will slide out from the right
  • default or not specified - Advanced search button won't be displayed, but quick search will be shown at the top of the table
  • dropdown - Advanced search form will open in a dropdown under the Filters button of the toolbar
  • ajax - Same as default but search results will load without page reload. Kept for old tblDefs: a new grid sets ajaxList, which works with every value above

DGS Advanced Search (top)

DGS Advanced Search (right)

auditMode

Controls saving data change history in DGS.

auditMode="true"

When enabled, a __{tableName} table will be created for audit records.

CREATE TABLE `tableName` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `id_user` int(11) unsigned DEFAULT NULL,
  `caption` varchar(100) DEFAULT NULL,
  `description` longtext,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `__tableName` (
  # Audit columns
  `__id` int(11) unsigned NOT NULL,
  `__action` varchar(100) DEFAULT NULL,
  `__id_author` int(11) unsigned DEFAULT NULL,
  `__action_date` datetime,

  # All original columns
  `id` int(11) unsigned NOT NULL,
  `id_user` int(11) unsigned DEFAULT NULL,
  `caption` varchar(100) DEFAULT NULL,
  `description` longtext,

  PRIMARY KEY (`__id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

refreshDataTimeout

There are situations when workers process data in the background, and to see changes, we have to reload the page. For automatic data updates, refreshDataTimeout is responsible

refreshDataTimeout="30"

Specifies the time in seconds to update data in the DGS list.

fixedHeader

Fixing column names.

fixedHeader="true"

ajaxList

Applies filters, sorting and paging without reloading the page, with any filter mode.

ajaxList="true"

In a ClassSchema: $table->ajaxList();

filter="ajax" turns it on too, so old tblDefs keep their behaviour.

hideTotalItems

Leaves out the Total Items count under the grid rows, for a grid where the count means nothing to the user.

hideTotalItems="true"

In a ClassSchema: $table->hideTotalItems();