PHPonTrax
[ class tree: PHPonTrax ] [ index: PHPonTrax ] [ all elements ]

Source for file active_record_helper.php

Documentation is available at active_record_helper.php

  1. <?php
  2. /**
  3. * File containing ActiveRecordHelper class and support functions
  4. *
  5. * (PHP 5)
  6. *
  7. * @package PHPonTrax
  8. * @version $Id$
  9. * @copyright (c) 2005 John Peterson
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining
  12. * a copy of this software and associated documentation files (the
  13. * "Software"), to deal in the Software without restriction, including
  14. * without limitation the rights to use, copy, modify, merge, publish,
  15. * distribute, sublicense, and/or sell copies of the Software, and to
  16. * permit persons to whom the Software is furnished to do so, subject to
  17. * the following conditions:
  18. *
  19. * The above copyright notice and this permission notice shall be
  20. * included in all copies or substantial portions of the Software.
  21. *
  22. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  23. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  24. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  25. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  26. * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  27. * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  28. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  29. *
  30. * @package PHPonTrax
  31. */
  32.  
  33. /**
  34. * @todo Document this class
  35. */
  36. class ActiveRecordHelper extends Helpers {
  37. /**
  38. * Whether to generate scaffolding HTML
  39. *
  40. * Set to true in {@link form_scaffolding.phtml}. If true
  41. * generate HTML scaffold otherwise generate final HTML
  42. * @var boolean
  43. */
  44. public $scaffolding = false;
  45.  
  46. /**
  47. * Returns a default input tag for the type of object returned by the method. Example
  48. * (title is a VARCHAR column and holds "Hello World"):
  49. * input("post", "title") =>
  50. * <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
  51. * @uses to_tag()
  52. */
  53. function input($object_name, $attribute_name, $options = array()) {
  54. return $this->to_tag($object_name, $attribute_name, $options);
  55. }
  56. /**
  57. * @todo Document this method
  58. * @uses to_scaffold_tag()
  59. */
  60. function input_scaffolding($object_name, $attribute_name, $options = array()) {
  61. return $this->to_scaffold_tag($object_name, $attribute_name, $options);
  62. }
  63.  
  64. /**
  65. * Returns an entire form with input tags and everything for a specified Active Record object. Example
  66. * (post is a new record that has a title using VARCHAR and a body using TEXT):
  67. * form("post") =>
  68. * <form action='/post/create' method='post'>
  69. * <p>
  70. * <label for="post_title">Title</label><br />
  71. * <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
  72. * </p>
  73. * <p>
  74. * <label for="post_body">Body</label><br />
  75. * <textarea cols="40" id="post_body" name="post[body]" rows="20">
  76. * Back to the hill and over it again!
  77. * </textarea>
  78. * </p>
  79. * <input type='submit' value='Create' />
  80. * </form>
  81. *
  82. * It's possible to specialize the form builder by using a different action name and by supplying another
  83. * block renderer. Example (entry is a new record that has a message attribute using VARCHAR):
  84. *
  85. * form("entry", array('action' => "sign", 'input_block' =>
  86. * 'foreach($record->content_columns() as $column_name => $column) $contents .= Inflector::humanize($column_name) . ": " . input($record, $column) . "<br />"')) =>
  87. *
  88. * <form action='/post/sign' method='post'>
  89. * Message:
  90. * <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /><br />
  91. * <input type='submit' value='Sign' />
  92. * </form>
  93. *
  94. * It's also possible to add additional content to the form by giving it a block, such as:
  95. *
  96. * form("entry", array('action' => "sign", 'block' =>
  97. * content_tag("b", "Department") .
  98. * collection_select("department", "id", $departments, "id", "name"))
  99. * )
  100. * @uses all_input_tags()
  101. * @uses content_tag()
  102. * @uses Helpers::object()
  103. */
  104. function form($record_name, $options = array()) {
  105. $record = $this->object($record_name);
  106. $options["action"] = $options[":action"] ? $options[":action"] : $record->is_new_record() ? "add" : "save";
  107. $action = url_for(array(':action' => $options[':action'], ':id' => $record->id));
  108. $submit_value = (isset($options['submit_value']) ? $options['submit_value'] : ucfirst(preg_replace('/[^\w]/', '', $options[':action'])));
  109.  
  110. $contents = '';
  111. if(!$record->is_new_record()) $contents .= hidden_field($record_name, 'id');
  112. $contents .= $this->all_input_tags($record, $record_name, $options);
  113. if(isset($options['block'])) $contents .= eval($options['block']);
  114. $contents .= "<br>".submit_tag($submit_value)."<br><br>";
  115.  
  116. return $this->content_tag('form', $contents, array('action' => $action, 'method' => 'post'));
  117. }
  118.  
  119. /**
  120. * Returns a string containing the error message attached to the +method+ on the +object+, if one exists.
  121. * This error message is wrapped in a DIV tag, which can be specialized to include both a +prepend_text+ and +append_text+
  122. * to properly introduce the error and a +css_class+ to style it accordingly. Examples (post has an error message
  123. * "can't be empty" on the title attribute):
  124. *
  125. * <?= error_message_on("post", "title") ?> =>
  126. * <div class="formError">can't be empty</div>
  127. *
  128. * <?= error_message_on "post", "title", "Title simply ", " (or it won't work)", "inputError" ?> =>
  129. * <div class="inputError">Title simply can't be empty (or it won't work)</div>
  130. * @uses attribute_name
  131. * @uses controller_object
  132. * @uses content_tag()
  133. * @uses object_name
  134. */
  135. function error_message_on($object_name, $attribute_name, $prepend_text = "", $append_text = "", $css_class = "formError") {
  136. $this->object_name = $object_name;
  137. $this->attribute_name = $attribute_name;
  138. $object = $this->controller_object->$object_name;
  139. if($errors = $object->errors[$attribute_name]) {
  140. return $this->content_tag("div", $prepend_text . (is_array($errors) ? current($errors) : $errors) . $append_text, array('class' => $css_class));
  141. }
  142. }
  143.  
  144. /**
  145. * Returns a string with a div containing all the error messages for the object located as an instance variable by the name
  146. * of <tt>object_name</tt>. This div can be tailored by the following options:
  147. *
  148. * <tt>header_tag</tt> - Used for the header of the error div (default: h2)
  149. * <tt>id</tt> - The id of the error div (default: errorExplanation)
  150. * <tt>class</tt> - The class of the error div (default: errorExplanation)
  151. * @param mixed object_name The name of a PHP class, or
  152. * an object instance of that class
  153. * @param string[] options Set of options: 'header_tag', 'id', 'class'
  154. * @uses content_tag()
  155. * @uses object_name
  156. * @uses Inflector::humanize()
  157. */
  158. function error_messages_for($object_name, $options = array()) {
  159. if(is_object($object_name)) {
  160. $object_name = get_class($object_name);
  161. //echo "object name:".$object_name;
  162. }
  163. $this->object_name = $object_name;
  164. $object = $this->controller_object->$object_name;
  165. if(!empty($object->errors)) {
  166. $errors = ($num_errors = count($object->errors)) > 1 ? "$num_errors errors" : "$num_errors error";
  167. return $this->content_tag("div",
  168. $this->content_tag(
  169. (isset($options['header_tag']) ? $options['header_tag'] : "h2"),
  170. $errors .
  171. " prohibited this " . Inflector::humanize($object_name) . " from being saved"
  172. ) .
  173. $this->content_tag("p", "There were problems with the following fields:") .
  174. $this->content_tag("ul", array_reduce($object->errors, create_function('$v,$w', 'return ($v ? $v : "") . content_tag("li", $w);'), '')),
  175. array("id" => (isset($options['id']) ? $options['id'] : "ErrorExplanation"), "class" => (isset($options['class']) ? $options['class'] : "ErrorExplanation"))
  176. );
  177. }
  178. }
  179.  
  180. /**
  181. * @todo Document this method
  182. * @uses default_input_block()
  183. */
  184. function all_input_tags($record, $record_name, $options) {
  185. //if($record_name) $this->object_name = $record_name;
  186. $input_block = (isset($options['input_block']) ? $options['input_block'] : $this->default_input_block());
  187. $contents = '';
  188. if(is_array($record->content_columns)) {
  189. foreach($record->content_columns as $column) {
  190. //$contents .= "<p><label for=\"".$record_name."_".$column['name']."\">";
  191. //$contents .= Inflector::humanize($column['name']) . ":</label><br />";
  192. //$contents .= input($record_name, $column['name']) . "</p>\n";
  193. if(!in_array($column['name'], $record->primary_keys)) {
  194. eval($input_block) . "\n";
  195. }
  196. }
  197. }
  198. return $contents;
  199. }
  200.  
  201. /**
  202. * @todo Document this method
  203. * @uses scaffolding
  204. * @uses input_scaffolding()
  205. */
  206. function default_input_block() {
  207. if($this->scaffolding) {
  208. return '$contents .= "<p><label for=\"{$record_name}_{$column[\'name\']}\">" . Inflector::humanize($column[\'name\']) . ":</label><br/>\n<?= " . input_scaffolding($record_name, $column[\'name\']) . " ?></p>\n";';
  209. } else {
  210. return '$contents .= "<p><label for=\"{$record_name}_{$column[\'name\']}\">" . Inflector::humanize($column[\'name\']) . ":</label><br/>\n" . input($record_name, $column[\'name\']) . "</p>\n";';
  211. }
  212. }
  213. /**
  214. * @todo Document this method
  215. *
  216. * @param string object_name Name of an ActiveRecord subclass
  217. * @param string attribute_name Name of an attribute of $object_name
  218. * @param string[] options
  219. * @uses attribute_name
  220. * @uses column_type()
  221. * @uses error_wrapping()
  222. * @uses object_name
  223. * @uses DateHelper::to_date_select_tag()
  224. * @uses FormHelper::to_boolean_select_tag()
  225. * @uses FormHelper::to_input_field_tag()
  226. * @uses FormHelper::to_text_area_tag()
  227. * @uses to_datetime_select_tag()
  228. * @uses object()
  229. */
  230. function to_tag($object_name, $attribute_name, $options = array()) {
  231. $this->object_name = $object_name;
  232. $this->attribute_name = $attribute_name;
  233. $form = new FormHelper($object_name, $attribute_name);
  234. switch($this->column_type()) {
  235. case 'string':
  236. case 'varchar':
  237. case 'varchar2':
  238. $field_type = (eregi("password", $this->attribute_name) ? "password" : "text");
  239. $results = $form->to_input_field_tag($field_type, $options);
  240. break;
  241.  
  242. case 'text':
  243. case 'blob':
  244. $results = $form->to_text_area_tag($options);
  245. break;
  246.  
  247. case 'integer':
  248. case 'int':
  249. case 'number':
  250. case 'float':
  251. case 'real':
  252. $results = $form->to_input_field_tag("text", $options);
  253. break;
  254.  
  255. case 'date':
  256. $form = new DateHelper($object_name, $attribute_name);
  257. $results = $form->to_date_select_tag($options);
  258. break;
  259.  
  260. case 'datetime':
  261. case 'timestamp':
  262. $results = $this->to_datetime_select_tag($options);
  263. break;
  264.  
  265. case 'boolean':
  266. case 'bool':
  267. $results = $form->to_boolean_select_tag($options);
  268. break;
  269.  
  270. }
  271. if(count($this->object()->errors)) {
  272. $results = $this->error_wrapping($results, $this->object()->errors[$this->attribute_name]);
  273. }
  274. return $results;
  275. }
  276.  
  277. /**
  278. * @todo Document this method
  279. *
  280. * @uses attribute_name
  281. * @uses column_type()
  282. * @uses error_wrapping
  283. * @uses object()
  284. * @uses object_name
  285. */
  286. function to_scaffold_tag($object_name, $attribute_name, $options = array()) {
  287. $this->object_name = $object_name;
  288. $this->attribute_name = $attribute_name;
  289. switch($this->column_type()) {
  290. case 'string':
  291. case 'varchar':
  292. case 'varchar2':
  293. $field_type = (eregi("password", $this->attribute_name) ? "password" : "text");
  294. $results = $field_type."_field(\"$object_name\", \"$attribute_name\")";
  295. break;
  296.  
  297. case 'text':
  298. case 'blob':
  299. $results = "text_area(\"$object_name\", \"$attribute_name\")";
  300. break;
  301.  
  302. case 'integer':
  303. case 'int':
  304. case 'number':
  305. case 'float':
  306. case 'real':
  307. $results = "text_field(\"$object_name\", \"$attribute_name\")";
  308. break;
  309.  
  310. case 'date':
  311. $results = "date_select(\"$object_name\", \"$attribute_name\")";
  312. break;
  313.  
  314. case 'year':
  315. $results = "year_select(\"$object_name\", \"$attribute_name\")";
  316. break;
  317.  
  318. case 'datetime':
  319. case 'timestamp':
  320. $results = "datetime_select(\"$object_name\", \"$attribute_name\")";
  321. break;
  322.  
  323. case 'time':
  324. $results = "time_select(\"$object_name\", \"$attribute_name\")";
  325. break;
  326.  
  327. case 'boolean':
  328. case 'bool':
  329. $results = "boolean_select(\"$object_name\", \"$attribute_name\")";
  330. break;
  331.  
  332. default:
  333. echo "No case statement for ".$this->column_type()."\n";
  334. }
  335. if(count($this->object()->errors)) {
  336. $results = $this->error_wrapping($results,
  337. $this->object()->errors[$this->attribute_name]);
  338. }
  339. return $results;
  340. }
  341.  
  342. /**
  343. * @todo Document this method
  344. *
  345. * @uses tag()
  346. */
  347. function tag_without_error_wrapping() {
  348. $args = func_get_args();
  349. return call_user_func_array(array(parent, 'tag'), $args);
  350. }
  351.  
  352. /**
  353. * @todo Document this method
  354. *
  355. * @uses error_wrapping()
  356. * @uses object()
  357. * @uses tag_without_error_wrapping()
  358. */
  359. function tag($name, $options = array()) {
  360. if(count($this->object()->errors)) {
  361. return $this->error_wrapping($this->tag_without_error_wrapping($name, $options), $this->object()->errors[$this->attribute_name]);
  362. } else {
  363. return $this->tag_without_error_wrapping($name, $options);
  364. }
  365. }
  366.  
  367. /**
  368. * @todo Document this method
  369. *
  370. * @uses content_tag()
  371. */
  372. function content_tag_without_error_wrapping() {
  373. $args = func_get_args();
  374. return call_user_func_array('content_tag', $args);
  375. }
  376.  
  377. /**
  378. * @todo Document this method
  379. *
  380. * @uses object()
  381. * @uses error_wrapping()
  382. * @uses content_tag_without_error_wrapping()
  383. */
  384. function content_tag($name, $value, $options = array()) {
  385. if (count($this->object()->errors)) {
  386. return $this->error_wrapping(
  387. $this->content_tag_without_error_wrapping($name, $value, $options),
  388. array_key_exists($this->attribute_name,$this->object()->errors)
  389. ? true : false);
  390. } else {
  391. return $this->content_tag_without_error_wrapping($name, $value,
  392. $options);
  393. }
  394. }
  395.  
  396. /**
  397. * @todo Document this method
  398. *
  399. * @uses object_name
  400. * @uses attribute_name
  401. * @uses DateHelper::to_date_select_tag()
  402. */
  403. function to_date_select_tag_without_error_wrapping() {
  404. $form = new DateHelper($this->object_name, $this->attribute_name);
  405. $args = func_get_args();
  406. return call_user_func_array(array($form, 'to_date_select_tag'), $args);
  407. }
  408.  
  409. /**
  410. * @todo Document this method
  411. */
  412. function to_date_select_tag($options = array()) {
  413. if (count($this->object()->errors)) {
  414. return $this->error_wrapping($this->to_date_select_tag_without_error_wrapping($options), $this->object()->errors[$this->attribute_name]);
  415. } else {
  416. return $this->to_date_select_tag_without_error_wrapping($options);
  417. }
  418. }
  419.  
  420. /**
  421. * @todo Document this method
  422. *
  423. * @uses attribute_name
  424. * @uses object_name
  425. * @uses DateHelper::to_datetime_select_tag()
  426. */
  427. function to_datetime_select_tag_without_error_wrapping() {
  428. $form = new DateHelper($this->object_name, $this->attribute_name);
  429. $args = func_get_args();
  430. return call_user_func_array(array($form, 'to_datetime_select_tag'),
  431. $args);
  432. }
  433.  
  434. /**
  435. * @todo Document this method
  436. *
  437. * @uses attribute_name
  438. * @uses error_wrapping()
  439. * @uses object()
  440. * @uses to_datetime_select_tag_without_error_wrapping
  441. */
  442. function to_datetime_select_tag($options = array()) {
  443. if (count($this->object()->errors)) {
  444. return $this->error_wrapping($this->to_datetime_select_tag_without_error_wrapping($options), $this->object()->errors[$this->attribute_name]);
  445. } else {
  446. return $this->to_datetime_select_tag_without_error_wrapping($options);
  447. }
  448. }
  449.  
  450. /**
  451. * @todo Document this method
  452. *
  453. * @param string $html_tag
  454. * @param boolean $has_error
  455. */
  456. function error_wrapping($html_tag, $has_error) {
  457. return ($has_error ? '<span class="fieldWithErrors">' . $html_tag . '</span>' : $html_tag);
  458. }
  459.  
  460. /**
  461. * @todo Document this method
  462. *
  463. * @uses attribute_name
  464. * @uses object()
  465. */
  466. function error_message() {
  467. return $this->object()->errors[$this->attribute_name];
  468. }
  469.  
  470. /**
  471. * @todo Document this method
  472. *
  473. * @uses attribute_name
  474. * @uses object()
  475. * @uses ActiveRecord::column_for_attribute()
  476. */
  477. function column_type() {
  478. $column = $this->object()->column_for_attribute($this->attribute_name);
  479. return $column['type'];
  480. }
  481.  
  482. }
  483.  
  484. /**
  485. * Avialble functions for use in views
  486. * error_message_on($object, $attribute_name, $prepend_text = "", $append_text = "", $css_class = "formError")
  487. * @uses ActiveRecordHelper::error_message_on()
  488. */
  489. function error_message_on() {
  490. $ar_helper = new ActiveRecordHelper();
  491. $args = func_get_args();
  492. return call_user_func_array(array($ar_helper, 'error_message_on'), $args);
  493. }
  494.  
  495. /**
  496. * error_messages_for($object_name, $options = array())
  497. * @uses ActiveRecordHelper::error_messages_for()
  498. */
  499. function error_messages_for() {
  500. $ar_helper = new ActiveRecordHelper();
  501. $args = func_get_args();
  502. return call_user_func_array(array($ar_helper, 'error_messages_for'), $args);
  503. }
  504.  
  505. /**
  506. * form($record_name, $options = array())
  507. * @uses ActiveRecordHelper::form()
  508. */
  509. function form() {
  510. $ar_helper = new ActiveRecordHelper();
  511. $args = func_get_args();
  512. return call_user_func_array(array($ar_helper, 'form'), $args);
  513. }
  514.  
  515. /**
  516. * Returns a default input tag for the type of object returned by the method. Example
  517. * (title is a VARCHAR column and holds "Hello World"):
  518. * input("post", "title") =>
  519. * <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
  520. * @uses ActiveRecordHelper::input()
  521. */
  522. function input() {
  523. $ar_helper = new ActiveRecordHelper();
  524. $args = func_get_args();
  525. return call_user_func_array(array($ar_helper, 'input'), $args);
  526. }
  527.  
  528. /**
  529. *
  530. * @uses ActiveRecordHelper::input_scaffolding()
  531. */
  532. function input_scaffolding() {
  533. $ar_helper = new ActiveRecordHelper();
  534. $args = func_get_args();
  535. return call_user_func_array(array($ar_helper, 'input_scaffolding'), $args);
  536. }
  537.  
  538. // -- set Emacs parameters --
  539. // Local variables:
  540. // tab-width: 4
  541. // c-basic-offset: 4
  542. // c-hanging-comment-ender-p: nil
  543. // indent-tabs-mode: nil
  544. // End:
  545. ?>

Documentation generated on Thu, 04 May 2006 19:47:15 -0600 by phpDocumentor 1.3.0RC4