Custom HTML Tags (WidgetHelper) June 20, 2006
Posted by rossoft in CakePHP.trackback
Do you want create custom helpers accessible in your views like:
Example of HighlightWidget:
<h:span class="high" search="/I'm \w+/">
Hi, I'm John. What's your name? I'm Marcus.
</h:span>
And it will output the html:
Hi, <span class="high">I'm john</span>. What's your name?
Example of RepeaterWidget:
<?php $array=array('Frodo'=>'Elijah Wood','Aragorn'=>'Viggo Mortensen')
$repeaterWidget->bind('cast',$array))?>
<repeater:repeater array='cast'>
<b><repeater:key/>:</b> <repeater:value/>
<br/>
</repeater>
Will output the html:
<b>Frodo:</b>: Elijah Wood
<br/>
<b>Aragorn:</b>: Viggo Mortensen
<br/>
The usage is really easy. The configuration is the same as a normal helper,
only include in your controller $helpers=array('xxx',…,'RepeaterWidget','HighlightWidget');
And the definition of new Widgets is easy too, see the Highlight & Repeater code:
HighlightWidgetHelper:
<?php
/**
* HighlightWidget Helper
*
* Highlights some expression found in a html text.
*
* Example:
* In style declaration:
* .high { color: yellow; }
* In view:
* <h:span class="high" search="/I'm \w+/">
* Hi, I'm john. What's your name?
* </h:h>
*
* Will output the html:
* Hi, <span class="high">I'm john</span>. What's your name?
*
* @author RosSoft
* @version 0.1
* @license MIT
*
*/
require_once(dirname(__FILE__) . DS . 'widget_helper.php');
class HighlightWidgetHelper extends WidgetHelper
{
//main tags name => array subtags
var $tag=array('h:div','h:span');
function tag_h_div($attr,$inner_html)
{
return $this->replace($attr,$inner_html,'div');
}
function tag_h_span($attr,$inner_html)
{
return $this->replace($attr,$inner_html,'span');
}
function replace($attr,$inner_html,$type)
{
$class=@$attr['class'];
$expression=@$attr['search'];
return preg_replace($expression,"<$type class=\"$class\">" . '\' . "</$type>",$inner_html);
}
}
?>
RepeaterWidgetHelper:
<?php
/**
* RepeaterWidget Helper
*
* Repeats the html within the tag <repeater:repeater>HTML</repeater:repeater>
* that contains the subtags <repeater:key /> and <repeater:value />
* It replaces the subtags with the key & value from an associative array.
* You must bind the widget to the array through
* Example:
* <?php
* $array=array('Frodo'=>'Elijah Wood','Aragorn'=>'Viggo Mortensen')
* $repeaterWidget->bind('cast',$array))?> *
* <repeater:repeater array='cast'>
* <b><repeater:key/>:</b> <repeater:value/>
* <br/>
* </repeater>
*
* Will output the html:
* <b>Frodo:</b>: Elijah Wood
* <br/>
* <b>Aragorn:</b>: Viggo Mortensen
* <br/>
*
* @author RosSoft
* @version 0.1
* @license MIT
*
* @link http://www.phpit.net/article/create-html-widgets-php/4/
*/
require_once(dirname(__FILE__) . DS . 'widget_helper.php');
class RepeaterWidgetHelper extends WidgetHelper
{
//main tags name => array subtags
var $tag=array(
'repeater:repeater'=>array('repeater:key','repeater:value')
);
function tag_repeater_repeater($attr,$inner_html)
{
$array=$this->_get_bound($attr['array']);
// loop through bound array
$new_html = '';
foreach ($array as $k=> $v)
{
$new_html .= $this->_process_subtags(array('key'=>$k,'value'=>$v));
}
return $new_html;
}
function subtag_repeater_key($attr,$inner_html,$params)
{
return $params['key'];
}
function subtag_repeater_value($attr,$inner_html,$params)
{
return $params['value'];
}
}
?>
The widgets are helpers, then you can use others helpers with $helpers variable, also
you can call a function of them with $highlightWidget->somefunction() like before.
The core class is WidgetHelper. That class must be extended in your widgets (you must do
a include_once for this).
Put in app/views/helpers/widget_helper.php (it ends in _helper) :
<?php
/**
* Widget Helper
*
* Parses custom html tags like
* <mytag:mytag1 attr1="value1" attr2="value2">some html</mytag:mytag1>
* (This class must be extended)
*
* @author RosSoft
* @version 0.1
* @license MIT
*
* The filename is widget_helper.php because you must
* overload it, not use directly.
*
*/
class WidgetHelper extends Helper
{
var $_library; //static array of widgets instances
//main tag names => array subtags
var $tag=array();
var $_data=array();
var $helpers=array('Util');
function __construct()
{
static $library=array(); //for php4 compat
$this->_library=& $library;
$this->_library[]=& $this; //add current instance
if (!in_array('Util',$this->helpers))
{
array_push($this->helpers,'Util');
}
}
/**
* afterRender Callback
*/
function afterRender()
{
static $once=true;
if (! $once) return;
$once=false;
$html=@ob_get_clean();
ob_start();
$matches=array();
preg_match_all("/(<(\w+:\w+)([^>]*)>(.*?)<\/\\2>)|(<(\w+:\w+)([^>]*)\/>())/is", $html, $matches,PREG_SET_ORDER);
foreach ($matches as $match)
{
if (isset($match[6]))
{
$tagname=$match[6];
$attributes=$match[7];
$inner_html=$match[8];
}
else
{
$tagname=$match[2];
$attributes=$match[3];
$inner_html=$match[4];
}
$outer_html=$match[0];
foreach ($this->_library as $widget)
{
foreach ($widget->tag as $tag=>$subtags)
{
if (is_numeric($tag))
{
$tag=$subtags;
$subtags=array();
}
if ($tag==$tagname)
{
$html=$widget->_process_tag($html, $tag, $outer_html,$inner_html,$attributes,$subtags);
}
}
}
}
echo $html;
}
/**
* Bind data to the widget
*
* @param string $name Name of the data
* @param mixed $data The data
*/
function bind($name, $data)
{
$this->_data[$name] = $data;
}
/**
* Process one tag
* @param string $content The complete document
* @param string $tag_html One tag
*/
function _process_tag($html, $tag_id,$outer_html,$inner_html,$attributes,$subtags)
{
// Parse tag
$attributes= $this->_parse_attributes($attributes);
$this->_tag_id=$tag_id;
$this->_tag_inner_html=$inner_html;
$this->_tag_subtags=$subtags;
$method_name='tag_' . str_replace(':','_',$tag_id);
$out=$this->$method_name($attributes,$inner_html);
if ($out!==false)
{
return str_replace($outer_html,$out, $html);
}
else
{
//show the error through html entities
return str_replace($outer_html,h($outer_html), $html);
}
}
function _get_bound($name)
{
return $this->_data[$name];
}
function _get_field_value($fieldName)
{
return $this->Util->retrieve_value($fieldName);
}
function _get_view_value($var_name)
{
return $this->view->_viewVars[$var_name];
}
function _process_subtags($params=array(),$html=null)
{
if ($html===null)
{
$html=$this->_tag_inner_html;
}
foreach ($this->_tag_subtags as $subtag)
{
preg_match_all("/(\<$subtag([^>]*)\>(.*?)\<\/$subtag\>)|(\<$subtag([^>]*)\/\>())/is", $html, $matches,PREG_SET_ORDER);
for ($i=0; $i < count($matches); $i++)
{
$outer_html= $matches[$i][0];
if (isset($matches[$i][5]))
{
$attributes=$matches[$i][5];
}
else
{
$attributes=$matches[$i][2];
}
$attributes=$this->_parse_attributes($attributes);
$inner_html= $matches[$i][3];
$method_name='subtag_' . str_replace(':','_',$subtag);
$out=$this->$method_name($attributes,$inner_html,$params);
if ($out!==false)
{
$html= str_replace($outer_html, $out, $html);
}
else
{
//show the error through html entities
$html= str_replace($outer_html, h($out), $html);
}
}
}
return $html;
}
function _parse_attributes($html)
{
$matches=array();
$attributes=array();
preg_match_all('/(\w+)\s*=\s*"([^"]*)"/is', $html, $matches,PREG_SET_ORDER);
foreach ($matches as $attr)
{
$attributes[low($attr[1])]=$attr[2];
}
return $attributes;
}
}
?>
Looks like this could help all the peoples that are making wizards, survey tool and other form generators, isn’t it ?
yeah, I’m starting the FormWidget
I think use attribute bind in repeater tag repeater:
:
And rewrite tag_repeater_repeater:
function tag_repeater_repeater($attr,$inner_html)
{
if($attr[‘bind’] != ”) {
$array=$this->view->_viewVars[$attr[‘bind’]];
} else {
$array=$this->_get_bound($attr[‘array’]);
}
// loop through bound array
$new_html = ”;
foreach ($array as $k=> $v)
{
$new_html .= $this->_process_subtags(array(‘key’=>$k,’value’=>$v));
}
return $new_html;
}
yes, that’s good idea, thanks. But remember that RepeaterWidget & HighlightWidget are stupid examples, the useful thing is the HelperWidget
Is there an easy way for the widget to simple render an element w/ its attributes as parameters?
yes: a helper with 2 ways: using or
( the function tag_element_sample has the name of the element to render )
require_once(dirname(__FILE__) . DS . ‘widget_helper.php’);
class ElementsWidgetHelper extends WidgetHelper
{
//main tags name => array subtags
var $tag=array(‘element:element’,’element:sample’);
//example:
function tag_element_element($attr,$inner_html)
{
$name=$attr[‘name’];
unset($attr[‘name’]);
return $this->view->renderElement($name,$attr);
}
//example:
function tag_element_sample($attr,$inner_html)
{
$name=’my_element’;
return $this->view->renderElement($name,$attr);
}
}
example: <element:element name=”my_element” ar1=”hello world” />
and
<element:sample var1=”hello world” />
Now *that* is what I call slick … thank you!
(That should be included as a snippet on cakeforge by itself, the ElementWidget)
Uploaded to http://cakeforge.org/snippet/detail.php?type=snippet&id=84
You have an bug in the example in hightlightwidget:
* In view:
*
* Hi, I’m john. What’s your name?
* ()
This safes some times for sleepy devs like me 😉
Oups, sorrye posted HTML tags up.
You have an bug in the example in hightlightwidget:
* In view:
* [h:span class=”high” search=”/I’m \w+/”]
* Hi, I’m john. What’s your name?
* [/h:h] (this must be: [/h:span])
This safes some times for sleepy devs like me
[…] What does this mean for the end user, i hear you cry? Well, as Miguel Ros demonstrates in his fantastic example, Custom HTML Tags, it seems to be very possible to write your very own markup, and have CakePHP transform it into valid HTML. This would allow someone to create a library of custom tags, that could be changed into HTML that has specific styles and layouts already associated with it. For example, a custom div with four containing divs could be transformed into a Tab Pane layout, with two divs being tabs, and two being the content in those tabs, with the main div controling the size and postion and so on. […]
In one of the last lines of the HighlightWidgetHelper, there is a syntax error. Replacement with the following code solves the issue:
return preg_replace($expression,”I’m \\1″,$inner_html);
how to output in php html tags like that
Test
I agreed with you
How you think when the economic crisis will end? I wish to make statistics of independent opinions!