joomla/joomla-cms — pull request #3231 by phproberto (2014-03-04 04:12:09+00:00)
Title: [imp] layout improvements and base system for fields

This pull request introduces some new `JLayout` based features:

# JLayout improvements

## 1. Chainable methods

I have added the option to chain methods when possible. Now you can do:

```php
$renderer = new JLayoutFile('joomla.content.tags', null, array('debug' => true));

echo $renderer
->setClient(0)
->setComponent('com_categories')
->render($this->item->tags->itemTags);
```

## 2. Suffixes improvements

Version 3.2 already supported suffixes but I created some improvements.

### 2.1 Add suffixes dynamically

Now suffixes can be added dynamically with a `setSuffixes($suffixes) method. Example:

```php
$renderer = new JLayoutFile('joomla.content.tags', null, array('debug' => true));

echo $renderer
->setComponent('com_content')
->setClient(1)
->setSuffixes(array('j25'))
->render($this->item->tags->itemTags);
```

### 2.2 Automatic version suffixes

Most extended suffixes use was for having different versions for Joomla 2.5 & Joomla 3.x. I've added an automatic mode that will load the default version suffixes:

```php
$renderer = new JLayoutFile('joomla.content.tags', null, array('debug' => true));

echo $renderer
->setSuffixes('autoversion')
->render($this->item->tags->itemTags);
```
Will automatically load this suffixes when executed on Joomla 3.2.3:

```php
Suffixes: Array
(
[0] => j323
[1] => j32
[2] => j3
)
```
So it will search for a layout in this priority order:

```
Searching layout for: joomla/content/tags.j323.php
Searching layout for: joomla/content/tags.j32.php
Searching layout for: joomla/content/tags.j3.php
Searching layout for: joomla/content/tags.php
```

This is mainly intended to be used on classes that extend `JLayoutFile` because sadly layouts are not available on Joomla 2.5.x series (they are included on FOF now).

For example an extended class running the same code on Joomla 2.5.18 will search layouts on:
```
Searching layout for: joomla/content/tags.j2518.php
Searching layout for: joomla/content/tags.j25.php
Searching layout for: joomla/content/tags.j2.php
Searching layout for: joomla/content/tags.php
```

As you can see is useful to have layout specific for a Joomla version.

### 2.3 Automatic language suffixes

This is an automatic suffix mode language oriented. Example:

```php
$renderer = new JLayoutFile('joomla.content.tags', null, array('debug' => true));

echo $renderer
->setSuffixes('autolanguage')
->render($this->item->tags->itemTags);
```

It will automatically add this suffixes when executed on a site with `en-GB` language active.

```php
Suffixes: Array
(
[0] => en-GB
[1] => en
[2] => ltr
)
```

So you can create a layout for:
* This specific locale
* This specific language
* This specific language direction

Good examples would be addresses that are ordered differently on different languages or layouts specifics to `RTL` (Right To Left) languages

## 3. Prefixes support

Now `JLayoutFile` also supports prefixes. This is mainly to group different prefixes under the same prefix. In the practice a prefix is a main folder that stores all that layouts. Example:

```php
$renderer = new JLayoutFile('joomla.content.tags', null, array('debug' => true));

echo $renderer
->setPrefixes('bootstrap3')
->render($this->item->tags->itemTags);
```

It will search layouts in this order when called from the article view:
```php
Include Paths: Array
(
[0] => templates/protostar/html/layouts/com_content/bootstrap3
[1] => components/com_content/layouts/bootstrap3
[2] => templates/protostar/html/layouts/bootstrap3
[3] => layouts/bootstrap3
[4] => templates/protostar/html/layouts/com_content
[5] => components/com_content/layouts
[6] => templates/protostar/html/layouts
[7] => layouts
)
```

So for example if you are on a template that supports both bootstrap2 & bootstrap3 mode you can put in the layouts/bootstrap3 folder the specific layouts for bootstrap3.

## 4. Cached file searches

When a layout is already searched in a specific list of include paths the final path is saved on cache. Next time the layout is searched in that list of include paths the cached path will be returned.

## 5. Statistics

Now `JLayoutFile` stores statistics about its use. To get the stats you have to use:

```php
$stats = JLayoutFile::getStats();
```

It will return something like:

```php
Array
(
[fileSearches] => 20
[fileSearchesCached] => 11
[fileSearchesSkipped] => 0
[times] => Array
(
[fileSearching] => 0.0028524398803711
[fileRendering] => 0.024386882781982
[/var/www/jcms3x/layouts/plugins/content/vote/vote.php] => 0.0019118785858154
[/var/www/jcms3x/layouts/joomla/content/blog_style_default_item_title.php] => 0.0065479278564453
[/var/www/jcms3x/layouts/joomla/content/icons.php] => 0.005518913269043
[/var/www/jcms3x/layouts/joomla/content/info_block/author.php] => 0.00033211708068848
[/var/www/jcms3x/layouts/joomla/content/info_block/category.php] => 0.0012009143829346
[/var/www/jcms3x/layouts/joomla/content/info_block/publish_date.php] => 0.00086474418640137
[/var/www/jcms3x/layouts/joomla/content/info_block/hits.php] => 0.0002598762512207
[/var/www/jcms3x/layouts/joomla/content/info_block/block.php] => 0.0072002410888672
[/var/www/jcms3x/layouts/joomla/content/intro_image.php] => 0.00026798248291016
)

[layoutsRendered] => Array
(
[/var/www/jcms3x/layouts/plugins/content/vote/vote.php] => 2
[/var/www/jcms3x/layouts/joomla/content/blog_style_default_item_title.php] => 2
[/var/www/jcms3x/layouts/joomla/content/icons.php] => 2
[/var/www/jcms3x/layouts/joomla/content/info_block/block.php] => 4
[/var/www/jcms3x/layouts/joomla/content/info_block/author.php] => 2
[/var/www/jcms3x/layouts/joomla/content/info_block/category.php] => 2
[/var/www/jcms3x/layouts/joomla/content/info_block/publish_date.php] => 2
[/var/www/jcms3x/layouts/joomla/content/info_block/hits.php] => 2
[/var/www/jcms3x/layouts/joomla/content/intro_image.php] => 2
)

)
```
All the stats in a single array.

### 5.1 Stats details

$stats['fileSearches'] : Number of layout searches performed
$stats['fileSearchesCached'] : Number of layout searches returning cached paths
$stats['fileSearchesSkipped'] : Number of layout searches skipped because they share same instance with same params
$stats['times']: Array containing different times tracked
$stats['times']['fileSearching']: Total time spent searching layouts on file system
$stats['times']['fileRendering']: Total time spent rendering layouts
$stats['times'][$path]: System stores also time spent rendering each layout. This includes the global rendering time of rendering all appearences of this layout.
$stats['layoutsRendered']: Array containing the list of layouts rendered (array key) and the times it has been rendered ( value).

## 6. Better includepaths handling

In the previous versions you could not specify a list of include paths. The layout paths were always calculated automatically. Now you have total control over them

### 6.1 setIncludePaths

```php
$renderer = new JLayoutFile('joomla.content.tags', null, array('debug' => true));

$renderer->setIncludePaths(
array(
JPATH_COMPONENT . '/layouts',
JPATH_SITE . '/layouts'
)
);

echo $renderer->render($this->item->tags->itemTags);
```
Will search for layouts on:

```php
Include Paths: Array
(
[0] => /var/www/jcms3x/components/com_content/layouts
[1] => /var/www/jcms3x/layouts
)
```

### 6.1 resetIncludePaths

Delete the include paths information

```php
echo $renderer
->setIncludePaths(
array(
JPATH_COMPONENT . '/layouts',
JPATH_SITE . '/layouts'
)
)
->resetIncludePaths()
->addIncludePaths(
array(
JPATH_COMPONENT . '/layouts-new',
JPATH_SITE . '/layouts-new'
)
)
->render($this->item->tags->itemTags);
```
will search in this include paths:

```php
Include Paths: Array
(
[0] => /var/www/jcms3x/components/com_content/layouts-new
[1] => /var/www/jcms3x/layouts-new
)
```
### 6.2 loadDefaultIncludePaths

It will load the automatically calculated paths.

# JFormField improvements

## 1. Field requirements support

This adds the option for fields to specify requirements that will prevent the field from being loaded.

There are only 2 main requirements defined at this point:

* **multilanguage** : Requires language filter is enabled
* **associations** : Requires language associations enabled

**Note:** This adds the support but most of the fields will require to modify the geInput() method to add the `$this->validateRequitements()` call. So don't expect it works on all fields out of the box. It should only work on labels and on text field.

A sample field definition:

```xml

```

You can specify more than one requirement:

```xml

```
You can easily extend requirements for your field like:

```php
/**
* Function to determine if field validates the requirements
*
* @return boolean
*/
protected function validateRequirements()
{
if (parent::validateRequirements())
{
$requires = explode(',', (string) $this->element['requires']);

if (in_array('myrequirement', $requires) && !MyHelper::validateRequirement())
{
return false;
}

return true;
}

return false;
}
```

## 2. Layouts support

This adds support to use `JLayoutFile` to render form fields. This is added in `JFormField` but it doesn't change most of the existing fields to ensure BC. So don't expect it to work out of the box on all the fields. Included fields are:
* JFormFieldList
* JFormFieldUser

Now layouts can be defined inside fields setting the property `$layout`:

```php
/**
* Layout to render
*
* @var string
*/
protected $layout = 'joomla.form.field.list';
```

But they also can be overriden on form. Thanks to this we can save to create new fields when the logic to get the data is the same and only the logic to render it changes. Examples would be to load `select2` on a field for example:

```xml

JGLOBAL_USE_GLOBAL

JSHOW

JHIDE

```
Or imagine that you have a layout to render a filed loading `typeahead.js` field:

```xml

```

You can also set a specific layout for the label and layout prefixes/suffixes on your field class:

```php
/**
* Layout to render the field label
*
* @var string
*/
protected $labelLayout = 'joomla.form.field.label';

/**
* Prefixes to use to render the field.
*
* @var array
*/
protected $layoutPrefixes = array();

/**
* Suffixes to use to render the field.
*
* @var array
*/
protected $layoutSuffixes = array();
```

or in your form XML file:

```xml

JGLOBAL_USE_GLOBAL

JSHOW

JHIDE

```

## 3. Overridable getRenderer

If you want to change the default paths or load your extended class instead of `JLayoutFile` you can nor override the getRenderer method:

```php
/**
* Get the layout used to render the field.
*
* @param string $layoutId Layout to render
* @param mixed $options Optional custom options to load. JRegistry or array format
*
* @return JLayoutFile The layout
*/
public function getRenderer($layoutId, $options = array())
{
$prefixes = !empty($this->element['layout-prefixes']) ? explode(',', $this->element['layout-prefixes']) : $this->layoutPrefixes;
$suffixes = !empty($this->element['layout-suffixes']) ? explode(',', $this->element['layout-suffixes']) : $this->layoutSuffixes;

$options = array_merge(array('prefixes' => $prefixes, 'suffixes' => $suffixes), $options);

return new JLayoutFile($layoutId, null, $options);
}
```

This gives a very flexible tool to developers.

# Plugins improvements

## 1. Layout support

Now in your plugin you can do something like:

```php
return $this->getRenderer('default')->render(
array(
'context' => $context,
'row' => $row,
'params' => $params,
'page' => $page
)
);
```
It will search a `default.php` layout on:

```php
Include Paths: Array
(
[0] => /var/www/jcms3x/templates/protostar/html/layouts/plugins/content/vote
[1] => /var/www/jcms3x/layouts/plugins/content/vote
)
```

**Note:** All the plugins are still using the old hardcoded method. Only the plugin `content/vote` has been converted for testing purposes.

## 2. Layouts information on debug

Now the debug console shows all the information of the layouts loaded in the current page:

![layouts-article-form](https://f.cloud.github.com/assets/1119272/2317634/d5ac61c4-a351-11e3-854b-acac68e6568a.png)

# Testing

Required tests include:
* Check that all list fields are shown and work correctly (including tags and all other extending fields)
* Check that user field works correctly
* Check that when you enable the option to show votes on articles it's dispalyed correctly
* Make performance tests with & without this patch to ensure that layouts doesn't overload system.
* Common layout override tests with prefixes, suffixes, include paths....