2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4 // +----------------------------------------------------------------------+
5 // | Akelos Framework - http://www.akelos.org |
6 // +----------------------------------------------------------------------+
7 // | Copyright (c) 2002-2006, Akelos Media, S.L. & Bermi Ferrer Martinez |
8 // | Released under the GNU Lesser General Public License, see LICENSE.txt|
9 // +----------------------------------------------------------------------+
14 * @author Bermi Ferrer <bermi a.t akelos c.om>
15 * @copyright Copyright (c) 2002-2006, Akelos Media, S.L. http://www.akelos.org
16 * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html>
20 require_once(AK_LIB_DIR
.DS
.'AkActionView'.DS
.'helpers'.DS
.'tag_helper.php');
21 require_once(AK_LIB_DIR
.DS
.'AkActionView'.DS
.'helpers'.DS
.'form_tag_helper.php');
22 require_once(AK_LIB_DIR
.DS
.'AkInflector.php');
25 * Provides a set of methods for working with forms and especially forms related to objects assigned to the template.
26 * The following is an example of a complete form for a person object that works for both creates and updates built
27 * with all the form helpers. The <tt>$person</tt> object was assigned by an action on the controller:
28 * <form action="save_person" method="post">
30 * <?= $form_helper->text_field("person", "name", array("size" => 20)) ?>
33 * <?= $form_helper->password_field("person", "password", array("maxsize" => 20)) ?>
36 * <?= $form_helper->check_box("person", "single") ?>
39 * <?= $form_helper->text_area("person", "description", array("cols" => 20)) ?>
41 * <input type="submit" value="Save" />
46 * <form action="save_person" method="post">
48 * <input type="text" id="person_name" name="person[name]"
49 * size="20" value="<?= $person->name ?>" />
52 * <input type="password" id="person_password" name="person[password]"
53 * size="20" maxsize="20" value="<?= $person->password ?>" />
56 * <input type="checkbox" id="person_single" name="person[single]" value="1" />
59 * <textarea cols="20" rows="40" id="person_description" name="person[description]">
60 * <?= $person->description ?>
63 * <input type="submit" value="Save">
66 * If the object name contains square brackets the id for the object will be inserted. Example:
68 * <?= $form_helper->textfield("person[]", "name") ?>
72 * <input type="text" id="person_<?= $person->id ?>_name" name="person[<?= $person->id ?>][name]" value="<?= $person->name ?>" />
74 * If the helper is being used to generate a repetitive sequence of similar form elements, for example in a partial
75 * used by render_collection_of_partials, the "index" option may come in handy. Example:
77 * <?= $form_helper->text_field("person", "name", "index" => 1) ?>
81 * <input type="text" id="person_1_name" name="person[1][name]" value="<?= $person->name ?>" />
83 * There's also methods for helping to build form tags in $form_options, $date and $active_record
87 class FormHelper
extends AkActionViewHelper
92 * Creates a form and a scope around a specific model object, which is then used as a base for questioning about
93 * values for the fields. Examples:
95 * <?php $f = $form_helper->form_for('person', $Person, array('url' => array('action' => 'update'))); ?>
96 * First name: <?= $f->text_field('first_name'); ?>
97 * Last name : <?= $f->text_field('last_name'); ?>
98 * Biography : <?= $f->text_area('biography'); ?>
99 * Admin? : <?= $f->check_box('admin'); ?>
100 * <?= $f->end_form_tag(); ?>
102 * The form_for yields a form_builder object, in this example as $f, which emulates the API for the stand-alone
103 * FormHelper methods, but without the object name. So instead of <tt>$form_helper->text_field('person', 'name');</tt>,
104 * you get away with <tt>$f->text_field('name');</tt>.
106 * That in itself is a modest increase in comfort. The big news is that form_for allows us to more easily escape the instance
107 * variable convention, so while the stand-alone approach would require <tt>$form_helper->text_field('person', 'name', array('object' => $Person));</tt>
108 * to work with local variables instead of instance ones, the form_for calls remain the same. You simply declare once with
109 * <tt>'person', $Person</tt> and all subsequent field calls save <tt>'person'</tt> and <tt>'object' => $Person</tt>.
111 * Also note that form_for doesn't create an exclusive scope. It's still possible to use both the stand-alone FormHelper methods
112 * and methods from FormTagHelper. Example:
114 * <?php $f = $form_helper->form_for('person', $Person, array('url' => array('action' => 'update'))); ?>
115 * First name: <?= $f->text_field('first_name'); ?>
116 * Last name : <?= $f->text_field('last_name'); ?>
117 * Biography : <?= $f->text_area('person', $Biography); ?>
118 * Admin? : <?= $form_helper->check_box_tag('person[admin]', $Person->company->isAdmin()); ?>
119 * <?= $f->end_form_tag(); ?>
121 * Note: This also works for the methods in FormOptionHelper and DateHelper that are designed to work with an object as base.
122 * Like collection_select and datetime_select.
124 function form_for($object_name, &$object, $options = array())
126 $url_for_options = $options['url'];
127 echo $this->_controller
->form_tag_helper
->form_tag($url_for_options, $options);
128 return $this->fields_for($object_name, $object);
132 * Creates a scope around a specific model object like form_for, but doesn't create the form tags themselves. This makes
133 * fields_for suitable for specifying additional model objects in the same form. Example:
135 * <?php $person_form = $this->form_for('person', $Person, array('url' => array('action'=>'update'))); ?>
136 * First name: <?= $person_form->text_field('first_name'); ?>
137 * Last name : <?= person_form->text_field('last_name'); ?>
139 * <?php $permission_fields = $form_helper->fields_for('permission', $Person->permission); ?>
140 * Admin? : <?= $permission_fields->check_box('admin'); ?>
141 * <?= $person_form->end_form_tag(); ?>
143 * Note: This also works for the methods in FormOptionHelper and DateHelper that are designed to work with an object as base.
144 * Like collection_select and datetime_select.
146 function fields_for($object_name, &$object)
148 return new AkFormHelperBuilder($object_name, $object, $this);
151 function end_form_tag()
156 * Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +column_name+) on an object
157 * assigned to the template (identified by +object+). Additional options on the input tag can be passed as an
158 * array with +options+.
160 * Examples (call, result):
161 * $form_helper->text_field("post", "title", array("size" => 20));
162 * <input type="text" id="post_title" name="post[title]" size="20" value="{post.title}" />
164 function text_field($object_name, $column_name = null, $options = array())
166 return $this->_field($object_name, $column_name, $options,'text');
170 * Works just like text_field, but returns an input tag of the "password" type instead.
172 function password_field($object_name, $column_name = null, $options = array())
174 return $this->_field($object_name, $column_name, $options,'password');
178 * Works just like text_field, but returns an input tag of the "hidden" type instead.
180 function hidden_field($object_name, $column_name = null, $options = array())
182 return $this->_field($object_name, $column_name, $options,'hidden');
186 * Works just like text_field, but returns an input tag of the "file" type instead, which won't have a default value.
188 function file_field($object_name, $column_name = null, $options = array())
190 return $this->_field($object_name, $column_name, $options,'file');
195 * Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +column_name+)
196 * on an object assigned to the template (identified by +object+). Additional options on the input tag can be passed as an
197 * array with +options+.
199 * Example (call, result):
200 * $form_helper->text_area('post', 'body', array('cols' => 20, 'rows' => 40));
201 * <textarea cols="20" rows="40" id="post_body" name="post[body]">
205 function text_area($object_name, $column_name = null, $options = array())
207 return $this->_field($object_name, $column_name, $options,'text_area');
211 * Returns a checkbox tag tailored for accessing a specified attribute (identified by +column_name+) on an object
212 * assigned to the template (identified by +object+). It's intended that +column_name+ returns an integer and if that
213 * integer is above zero, then the checkbox is checked. Additional options on the input tag can be passed as an
214 * array with +options+. The +checked_value+ defaults to 1 while the default +unchecked_value+
215 * is set to 0 which is convenient for boolean values. Usually unchecked checkboxes don't post anything.
216 * We work around this problem by adding a hidden value with the same name as the checkbox.
218 * Example (call, result). Imagine that $Post->validate() returns 1:
219 * $form_helper->check_box("post", "validate");
220 * <input type="checkbox" id="post_validate" name="post[validate]" value="1" checked="checked" />
221 * <input name="post[validated]" type="hidden" value="0" />
223 * Example (call, result). Imagine that $Puppy->gooddog() returns no:
224 * $form_helper->check_box("puppy", "gooddog", array(), "yes", "no");
225 * <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
226 * <input name="puppy[gooddog]" type="hidden" value="no" />
228 function check_box($object_name, $column_name = null, $options = array(), $checked_value = '1', $unchecked_value = '0')
230 return $this->_field($object_name, $column_name, $options,'check_box', $checked_value, $unchecked_value);
234 * Returns a radio button tag for accessing a specified attribute (identified by +column_name+) on an object
235 * assigned to the template (identified by +object+). If the current value of +column_name+ is +tag_value+ the
236 * radio button will be checked. Additional options on the input tag can be passed as an
237 * array with +options+.
238 * Example (call, result). Imagine that $Post->category() returns "PHP":
239 * $form_helper->radio_button("post", "category", "PHP");
240 * $form_helper->radio_button("post", "category", "Ruby");
241 * <input type="radio" id="post_category" name="post[category]" value="PHP" checked="checked" />
242 * <input type="radio" id="post_category" name="post[category]" value="Ruby" />
244 function radio_button($object_name, $column_name = null, $tag_value, $options = array())
246 return $this->_field($object_name, $column_name, $options,'radio_button', $tag_value);
250 * File field auxiliar function
253 function _field($object_name, $column_name, $options = array(), $type, $extra_param_1 = '', $extra_param_2 = '')
255 if(empty($column_name) && isset($this->object_name
)){
256 $column_name = $object_name;
257 $object_name = $this->object_name
;
261 if(isset($options['object'])){
262 if(is_object($options['object'])){
263 $object =& $options['object'];
264 if(empty($this->_remove_object_from_options
)){
265 unset($options['object']);
269 if(empty($object) && !empty($this->object)){
270 $object =& $this->object;
271 //$this->object =& $this->getObject($object_name);
274 $InstanceTag = new AkFormHelperInstanceTag($object_name, $column_name, $this, null, $object);
280 return $InstanceTag->to_input_field_tag($type,$options);
283 return $InstanceTag->to_text_area_tag($options);
286 return $InstanceTag->to_radio_button_tag($extra_param_1, $options);
289 return $InstanceTag->to_check_box_tag($options, $extra_param_1, $extra_param_2);
297 class AkFormHelperInstanceTag
extends TagHelper
299 var $default_field_options = array('size'=>30);
300 var $default_radio_options = array();
301 var $default_text_area_options = array('cols'=>40,'rows'=>20);
302 var $default_date_options = array('discard_type'=>true);
308 //AkFormHelperInstanceTag
310 function AkFormHelperInstanceTag($object_name, $column_name, &$template_object, $local_binding = null, $object = null)
312 $this->object_name
= $object_name;
313 $this->_column_name
= $column_name;
314 $this->_template_object
=& $template_object;
315 $this->_local_binding
= $local_binding;
317 if(empty($object) && !empty($this->_template_object
->_controller
->{$this->object_name
})){
318 $this->object =& $this->_template_object
->_controller
->{$this->object_name
};
320 $this->object =& $object;
323 $_object_name = preg_replace('/\[\]$/','',$this->object_name
);
324 if($_object_name != $this->object_name
){
325 $this->_auto_index
= $this->_template_object
->{AkInflector
::camelize($_object_name)}->id_before_type_cast
;
329 function to_input_field_tag($field_type, $options = array())
331 $options['size'] = !empty($options['size']) ?
$options['size'] : (!empty($options['maxlength']) ?
$options['maxlength'] : $this->default_field_options
['size']);
332 $options = array_merge($this->default_field_options
,$options);
333 if($field_type == 'hidden'){
334 unset($options['size']);
336 $options['type'] = $field_type;
337 if($field_type != 'file'){
338 $options['value'] = !empty($options['value']) ?
$options['value'] : $this->value_before_type_cast();
340 $this->add_default_name_and_id($options);
341 return TagHelper
::tag('input', $options);
344 function to_radio_button_tag($tag_value, $options = array())
346 $options = array_merge($this->default_radio_options
,$options);
347 $options['type'] = 'radio';
348 $options['value'] = $tag_value;
349 if($this->getValue() == $tag_value){
350 $options['checked'] = 'checked';
353 $pretty_tag_value = strtolower(preg_replace('/\W/', '', preg_replace('/\s/', '_',$tag_value)));
354 $options['id'] = $this->_auto_index ?
355 "{$this->object_name}_{$this->_auto_index}_{$this->_column_name}_{$pretty_tag_value}" :
356 "{$this->object_name}_{$this->_column_name}_{$pretty_tag_value}";
357 $this->add_default_name_and_id($options);
358 return TagHelper
::tag('input', $options);
361 function to_text_area_tag($options = array())
363 $options = array_merge($this->default_text_area_options
,$options);
364 $this->add_default_name_and_id($options);
365 return TagHelper
::content_tag('textarea', TagHelper
::escape_once($this->value_before_type_cast()), $options);
368 function to_check_box_tag($options = array(), $checked_value = '1', $unchecked_value = '0')
370 $options['type'] = 'checkbox';
371 $options['value'] = $checked_value;
372 $value = $this->getValue();
374 if (is_numeric($value)){
375 $checked = $value != 0;
376 }elseif (is_string($value)){
377 $checked = $value == $checked_value;
379 $checked = !empty($value);
382 if($checked ||
isset($options['checked']) && $options['checked'] == 'checked'){
383 $options['checked'] = 'checked';
385 unset($options['checked']);
387 $this->add_default_name_and_id($options);
388 return TagHelper
::tag('input', array('name' => $options['name'], 'type' => 'hidden', 'value' => $unchecked_value)).TagHelper
::tag('input', $options);
391 function to_date_tag()
393 require_once(AK_LIB_DIR
.DS
.'AkActionView'.DS
.'helpers'.DS
.'date_helper.php');
394 $defaults = $this->default_date_options
;
395 $date = $this->getValue();
396 $date = !empty($date) ?
$date : Ak
::getDate();
397 return DateHelper
::select_day($date, array_merge($defaults,array('prefix'=>"{$this->object_name}[{$this->_column_name}(3)]"))) .
398 DateHelper
::select_month($date, array_merge($defaults,array('prefix'=>"{$this->object_name}[{$this->_column_name}(2)]"))) .
399 DateHelper
::select_year($date, array_merge($defaults,array('prefix'=>"{$this->object_name}[{$this->_column_name}(1)]")));
402 function to_date_select_tag($options = array())
404 require_once(AK_LIB_DIR
.DS
.'AkActionView'.DS
.'helpers'.DS
.'date_helper.php');
405 $DateHelper =& new DateHelper();
406 $object_name = empty($this->_object_name
) ?
$this->object_name
: $this->_object_name
;
407 if(isset($this->object)){
408 $DateHelper->_object
[$object_name] =& $this->object;
410 return $DateHelper->date_select($object_name, $this->_column_name
, $options);
413 function to_datetime_select_tag($options = array())
415 require_once(AK_LIB_DIR
.DS
.'AkActionView'.DS
.'helpers'.DS
.'date_helper.php');
416 $DateHelper =& new DateHelper();
417 $object_name = empty($this->_object_name
) ?
$this->object_name
: $this->_object_name
;
418 if(isset($this->object)){
419 $DateHelper->_object
[$object_name] =& $this->object;
421 return $DateHelper->datetime_select($object_name, $this->_column_name
, $options);
424 function to_boolean_select_tag($options = array())
426 $this->add_default_name_and_id($options);
428 TagHelper
::_tag_options($options).
429 '><option value="false"'.
430 ($this->getValue() == false ?
' selected' : '').
431 '>'.Ak
::t('False',array(),'helpers/form').'</option><option value="true"'.
432 ($this->getValue() ?
' selected' : '').
433 '>'.Ak
::t('True',array(),'helpers/form').'</option></select>';
436 function to_content_tag($tag_name, $options = array())
438 return TagHelper
::content_tag($tag_name, $this->getValue(), $options);
441 function &getObject($object_name = null)
443 if(!empty($this->object)){
444 return $this->object;
445 }elseif (!empty($this->_template_object
->{$this->object_name
})){
446 return $this->_template_object
->{$this->object_name
};
448 if(!empty($object_name) && !empty($this->_object
[$object_name])){
449 return $this->_object
[$object_name];
451 return $this->object;
456 $object = $this->getObject();
458 return $object->get($this->_column_name
);
462 function value_before_type_cast()
464 $object =& $this->getObject();
466 return !empty($object->{$this->_column_name
.'_before_type_cast'}) ?
467 $object->{$this->_column_name
.'_before_type_cast'} :
468 $object->get($this->_column_name
);
472 function add_default_name_and_id(&$options)
474 if(isset($options['index'])){
475 $options['name'] = empty($options['name']) ?
$this->tag_name_with_index($options['index']) : $options['name'];
476 $options['id'] = empty($options['id']) ?
$this->tag_id_with_index($options['index']) : $options['id'];
477 unset($options['index']);
478 }elseif(!empty($this->_auto_index
)){
479 $options['name'] = empty($options['name'])?
$this->tag_name_with_index($this->_auto_index
) : $options['name'];
480 $options['id'] = empty($options['id']) ?
$this->tag_id_with_index($this->_auto_index
) : $options['id'];
482 $options['name'] = empty($options['name']) ?
$this->tag_name() : $options['name'];
483 $options['id'] = empty($options['id']) ?
$this->tag_id() : $options['id'];
486 if(!empty($options['multiple'])){
487 if(substr($options['name'],-2) != '[]'){
488 $options['name'] = $options['name'].'[]';
495 return "{$this->object_name}[{$this->_column_name}]";
498 function tag_name_with_index($index)
500 return "{$this->object_name}[{$index}][{$this->_column_name}]";
505 return "{$this->object_name}_{$this->_column_name}";
508 function tag_id_with_index($index)
510 return "{$this->object_name}_{$index}_{$this->_column_name}";
514 class AkFormHelperBuilder
extends FormHelper
516 function AkFormHelperBuilder($object_name, &$object, &$template)
518 $this->object_name
= $object_name;
519 $this->object =& $object;
520 $this->template
=& $template;
521 $this->proccessing
= $object_name;
522 $this->template
->_remove_object_from_options
= true;