Rearranging scripts to reduce the hassle of updating local application whenever scrip...
[akelos.git] / lib / AkActionView / helpers / active_record_helper.php
blob01e4606eedf7c98342e435a55469f453718c2f35
1 <?php
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 // +----------------------------------------------------------------------+
11 /**
12 * @package ActionView
13 * @subpackage Helpers
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.'form_helper.php');
22 /**
23 * The Active Record Helper makes it easier to create forms for records kept in instance variables. The most far-reaching is the form
24 * method that creates a complete form for all the basic content types of the record (not associations or aggregations, though). This
25 * is a great of making the record quickly available for editing, but likely to prove lackluster for a complicated real-world form.
26 * In that case, it's better to use the input method and the specialized form methods from the FormHelper
28 class ActiveRecordHelper extends AkActionViewHelper
31 /**
32 * Returns a default input tag for the type of object returned by the method. Example
33 * (title is a VARCHAR column and holds "Hello World"):
34 * $active_record_helper->input('post', 'title'); =>
35 * <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
37 function input($record_name, $method, $options = array())
39 $InstanceTag = new ActiveRecordInstanceTag($record_name, $method, $this);
40 return $InstanceTag->to_tag($options);
43 /**
44 * Returns an entire form with input tags and everything for a specified Active Record object. Example
45 * (post is a new record that has a title using VARCHAR and a body using TEXT):
46 * $active_record_helper->form('post'); =>
47 * <form action='/post/create' method='post'>
48 * <p>
49 * <label for="post_title">Title</label><br />
50 * <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
51 * </p>
52 * <p>
53 * <label for="post_body">Body</label><br />
54 * <textarea cols="40" id="post_body" name="post[body]" rows="20">
55 * Back to the hill and over it again!
56 * </textarea>
57 * </p>
58 * <input type='submit' value='Create' />
59 * </form>
61 * It's possible to specialize the form builder by using a different action name and by supplying another
62 * block renderer that will be evaled by PHP.
63 * Example (entry is a new record that has a message attribute using VARCHAR):
65 * $active_record_helper->form('entry', array('action'=>'sign','input_block' =>
66 * '<p><?=AkInflector::humanize($column)?>: <?=$this->input($record_name, $column)?></p><br />'
67 * );
69 * <form action='/post/sign' method='post'>
70 * Message:
71 * <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /><br />
72 * <input type='submit' value='Sign' />
73 * </form>
75 function form($record_name, $options = array())
77 $record =& $this->_controller->$record_name;
79 $options['action'] = !empty($options['action']) ? $options['action'] : ($record->isNewRecord() ? 'create' : 'update');
81 $action = $this->_controller->urlFor(array('action'=>$options['action'], 'id' => $record->getId()));
83 $submit_value = !empty($options['submit_value']) ? $options['submit_value'] : strtoupper(preg_replace('/[^\w]/','',$options['action']));
85 $contents = '';
86 $contents .= $record->isNewRecord() ? '' : $this->_controller->form_helper->hidden_field($record_name, 'id');
87 $contents .= $this->all_input_tags($record, $record_name, $options);
88 $contents .= FormTagHelper::submit_tag(Ak::t($submit_value,array(),'helpers/active_record'));
89 return TagHelper::content_tag('form', $contents, array('action'=>$action, 'method'=>'post',
90 'enctype'=> !empty($options['multipart']) ? 'multipart/form-data': null ));
93 /**
94 * Returns a string containing the error message attached to the +method+ on the +object+, if one exists.
95 * This error message is wrapped in a DIV tag, which can be specialized to include both a +prepend_text+ and +append_text+
96 * to properly introduce the error and a +css_class+ to style it accordingly. Examples (post has an error message
97 * "can't be empty" on the title attribute):
99 * <?= $active_record_helper->error_message_on('post', 'title'); ?>
100 * <div class="formError">can't be empty</div>
102 * <?=$active_record_helper->error_message_on('post','title','Title simply ', " (or it won't work)", 'inputError') ?> =>
103 * <div class="inputError">Title simply can't be empty (or it won't work)</div>
105 function error_message_on($object_name, $method, $prepend_text = '', $append_text = '', $css_class = 'formError')
107 if($errors = $this->_controller->$object_name->getErrorsOn($method)){
108 return TagHelper::content_tag('div', $prepend_text.(is_array($errors) ? array_shift($errors) : $errors).$append_text, array('class'=>$css_class));
110 return '';
114 * Returns a string with a div containing all the error messages for the object located as an instance variable by the name
115 * of <tt>object_name</tt>. This div can be tailored by the following options:
117 * * <tt>header_tag</tt> - Used for the header of the error div (default: h2)
118 * * <tt>id</tt> - The id of the error div (default: errorExplanation)
119 * * <tt>class</tt> - The class of the error div (default: errorExplanation)
121 * NOTE: This is a pre-packaged presentation of the errors with embedded strings and a certain HTML structure. If what
122 * you need is significantly different from the default presentation, it makes plenty of sense to access the $object->getErrors()
123 * instance yourself and set it up. View the source of this method to see how easy it is.
125 function error_messages_for($object_name, $options = array())
127 $object =& $this->_controller->$object_name;
128 if($object->hasErrors()){
129 $error_list = '<ul>';
130 foreach ($object->getFullErrorMessages() as $field=>$errors){
131 foreach ($errors as $error){
132 $error_list .= TagHelper::content_tag('li',$error);
135 $error_list .= '</ul>';
136 return
137 TagHelper::content_tag('div',
138 TagHelper::content_tag(
139 (!empty($options['header_tag']) ? $options['header_tag'] :'h2'),
140 Ak::t('%number_of_errors %errors prohibited this %object_name from being saved' ,
141 array('%number_of_errors'=>$object->countErrors(),'%errors'=>Ak::t(AkInflector::conditionalPlural($object->countErrors(),'error'),array(),'helpers/active_record'),
142 '%object_name'=>Ak::t(AkInflector::humanize($object->getModelName()),array(),'helpers/active_record'))
143 ,'helpers/active_record')).
144 TagHelper::content_tag('p', Ak::t('There were problems with the following fields:',array(),'helpers/active_record')).
145 $error_list,
146 array('id'=> !empty($options['id']) ? $options['id'] : 'errorExplanation', 'class' => !empty($options['class']) ? $options['class'] : 'errorExplanation')
152 function all_input_tags(&$record, $record_name, $options = array())
154 $input_block = !empty($options['input_block']) ? $options['input_block'] : $this->default_input_block();
155 $columns = empty($options['columns']) ? array_keys($record->getContentColumns()) : $options['columns'];
156 $result = '';
157 foreach ($columns as $column){
158 ob_start();
159 eval("?>$input_block<?php ");
160 $result .= ob_get_clean()."\n";
162 return $result;
165 function default_input_block()
167 return '<p><label for="<?=$record_name?>_<?=$column?>"><?=AkInflector::humanize($column)?></label><br /><?=$this->input($record_name, $column)?></p>';
171 class ActiveRecordInstanceTag extends AkFormHelperInstanceTag
173 var $method_name;
175 function ActiveRecordInstanceTag($object_name, $column_name, &$template_object)
177 $column_name = $this->method_name = $this->_getColumnName($column_name, $object_name, $template_object);
178 $this->AkFormHelperInstanceTag($object_name, $column_name, $template_object);
181 function to_tag($options = array())
183 $options = array_merge($this->object->getErrorsOn($this->method_name)==false?array():array("class"=>"fieldError"), $options);
185 switch ($this->get_column_type()) {
187 case 'string':
188 $field_type = strstr($this->method_name,'password') ? 'password' : 'text';
189 return $this->to_input_field_tag($field_type, $options);
190 break;
192 case 'text':
193 return $this->to_text_area_tag($options);
194 break;
196 case 'integer':
197 case 'float':
198 return $this->to_input_field_tag('text', $options);
199 break;
201 case 'date':
202 return $this->to_date_select_tag($options);
203 break;
205 case 'datetime':
206 case 'timestamp':
207 return $this->to_datetime_select_tag($options);
208 break;
210 case 'boolean':
211 return $this->to_check_box_tag($options);
212 break;
214 default:
215 return '';
216 break;
220 function tag($name, $options)
222 if($this->object->hasErrors()){
223 return $this->error_wrapping($this->tag_without_error_wrapping($name, $options), $this->object->getErrorsOn($this->method_name));
224 }else{
225 return $this->tag_without_error_wrapping($name, $options);
229 function tag_without_error_wrapping($name, $options)
231 return parent::tag($name, $options);
235 function content_tag($name, $value, $options)
237 if($this->object->hasErrors()){
238 return $this->error_wrapping($this->content_tag_without_error_wrapping($name, $value, $options), $this->object->getErrorsOn($this->method_name));
239 }else{
240 return $this->content_tag_without_error_wrapping($name, $value, $options);
244 function content_tag_without_error_wrapping($name, $value, $options)
246 return parent::content_tag($name, $value, $options);
249 function to_date_select_tag($options = array())
251 if($this->object->hasErrors()){
252 return $this->error_wrapping($this->to_date_select_tag_without_error_wrapping($options), $this->object->getErrorsOn($this->method_name));
253 }else{
254 return $this->to_date_select_tag_without_error_wrapping($options);
258 function to_date_select_tag_without_error_wrapping($options = array())
260 return parent::to_date_select_tag($options);
263 function to_datetime_select_tag($options = array())
265 if($this->object->hasErrors()){
266 return $this->error_wrapping($this->to_datetime_select_tag_without_error_wrapping($options), $this->object->getErrorsOn($this->method_name));
267 }else{
268 return $this->to_datetime_select_tag_without_error_wrapping($options);
272 function to_datetime_select_tag_without_error_wrapping($options = array())
274 return parent::to_datetime_select_tag($options);
277 function to_check_box_tag($options = array())
279 if($this->object->hasErrors()){
280 return $this->error_wrapping($this->to_check_box_tag_without_error_wrapping($options), $this->object->getErrorsOn($this->method_name));
281 }else{
282 return $this->to_check_box_tag_without_error_wrapping($options);
286 function to_check_box_tag_without_error_wrapping($options = array())
288 return parent::to_check_box_tag($options);
291 function error_wrapping($html_tag, $has_error)
293 return $has_error ? "<div class=\"fieldWithErrors\">$html_tag</div>" : $html_tag;
296 function error_message()
298 return $this->object->getErrorsOn($this->method_name);
301 function get_column_type()
303 return $this->object->getColumnType($this->method_name);
306 function _getColumnName($column_name, $object_name, &$template_object)
308 $object =& $template_object->_controller->{$object_name};
309 $internationalized_columns = $object->getInternationalizedColumns();
310 if(!empty($internationalized_columns[$column_name])) {
311 $current_locale = $object->getCurrentLocale();
312 if(in_array($current_locale, $internationalized_columns[$column_name])) {
313 $column_name = $current_locale.'_'.$column_name;
316 return $column_name;