Fixing content type ordering when content_type is not defined.
[akelos.git] / lib / AkActionView / helpers / active_record_helper.php
bloba60e7948bf0be88ab59d8beebd076559b00bdec7
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 $text = $prepend_text.(is_array($errors) ? array_shift($errors) : $errors).$append_text;
109 return TagHelper::content_tag('div', Ak::t($text,array(),'helpers/active_record'), array('class'=>$css_class));
111 return '';
115 * Returns a string with a div containing all the error messages for the object located as an instance variable by the name
116 * of <tt>object_name</tt>. This div can be tailored by the following options:
118 * * <tt>header_tag</tt> - Used for the header of the error div (default: h2)
119 * * <tt>id</tt> - The id of the error div (default: errorExplanation)
120 * * <tt>class</tt> - The class of the error div (default: errorExplanation)
122 * NOTE: This is a pre-packaged presentation of the errors with embedded strings and a certain HTML structure. If what
123 * you need is significantly different from the default presentation, it makes plenty of sense to access the $object->getErrors()
124 * instance yourself and set it up. View the source of this method to see how easy it is.
126 function error_messages_for($object_name, $options = array())
128 $object =& $this->_controller->$object_name;
129 if($object->hasErrors()){
130 $error_list = '<ul>';
131 foreach ($object->getFullErrorMessages() as $field=>$errors){
132 foreach ($errors as $error){
133 $error_list .= TagHelper::content_tag('li',Ak::t($error,array(),'helpers/active_record'));
136 $error_list .= '</ul>';
137 return
138 TagHelper::content_tag('div',
139 TagHelper::content_tag(
140 (!empty($options['header_tag']) ? $options['header_tag'] :'h2'),
141 Ak::t('%number_of_errors %errors prohibited this %object_name from being saved' ,
142 array('%number_of_errors'=>$object->countErrors(),'%errors'=>Ak::t(AkInflector::conditionalPlural($object->countErrors(),'error'),array(),'helpers/active_record'),
143 '%object_name'=>Ak::t(AkInflector::humanize($object->getModelName()),array(),'helpers/active_record'))
144 ,'helpers/active_record')).
145 TagHelper::content_tag('p', Ak::t('There were problems with the following fields:',array(),'helpers/active_record')).
146 $error_list,
147 array('id'=> !empty($options['id']) ? $options['id'] : 'errorExplanation', 'class' => !empty($options['class']) ? $options['class'] : 'errorExplanation')
153 function all_input_tags(&$record, $record_name, $options = array())
155 $input_block = !empty($options['input_block']) ? $options['input_block'] : $this->default_input_block();
156 $columns = empty($options['columns']) ? array_keys($record->getContentColumns()) : $options['columns'];
157 $result = '';
158 foreach ($columns as $column){
159 ob_start();
160 eval("?>$input_block<?php ");
161 $result .= ob_get_clean()."\n";
163 return $result;
166 function default_input_block()
168 return '<p><label for="<?=$record_name?>_<?=$column?>"><?=AkInflector::humanize($column)?></label><br /><?=$this->input($record_name, $column)?></p>';
172 class ActiveRecordInstanceTag extends AkFormHelperInstanceTag
174 var $method_name;
176 function ActiveRecordInstanceTag($object_name, $column_name, &$template_object)
178 $column_name = $this->method_name = $this->_getColumnName($column_name, $object_name, $template_object);
179 $this->AkFormHelperInstanceTag($object_name, $column_name, $template_object);
182 function to_tag($options = array())
184 $options = array_merge($this->object->getErrorsOn($this->method_name)==false?array():array("class"=>"fieldError"), $options);
186 switch ($this->get_column_type()) {
188 case 'string':
189 $field_type = strstr($this->method_name,'password') ? 'password' : 'text';
190 return $this->to_input_field_tag($field_type, $options);
191 break;
193 case 'text':
194 return $this->to_text_area_tag($options);
195 break;
197 case 'integer':
198 case 'float':
199 return $this->to_input_field_tag('text', $options);
200 break;
202 case 'date':
203 return $this->to_date_select_tag($options);
204 break;
206 case 'datetime':
207 case 'timestamp':
208 return $this->to_datetime_select_tag($options);
209 break;
211 case 'boolean':
212 return $this->to_check_box_tag($options);
213 break;
215 default:
216 return '';
217 break;
221 function tag($name, $options)
223 if($this->object->hasErrors()){
224 return $this->error_wrapping($this->tag_without_error_wrapping($name, $options), $this->object->getErrorsOn($this->method_name));
225 }else{
226 return $this->tag_without_error_wrapping($name, $options);
230 function tag_without_error_wrapping($name, $options)
232 return parent::tag($name, $options);
236 function content_tag($name, $value, $options)
238 if($this->object->hasErrors()){
239 return $this->error_wrapping($this->content_tag_without_error_wrapping($name, $value, $options), $this->object->getErrorsOn($this->method_name));
240 }else{
241 return $this->content_tag_without_error_wrapping($name, $value, $options);
245 function content_tag_without_error_wrapping($name, $value, $options)
247 return parent::content_tag($name, $value, $options);
250 function to_date_select_tag($options = array())
252 if($this->object->hasErrors()){
253 return $this->error_wrapping($this->to_date_select_tag_without_error_wrapping($options), $this->object->getErrorsOn($this->method_name));
254 }else{
255 return $this->to_date_select_tag_without_error_wrapping($options);
259 function to_date_select_tag_without_error_wrapping($options = array())
261 return parent::to_date_select_tag($options);
264 function to_datetime_select_tag($options = array())
266 if($this->object->hasErrors()){
267 return $this->error_wrapping($this->to_datetime_select_tag_without_error_wrapping($options), $this->object->getErrorsOn($this->method_name));
268 }else{
269 return $this->to_datetime_select_tag_without_error_wrapping($options);
273 function to_datetime_select_tag_without_error_wrapping($options = array())
275 return parent::to_datetime_select_tag($options);
278 function to_check_box_tag($options = array())
280 if($this->object->hasErrors()){
281 return $this->error_wrapping($this->to_check_box_tag_without_error_wrapping($options), $this->object->getErrorsOn($this->method_name));
282 }else{
283 return $this->to_check_box_tag_without_error_wrapping($options);
287 function to_check_box_tag_without_error_wrapping($options = array())
289 return parent::to_check_box_tag($options);
292 function error_wrapping($html_tag, $has_error)
294 return $has_error ? "<div class=\"fieldWithErrors\">$html_tag</div>" : $html_tag;
297 function error_message()
299 return $this->object->getErrorsOn($this->method_name);
302 function get_column_type()
304 return $this->object->getColumnType($this->method_name);
307 function _getColumnName($column_name, $object_name, &$template_object)
309 $object =& $template_object->_controller->{$object_name};
310 $internationalized_columns = $object->getInternationalizedColumns();
311 if(!empty($internationalized_columns[$column_name])) {
312 $current_locale = $object->getCurrentLocale();
313 if(in_array($current_locale, $internationalized_columns[$column_name])) {
314 $column_name = $current_locale.'_'.$column_name;
317 return $column_name;