Models - tables

This tutorial shows how to use Artaengine class named Table to create flexible customizable edicode/non-edicode/sortable... HTML tables from model data.

Class Table iterates over model data and renders rows.

Creating and config up object

Creating a table from model data is as simple as shown in the example below. This creates a table from all model data properties:

$user = new User();
$user->query();
$table = new Table($user);
$html = $table->renderTable();

To limit table columns to few model data properties:

/* # will show row index */
$table = new Table($user, '#', 'name', 'UserGroup.name');

$table = new Table(
    $user,
    array(
        'id',
        'fullName',         // method name
        'UserGroup',        // UserGroup.{PRESENTOR properti/ies}
        'City.Country.name',
    )
);

/* selective model properties to be shown in a table
   elegant way - property list is defined inside model */
$table = new Table($user, User::listCols());

Putting # inside the column list list will create a column of row-numbers.

Column list items are not limited to model data properties and #, they can have model method names (which return a scalar value) or any stage of objects of object model data properties. For example Group.name will show values of name property of model Group which is an object property of the base model object.

Columns can be deeper traverse paths of objects for example Client.City.County.name and Client.City.County are acceptable. If the last node is an object and not a scalar data property, value of the presenter data property/properties of the object will be shown.

Method set can be used to set model object and column list instead of Table constructor. Or when creating tables from different models with one Table object:

$user = new User();
$user->query();
$client = new Client();
$client->query();

$table = new Table($user);
$userTable = $table->renderTable();

$table->set($user, $client::listCols());
$clientTable = $table->renderTable();

Table header labels comes from:

  • Model property definition. For example: 'label' => 'First name',
  • Model method label. If property or method exists in return array {property-name: label,}
  • If property is model object or model object list (at any traverse stage) then label will be inherited from it's model presenter property label.
  • Model property name

Class Table translates labels using gettext (if application is multi lingual);

class User extends Model {
    /* ASSUME PROPERTIES AND CONSTRUCTOR ARE HERE... */

    /* labels method */
    public function labels() {
        return array(
            /* the only way to define a label for a method */
            'fullName' => _('Name'),
            /* to override default UserGroup.UserPermission.name label */
            'UserGroup.UserPermission.name' => _('Permission'),
        );
    }

    public function fullName() {
        return $this->name' '.$this->surname;
    }
}

Actions

Actions are additional non data columns such as links/buttons/... for adding/updating/deleting/...

Actions are defined in an array. Each action contains keys and values, some are attributes which will be assigned to the action HTML tag while others are used as configs. Below is a list of the config keys which will be explained later in this section.

  • Tag text.
  • Alias for text.
  • Model show decision method.
  • Model property/method list.
  • Action type.
  • For radio/checkbox actions
  • Alias for checked.

Any key/value other than the keys shown as config will be considered as attributes.

Action definition can be set using method actions or passed to render methods as an argument (render methods will be introduced later in this tutorial):

$user = new User();
$table = new Table($user, User::listCols());
$table->actions($actionDefinitions);

Defining basic actions

The most basic actions are defined as below, each action is an array of attributes which will be assigned to an anchor tag.

/* create columns of
   <a onclick="event.cancelBubble=true;"
      href="http://appdomain.com/users/update/3" 
      title="Press to update row">Update</a>
 */
$actions['update'] = array(
    'title' => _('Press to update row'),
    'text'  => _('Update'),
    'href'  => BASE_URL.'users/update/Model::id',
);
/* create columns of
   <a href="#"
      onclick="event.cancelBubble=true;return Dashboard.action('delete', 3);"
      title="Press to delete row"
      class="delete">&nbsp;</a>
 */
$actions['delete'] = array(
    'class'   => 'delete',
    'title'   => _('Press to delete row'),
    'onclick' => "Dashboard.action('delete', Model::id)",
);

When defining an action an optional key can be used, in above example update and delete were used.

Note: Model::key-property-name can be used in attribute values (excluding text) and will be replaced with the actual row key value.

Other properties or methods can be used as Model::method-or-property-name inside attribute values to be replaced with actual values. But must be defined in an array named replace this is to prevent wasting performance on checking all property names inside attribute values:

/* create columns of
   <a onclick="event.cancelBubble=true;"
      href="http://appdomain.com/users/update/Arta" 
      title="This user is active">Update</a>
 */
$actions['update'] = array(
    'replace' => array('name', 'activeDescription'),
    'title'   => 'Model::activeDescription',
    'text'    => _('Update'),
    'href'    => BASE_URL.'users/update/Model::name',
);

Show action or not

To show or not to show an action in a row: a decision maker method must exist inside the model which decides(returns True/False) if an action is shown or not, if False is returned the cell will remain empty.

This is an example of a decision maker method:

class User extends Model {
    /* ASSUME PROPERTIES AND CONSTRUCTOR ARE HERE... */

    /* decision maker method */
    public function deletable() {
        /* check permission or something ... */
        return $this->readonly === 'N';
    }
}

In next example the decision maker method shown in previous example is linked to an action by assigning model method name deletable to key show. Existence of this action for a row will depend on what method deletable returns for that row:

$actions['delete'] = array(
    'class'   => 'delete',
    'title'   => _('Press to delete row'),
    'onclick' => "Dashboard.action('delete', Model::id)",
    'show'    => 'deletable',
);

Select one action from a group of actions

To define a group of actions and select only one to be shown in the column: an action selector method must exist inside the model which decides which action is suitable for a row and returns the action name.

This is an example of an action selector method:

class User extends Model {
    /* ASSUME PROPERTIES AND CONSTRUCTOR ARE HERE... */

    /* action selector method */
    public function updateORview() {
        /* decide, select and return one of the action
           names in the group */
        return 'IF I AM THE ROW OWNER'? 'update': 'view';
    }
}

In next example two actions have been put in a group, the action selector method shown in previous example is linked to the group by assigning model method name updateORview to key select. Return value of updateORview is an action key name inside the group. Only the action which is returned by updateORview will be shown in the row:

$actions['editvsview'] = array(
    'select' => 'updateORview',
    'update' => array(
        'text'    => 'update',
        'onclick' => "Dashboard.action('update', Model::id)",
    ),
    'view' => array(
        'text'    => 'view',
        'onclick' => "Dashboard.action('view', Model::id)",
    ),
);

Checkbox actions

To put a checkbox on each row:

$actions['sel'] = array(
    'type'  => Table::CHECKBOX,
    'value' => 'Model::id',
    'title' => 'I am a checkbox please select me',
);

A decision method can be put in the model to decide if a row is checked or not:

class User extends Model {
    /* ASSUME PROPERTIES AND CONSTRUCTOR ARE HERE... */

    /* decision maker method */
    public function checkedOrNot() {
        /* decide if a row is selected or not */
        return $this->active === 'Y';
    }
}

Inside the action definition, the model decision method is assigned to key checked or selected:

$actions['selectme'] = array(
    'type'  => Table::CHECKBOX,
    'value' => 'Model::id',
    'title' => 'I am a checkbox please select me',
    'selected' => 'checkedOrNot',
);

Radiobox actions

Radioboxing rows is useful when only one row of all rows is going to be selected. For example in rows of photos, to select a main photo.

To put a radiobox on each row, note that normally only one radiobox is selected so the name attribute is set to the same value:

$actions['sel'] = array(
    'type'  => Table::RADIO,
    'value' => 'Model::id',
    'name'  => 'radio-name',
    'title' => 'I am a radiobox please select me',
);

A decision method can be put in the model to decide if a row is checked or not:

class User extends Model {
    /* ASSUME PROPERTIES AND CONSTRUCTOR ARE HERE... */

    /* decision maker method */
    public function radioOrNot() {
        /* decide if a row is selected or not */
        return $this->id === $this->__superUserId__;
    }
}

Inside the action definition, the model decision method is assigned to key checked or selected:

$actions['selectme'] = array(
    'type'     => Table::RADIO,
    'value'    => 'Model::id',
    'name'     => 'radio-name',
    'title'    => 'I am a radiobox please select me',
    'selected' => 'radioOrNot',
);

Attributes

When rendered with renderTable the structure of the HTML would be like shown below:

<table id="table-list-user">
  <thead>
    <tr id="user-trhead">
      <th>ID</th>
      <th>Name</th>
      <th colspan="2" class="actions"></th>
    </tr>
  </thead>
  <tbody>
    <tr class="odd">
      <td>1</td>
      <td>Name1</td>
      <td class="action">
        ACTION1 ANCHOR OR INPUT
      </td>
      <td class="action">
        ACTION2 ANCHOR OR INPUT
      </td>
    </tr>
  </tbody>
</table>

It is possible to assign values to the elements as explained in below example:

/* if value is string it will be assiged to class attribute */
$atts = array(
    /* assign this attributes to table tags */
    'table'     => array('class' => 'my-table-class'),
    /* assign this attributes to all tr tags */
    'tr'        => array('onclick' => "Dashboard.action('view', Model::id)"),
    /* assign attributes to td tags by property name */
    'id'        => 'center id-Model::id',
    'fullName'  => 'left',
    'UserGroup' => array('class' => 'center'),
)

Model::key-property-name can be used inside values and will be replaced by real key value for the row.

Attributes are assigned as {attribute:value,} arrays, however if a string is assigned instead of array it would be considered as class attribute.

To assign attributes to th tag:

$atts['th'] = array(
    /* assign attributes by header column (property name) */
    'id'   => array('class' => 'darker'),
    'name' => array('class' => 'lighter'),
    'City' => array(
        'class'   => 'city-class',
        'title'   => 'Click to see more',
        'onclick' => 'showInfo(this)',
    ),
);

th can include a key named sort where a JavaScript function name is assigned to it. If an href attribute exists then an anchor otherwise a button will be placed inside the th, the JavaScript function is for handling column header click probably for sorting the table by the column. Based on model query sort, class Table will mark the sorted column anchor/button class attribute to asc or desc.

$atts['th'] = array(
    'id'   => array('sort' => 'sortX(this, Table::COL)', 'class' => 'id',),
    'name' => array('sort' => 'sortX(this, Table::COL)'),
    'City' => array('sort' => "sortX(this, Table::COL)",),
    'City.County' => array(
        'sort' => "sortX(this, 'City.County.name')",
    ),
    'City.County.Country' => array(
        'sort' => "sortX(this, 'City.County.Country.name')",
    ),
);

The shortcut for header columns is the sort key. So if instead of an attribute array a string is assigned to a header(th) column, it will be considered as the sort JavaScript function:

$atts['th'] = array(
    'id'   => 'sortX(this, Table::COL)',
    'name' => 'sortX(this, Table::COL)',
    'City' => "sortX(this, Table::COL)",
    'City.County'            => "sortX(this, , Table::COL)",
    'City.County.Country'    => "sortX(this, , Table::COL)",
    'City.County.Country.id' => "sortX(this, 'City.County.Country.name')",
);

The rendered th would look similar to:

<th>
  <button onclick="sortX(this, 'City.name');" id="sort-user___City">
    City
  </button>
</th>

If True is assigned to the sort key then Arta.listSort(this, Table::COL) will be set as the JavaScript function. The example download for this demonstrates how to sort tables using this function.

See Table API - attributes.

Attribute definition can be set using method attributes or passed to render methods as an argument (render methods will be introduced later in this tutorial).

Parameters

To control renderer behaviors an array of available parameters for the renderer can be defined.

Parameters definition can be set using method params or passed to render methods as an argument (render methods will be introduced later in this tutorial).

renderTable() parameters:

$params = array(
    'stripe'    => False,
    'no_result' => 'Show this string when there is no row.',
);

renderDiv() parameters:

$params = array(
    'stripe'      => True,
    'no_result'   => 'Show this string when there is no row.',
    'tag_actions' => 'div',
    'tag_action'  => 'span',
    'tag_data'    => 'div',
    'empty'       => False,
    'i18n'        => Table::LOCALE,
);

renderView() parameters:

$params = array(
    'stripe'      => True,
    'no_result'   => 'Show this string when there is no row.',
    'tag_data'    => 'div',
    'empty'       => False,
    'i18n'        => Table::LOCALE,
);
  • Adds odd to class attribute of odd indexed rows and even to even indexed rows to make striping rows possible by CSS.
  • Text or HTML to be returned if model query result was empty.
  • Tag to use for action container
  • Tag to use for each action.
  • Tag to use as data container.
  • False = do not show columns which their value is Null. True = show all columns.
  • Style of showing all multi lingual data at once. Table::LOCALE or Table::COLUMN.

Styling rows

It is possible to control row tag class attribute within a model method. For example this is useful to color(by CSS) rows based on their data.

This example is a model method which returns a string based on the value of a data property which shows if a user is active or not:

class User extends Model {
    /* ASSUME PROPERTIES AND CONSTRUCTOR ARE HERE... */

    /* returns value to be added to row class attribute */
    public function rowColors() {
        return $this->active === 'Y'? 'white-row': 'red-row';
    }
}

In this example the model method is set to the table object:

$table->style('rowColors');

After rendering the table active and not active rows will have different values set to their tr tag class attribute:

<tr class="white-row">.....
or
<tr class="red-row">.....

Grouping

Normally a rendered table looks like the left table in below example. However it is possible to customize the table layout to look like the right table in the example:

| ID | Name   | Instrument |        | ID |  Name  |
|____|________|____________|        |____|________|
| 1  | David  | Guitar     |        |   Guitar    |
| 2  | Richie | Guitar     |        |_____________|
| 4  | Tony   | Guitar     |        | 1  | David  |
| 6  | Jimmy  | Guitar     |        | 2  | Richie |
| 7  | Yngwie | Guitar     |        | 4  | Tony   |
| 9  | Ulrich | Guitar     |        | 6  | Jimmy  |
| 10 | Bruce  | Vocals     |        | 7  | Yngwie |
| 11 | Ronnie | Vocals     |        | 9  | Ulrich |
| 14 | Robert | Vocals     |        |____|________|
| 16 | Steve  | Bass       |        |   Vocals    |
| 17 | Roger  | Bass       |        |_____________|
| 19 | Niko   | Drums      |        | 10 | Bruce  |
                                    | 11 | Ronnie |
                                    | 14 | Robert |
                                    |____|________|
                                    |    Bass     |
                                    |_____________|
                                    | 16 | Steve  |
                                    | 17 | Roger  |
                                    |____|________|
                                    |    Drums    |
                                    |_____________|
                                    | 19 | Niko   |

To render a table grouped by one or more model properties when model queried data is sorted by the grouping properties.

Below example renders a table which looks like the right table in above example:

$star = new RockStar();
$star->sort('Instrument');
$star->query();
$table = new Table($star, 'id', 'name');
/* group */
$table->group('Instrument');
/* or */
$table->group(array('Instrument'));
/* render */
$html = $table->renderTable();

Below example uses method groupStyle to change the table shape as shown below:

$star = new RockStar();
$star->sort('Instrument');
$star->query();
$table = new Table($star, 'id', 'name', 'Instrument');
/* group */
$table->group('Instrument');
/* group style */
$table->groupStyle(Table::GROUP_IN_ROW);
/* render */
$html = $table->renderTable();
| ID | Name   | Instrument |
|____|________|____________|
|    |        | Guitar     |
| 1  | David  | Guitar     |
| 2  | Richie | Guitar     |
| 4  | Tony   | Guitar     |
| 6  | Jimmy  | Guitar     |
| 7  | Yngwie | Guitar     |
| 9  | Ulrich | Guitar     |
|    |        | Vocals     |
| 10 | Bruce  | Vocals     |
| 11 | Ronnie | Vocals     |
| 14 | Robert | Vocals     |
|    |        | Bass       |
| 16 | Steve  | Bass       |
| 17 | Roger  | Bass       |
|    |        | Drums      |
| 19 | Niko   | Drums      |

To manually provide group row text, output of a model method can be used:

class RockStar extends Model {

    /* ASSUME PROPERTIES AND CONSTRUCTOR ARE HERE... */

    /* returns value to be added to row class attribute */
    public function groupRowText() {
        return '**'.strtoupper($this->Instrument->name).'**';
    }
}
$star = new RockStar();
$star->sort('Instrument');
$star->query();
$table = new Table($star, 'id', 'name');
/* group */
$table->group('Instrument');
/* group style */
$table->groupStyle(Table::GROUP_METHOD, 'groupRowText');
/* render */
$html = $table->renderTable();
| ID |  Name  |
|____|________|
| **GUITAR**  |
|_____________|
| 1  | David  |
| 2  | Richie |
| 4  | Tony   |
| 6  | Jimmy  |
| 7  | Yngwie |
| 9  | Ulrich |
|____|________|
| **VOCALS**  |
|_____________|
| 10 | Bruce  |
| 11 | Ronnie |
| 14 | Robert |
|____|________|
|  **BASS**   |
|_____________|
| 16 | Steve  |
| 17 | Roger  |
|____|________|
|  **DRUMS**  |
|_____________|
| 19 | Niko   |

Note: if model data is not sorted by the grouped properties then the results will not be desirable.

Note: grouping is only possible when rendering by renderTable method.

Rendering

After customizing the table as shown before, the last thing to do is to get rendered table as HTML and show it on the page.

Table comes with three rendering methods which produce different layouts.

Table (renderTable)

To render a normal table with table/tr/th/td tags:

$star = new RockStar();
$star->query();
$table = new Table($star, 'id', 'name', 'Instrument');
/* configs */
$actions = array(
    'update' => array(
        'text'  => 'Update',
        'onclick' => "Dashboard.action('update', Model::id)",
    ),
    'delete' => array(
        'text'    => 'delete',
        'onclick' => "Dashboard.action('delete', Model::id)",
    ),
);
$atts = array(
    'id'         => 'id middle',
    'instrument' => 'middle',
);
$params = array(
    'no_result' => 'No rows where found!',
);
/* render */
$html = $table->renderTable($actions, $atts, $params);
| ID | Name   | Instrument |                 |
|____|________|____________|_________________|
| 1  | David  |   Guitar   | update | delete |
| 2  | Richie |   Guitar   | update | delete |
| 4  | Tony   |   Guitar   | update | delete |
| 6  | Jimmy  |   Guitar   | update | delete |
| 7  | Yngwie |   Guitar   | update | delete |
| 9  | Ulrich |   Guitar   | update | delete |
| 10 | Bruce  |   Vocals   | update | delete |
| 11 | Ronnie |   Vocals   | update | delete |
| 14 | Robert |   Vocals   | update | delete |
| 16 | Steve  |    Bass    | update | delete |
| 17 | Roger  |    Bass    | update | delete |
| 19 | Niko   |   Drums    | update | delete |

Actions, attributes and parameters are optional arguments.

Actions, attributes and parameters can be set by actions, attributes and params methods.

View (renderView)

To get data view of only one record:

$star = new RockStar();
$star->id = 7;
$star->query();
$table = new Table($star, RockStar::viewCols());
/* configs */
$atts = array(

);
$params = array(
    'no_result' => 'Data not exists!',
    'empty'     => True,
);
/* render */
$html = $table->renderTable($actions, $atts, $params);
           ID: 7
         Name: Yngwie
   Instrument: Guitar
       Active: yes
  Create date: 2012/01/20 12:02:02

Attributes and parameters are optional arguments.

Attributes and parameters can be set by attributes and params methods.

Div (renderDiv)

To make each row of data look like renderView:

$star = new RockStar();
$star->query();
$table = new Table($star, 'id', 'name', 'Instrument');
/* configs */
$actions = array(
    'update' => array(
        'text'  => 'Update',
        'onclick' => "Dashboard.action('update', Model::id)",
    ),
    'delete' => array(
        'text'    => 'delete',
        'onclick' => "Dashboard.action('delete', Model::id)",
    ),
);
$atts = array(
    'id'         => 'id middle',
    'instrument' => 'middle',
);
$params = array(
    'no_result' => 'No rows where found!',
);
/* render */
$html = $table->renderDiv($actions, $atts, $params);
           ID: 1
         Name: David
   Instrument: Guitar
               update delete
_______________________________
           ID: 2
         Name: Richie
   Instrument: Guitar
               update delete
_______________________________
           ID: 4
         Name: Tony
   Instrument: Guitar
               update delete
_______________________________
           ID: 6
         Name: Jimmy
   Instrument: Guitar
               update delete
_______________________________
           ID: 7
         Name: Yngwie
   Instrument: Guitar
               update delete
_______________________________
           ID: 9
         Name: Ulrich
   Instrument: Guitar
               update delete
_______________________________
           ID: 10
         Name: Bruce
   Instrument: Vocals
               update delete
_______________________________
           ID: 11
         Name: Ronnie
   Instrument: Vocals
               update delete
_______________________________
           ID: 14
         Name: Robert
   Instrument: Guitar
               update delete
_______________________________
           ID: 16
         Name: Steve
   Instrument: Bass
               update delete
_______________________________
           ID: 17
         Name: Roger
   Instrument: Bass
               update delete
_______________________________
           ID: 10
         Name: Niko
   Instrument: Drums
               update delete

Actions, attributes and parameters are optional arguments.

Actions, attributes and parameters can be set by actions, attributes and params methods.

Example

Models - tables
TOP