2.7.1. Section

Sections are used to create lists. It starts with a {section} tag and ends with {/section}. The content between them is the look of a single element of the list. The section has a unique name, which we use to access the list element data and to identify, where are the list data for it.

Example 2.24. Simple section

<table>
<tr>
<td>Product name</td>
<td>Product Price</td>
</tr>
{section=products}
<tr>
<td>{$products.name}</td>
<td>{$products.price}</td>
</tr>
{/section}
</table>

Inside sections, the syntax {$table.key} changes its meaning and it is used to access the section element blocks. The first part identifies the section and the second - the name of an element block. Since OPT 1.1.3, sections could be also used with non-array elements, such as integers or objects.

Example 2.25. Non-array section data

<p>Numbers:</p>
<ul>
{section=numbers}
	<li>{$numbers}</li>
{/section}
</ul>

<p>Objects:</p>
<ul>
{section=objects}
	<li>{$objects -> method()}</li>
{/section}
</ul>

The optional {sectionelse} element can be added to display some text, if the section contains no elements:

Example 2.26. Sectionelse

<table>
<tr>
<td>Product name</td>
<td>Product Price</td>
</tr>
{section=products}
<tr>
<td>{$products.name}</td>
<td>{$products.price}</td>
</tr>
{sectionelse}
<tr>
<td colspan="2">There are no products in this list!</td>
</tr>
{/section}
</table>

The section takes some parameters:

Table 2.3. Section: parameters

Section: parameters
NameTypeRequired?Description
nameIDYesThe section name.
orderIDNoIf set to "reversed", the section items are shown in reversed order.
stateExpressionNoIf the expression returns false, the section will be hidden like it contains no elements.
datasourceExpressionNoThe location of section data. If not specified, the section name is used.
separatorExpressionNoA separator inserted between two section elements. The parameter can be replaced with the separator instruction. The parameter is available since OPT 1.1.3.

In the examples above, we were not able to hide the whole table structure, if the section contains not elements. However, OPT allows to do this by using additional {show} tags.

Example 2.27. {show}

{show=products}
<table>
<tr>
<td>Product name</td>
<td>Product Price</td>
</tr>
{section}
<tr>
<td>{$products.name}</td>
<td>{$products.price}</td>
</tr>
{/section}
</table>
{showelse}
<p>There are no products in this list</p>
{/show}

In this variant, we define all the parameters in the {show} tags and keep {section} empty. Moreover, instead of {sectionelse}, we use {showelse}.

Now, if the section has some data, the table will be displayed and the code inside a section will be used as the list element. In the other case, the paragraph with a message is shown.

You may also create subsections. Just put a section... /section block into another section:

Example 2.28. Subsections

<table>
<tr>
<td>Product name</td>
<td>Product categories</td>
</tr>
{section=products}
<tr>
<td>{$products.name}</td>
<td><ul>
  {section=categories}
  <li>{$categories.name} (or) {$products.categories.name}</li>
  {/section}
</ul></td>
</tr>
{/section}
</table>

Note that you may access to the subsection data in two ways: $categories.block or $products.categories.block. Just make sure the first element of the block is a name of one of the sections - otherwise OPT translates it as a standard array call.

2.7.1.1. Attributes

Since OPT 1.1.0, sections provide some attributes to use with HTML tags.

2.7.1.1.1. opt:sectionfirst

Defines the class="first" HTML attribute for the tag, if the current section element is the first on the list. It takes the section name as the value.

Example 2.29. opt:sectionfirst

<table>
<tr>
<td>Product name</td>
<td>Product Price</td>
</tr>
{section=products}
<tr>
<td opt:sectionfirst="products">{$products.name}</td>
<td opt:sectionfirst="products">{$products.price}</td>
</tr>
{/section}
</table>

2.7.1.1.2. opt:sectionlast

Defines the class="last" HTML attribute for the tag, if the current section element is the last on the list. It takes the section name as the value.

Example 2.30. opt:sectionlast

<table>
<tr>
<td>Product name</td>
<td>Product Price</td>
</tr>
{section=products}
<tr>
<td opt:sectionlast="products">{$products.name}</td>
<td opt:sectionlast="products">{$products.price}</td>
</tr>
{/section}
</table>

2.7.1.1.3. opt:sectioncycle

Alternates a set of HTML attributes. This makes it easy to alternate between two or more colors in the table. The values are defined in additional tag located inside a section: {cycle=attributename; ...} - the first parameter is the name of HTML attribute; next we define the values to cycle.

Example 2.31. opt:sectioncycle

<table>
<tr>
<td>Product name</td>
<td>Product Price</td>
</tr>
{section=products}
{cycle='class'; 'brighter'; 'darker'}
<tr>
<td opt:sectioncycle="products">{$products.name}</td>
<td opt:sectioncycle="products">{$products.price}</td>
</tr>
{/section}
</table>

The result:

<table>
<tr>
<td>Product name</td>
<td>Product Price</td>
</tr>
<tr>
<td class="brighter">Product 1</td>
<td class="brighter">15.50</td>
</tr>
<tr>
<td class="darker">Product 2</td>
<td class="darker">12.00</td>
</tr>
<tr>
<td class="brighter">Product 3</td>
<td class="brighter">17.30</td>
</tr>
<tr>
<td class="darker">Product 4</td>
<td class="darker">13.99</td>
</tr>
</table>

2.7.1.2. {$opt} features

Sections could tell you much more with $opt special block:

Table 2.4. $opt.section reference

$opt.section reference
Special blockValue
$opt.section.sect_name.countThe number of elements in the section sect_name.
$opt.section.sect_name.idThe current section element ID.
$opt.section.sect_name.sizeThe current section element size (number of section blocks).
$opt.section.sect_name.firstTrue, if the first section element is processed.
$opt.section.sect_name.lastTrue, if the last section element is processed.
$opt.section.sect_name.farTrue, if the first or last section element is processed.

Sample use:

Example 2.32. Sample use

{show=products}
<p>There are {$opt.section.products.count} products here.</p>
<ul>
{section}
  {if $opt.section.products.first}
  <li><b>Hit!</b> {$products.name}</li>
  {else}
  <li>{$products.name}</li>
  {/if}
{/section}
</ul>
{/show}

2.7.1.3. Sections for programmers

Sections require data as an specific-type array. For single sections it is always the same. OPT supports two formats for subsections and it is you who decide, which is default. The second format is also available using the parameter.

The data array for sections is multi-dimensional. It is an array of arrays containing data for the section blocks. Here we see a sample PHP code:

Example 2.33. Data for single sections

$products = array(0 =>
   array('name' => 'Apples', 'price' => 15),
   array('name' => 'Pears', 'price' => 17),
   array('name' => 'Bananas', 'price' => 13)
);
$tpl -> assign('products', $products);

This code creates three list elements. Each of them contains two blocks: name and price.

Since OPT 1.1.3, it is possible to work with non-array elements. In order to use them, you have to replace the array of element blocks visible above, with an integer, string or some object. Remember that you must still keep the contignous and starting from 0 index order.

Example 2.34. Data for non-array sections

$integers = array(0 =>
   15, 17, 13, 21
);
$tpl -> assign('integers', $integers);

As we mentioned above, you have to decide, how you want to support subsection data. You may specify the format with the sectionStructure configuration directive, which takes one of two values: OPT_SECTION_MULTI (default) and OPT_SECTION_SINGLE.

OPT_SECTION_MULTI: This format is set by default. Each subsection must be assigned separately, as another array. It has to have as many indexes, as deep it is located. If we want to show the categories assigned to the product, using this format, we have to built second array with three indexes. First one tells, which product it is assigned to, second is the category iterator inside this product, and the third is block data index:

Example 2.35. OPT_SECTION_MULTI

$categories = array(0 =>
   // Categories for product 1
   array(0 =>
      array('name' => 'Food'),
      array('name' => 'Drinks')
   ),
   // Categories for product 2
   array(0 =>
      array('name' => 'Meat'),
      array('name' => 'Dinner'),
   ),
   // Categories for product 3
   array(0 =>
      array('name' => 'Vegetables'),
      array('name' => 'Dinner'),
   )
);
$tpl -> assign('categories', $categories);

If the subsection is located inside two other, you must create four indexes, etc.

OPT_SECTION_SINGLE: In this format, all the data for section and its subsections are located in one array and assigned only once to the template engine. Within the section blocks we create an index named like the subsection and we put its array there.

Example 2.36. OPT_SECTION_SINGLE

$products = array(0 =>
   array('name' => 'Apples', 'price' => 17, 'categories' =>
     // Category subsection data for this product
     array(0 =>
       array('name' => 'Fruit'),
       array('name' => 'Drinks')
     )
   ),
   array('name' => 'Pears', 'price' => 15, 'categories' =>
     // Category subsection data for this product
     array(0 =>
       array('name' => 'Fruit'),
       array('name' => 'Ice cream')
     )
   )
);
$tpl -> assign('products', $products);

.1. The "datasource" parameter

As mentioned above, you can always use both multi-array and single-array format, even if you chose the other as your default one. This is provided by the datasource parameter available in each section. If you work in OPT_SECTION_MULTI mode and you want to read subsection data from the upper section table, you specify here its block that contains the data:

Example 2.37. Datasource and OPT_SECTION_MULTI

<td>Product categories</td> {* for OPT_SECTION_MULTI *}
</tr>
{section=products}
<tr>
<td>{$products.name}</td>
<td><ul>
  {* read the data like in OPT_SECTION_SINGLE *}
  {section=categories; !x; !x; $products.categories}
  <li>{$categories.name} (or) {$products.categories.name}</li>
  {/section}
</ul></td>
</tr>
{/section}
</table>

The data of categories subsection are located in $products.categories block.

Example 2.38. Datasource and OPT_SECTION_SINGLE

<td>Product categories</td> {* for OPT_SECTION_SINGLE *}
</tr>
{section=products}
<tr>
<td>{$products.name}</td>
<td><ul>
  {* read the data like in OPT_SECTION_MULTI *}
  {section=categories; !x; !x; $categories}
  <li>{$categories.name} (or) {$products.categories.name}</li>
  {/section}
</ul></td>
</tr>
{/section}
</table>

.2. Dynamic sections

OPT 1.1.0 introduces the new way of handling the section data. Instead of passing the already-generated lists to the parser, we pass only the function/method that will generate it, if a section with a specified name is used. This can be useful in modular websites - if a template does not make use of some available sections, the data for them are not generated. This is, what we call dynamic sections.

The template designer does not know anything, which section is dynamic and which is not - this is only a PHP script issue. OPT supports two ways of processing dynamic sections: statically compiled (default) and runtime.

In order to make dynamic section, we have to write a function or class method that generates the section data in the format described above. We pass a standard PHP callback to the parser with optClass::assignDynamic() method.

If the dynamic sections are statically compiled, the callbacks must be defined BEFORE the template compilation. The calls of the functions are compiled in the template and we must not change it later (in other words - you must not pass callback for one request and the normal data for the second using the same section and template). The advantage is that "normal" sections are not touched by the compiler and work like in previous releases of OPT.

The dynamic sections may be also handled during the runtime. Now you can use function callback, wherever and whenever you want, but at the cost of performance - the compiler puts a small condition at the beginning of each section, which checks, whether the static data or dynamic callback was provided.

The way of dynamic section handling can be changed with sectionDynamic configuration directive and the possible values: OPT_SECTION_COMPILE (default) and OPT_SECTION_RUNTIME.

Example 2.39. Making dynamic sections

function dynamicSectionGenerator()
{
	// Section data generator
	return array(0 =>
		array('title' => 'Foo'),
		array('title' => 'Bar'),
		array('title' => 'Joe')
	);	
} // end dynamicSectionGenerator();
	
$tpl -> assignDynamic('dyn', 'dynamicSectionGenerator', array());
$tpl -> parse('template.tpl');