PageRenderTime 29ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/Morf.php

http://github.com/samsoir/morf
PHP | 522 lines | 275 code | 66 blank | 181 comment | 26 complexity | 3457e152f82d058c06db20e1deca6e84 MD5 | raw file
  1. <?php
  2. /**
  3. * Morf
  4. * Yet another form library
  5. *
  6. * @package default
  7. * @author Sam Clark
  8. * @license GNU Public License v3 http://www.gnu.org/licenses/gpl-3.0.html
  9. */
  10. class Morf_Core
  11. {
  12. // Configuration
  13. protected $config;
  14. // Attributes
  15. protected $attributes = array
  16. (
  17. 'action' => NULL,
  18. 'method' => 'post',
  19. 'id' => NULL,
  20. 'class' => NULL,
  21. 'charset' => 'utf-8',
  22. 'enctype' => 'application/x-www-form-urlencoded',
  23. );
  24. // Form elements
  25. protected $elements;
  26. // Errors
  27. protected $errors = array();
  28. // Post
  29. protected $post = FALSE;
  30. /**
  31. * Factory method for creating a new Morf object. Allows chaining
  32. *
  33. * @param array $config array of key => value pairs for configuration, merges with config file
  34. * @param array $hidden_fields array of key => value pairs for hidden fields to be automatically included
  35. * @return Morf object
  36. * @author Sam Clark
  37. */
  38. public function factory($config = array(), $hidden_fields = array())
  39. {
  40. return new Morf($config, $hidden_fields);
  41. }
  42. /**
  43. * Constructor. Takes configuration as an associative array. Merges with the config/morf.php pre-defined config file
  44. *
  45. * @param array $config array of key => value pairs for configuration, merges with config file
  46. * @param array $hidden_fields array of key => value pairs for hidden fields to be automatically included
  47. * @return void
  48. * @author Sam Clark
  49. */
  50. public function __construct($config = array(), $hidden_fields = array())
  51. {
  52. // Setup configuration
  53. $config += Kohana::config('morf');
  54. $this->config = $config;
  55. $this->_init($this->config['post']);
  56. if ($hidden_fields)
  57. $this->_hidden_fields($hidden_fields);
  58. return;
  59. }
  60. /**
  61. * Object initialisation. Sets up the element array, post fields and form action
  62. *
  63. * @param boolean $post if TRUE, all elements will take values from post, else they will use initial values
  64. * @param array $post using an associative array, control which elements load their values from post
  65. * @return void
  66. * @author Sam Clark
  67. */
  68. protected function _init($post)
  69. {
  70. // Setup elements as an array object
  71. $this->elements = new ArrayObject();
  72. // Manage post
  73. $this->post = $post;
  74. // Create url
  75. $this->attributes['action'] = $this->_set_action();
  76. // Return
  77. return;
  78. }
  79. protected function _hidden_fields($hidden_fields = array())
  80. {
  81. foreach ($hidden_fields as $name => $value)
  82. $this->hidden($name, $value);
  83. return;
  84. }
  85. /**
  86. * Overload the __get() method to allow access to attributes and elements
  87. *
  88. * @param string $key the name of the attribute or form field name
  89. * @return mixed $value the result of the get request
  90. * @author Sam Clark
  91. */
  92. public function __get($key)
  93. {
  94. $value = NULL;
  95. if (array_key_exists($key, $this->attributes))
  96. $value = $this->attributes[$key];
  97. elseif ($this->elements->offsetExists($key))
  98. $value = $this->elements->offsetGet($key);
  99. return $value;
  100. }
  101. /**
  102. * Overload the __set() method to allow access to attributes
  103. *
  104. * @param string $key The attribute name
  105. * @param string $value the value to assign to the attribute
  106. * @param integer $value the value to assign to the attribute
  107. * @param float $value the value to assign to the attribute
  108. * @return void
  109. * @author Sam Clark
  110. */
  111. public function __set($key, $value)
  112. {
  113. if (array_key_exists($key, $this->attributes))
  114. {
  115. $this->attributes[$key] = $value;
  116. }
  117. }
  118. /**
  119. * Overload the __isset() method to allow checking for values
  120. *
  121. * @param string key The key to check
  122. * @return boolean TRUE if value is set, FALSE otherwise
  123. * @author Sam Clark
  124. */
  125. public function __isset($key)
  126. {
  127. $result = FALSE;
  128. if (array_key_exists($key, $this->attributes))
  129. $result = isset($this->attributes[$key]);
  130. elseif ($this->elements->offsetExists($key))
  131. $result = $this->elements->offsetExists($key);
  132. return $result;
  133. }
  134. /**
  135. * Overload the __call() method to give access to new element/fieldset creation, also allows access to attributes to allow chaining
  136. *
  137. * @param string $method The name of the element to create
  138. * @param string $method The name of the attribute to augment
  139. * @param array $args All of the supplied arguments will be in this array
  140. * @return Morf $this returns this object to allow chaining
  141. * @author Sam Clark
  142. */
  143. public function __call($method, $args)
  144. {
  145. // Setup result
  146. $result = $this;
  147. // Elements (this should be always loaded from the config file, not local config)
  148. $elements = Kohana::config('morf.elements');
  149. // Refactor method
  150. $method = strtolower($method);
  151. // Count the number of arguments
  152. $num_args = count($args);
  153. // If method is an existing key
  154. if (array_key_exists($method,$elements))
  155. {
  156. // If the call of selection group type
  157. if (in_array($elements[$method]['type'], array('select', 'checkgroup', 'radiogroup')))
  158. {
  159. // Configuration
  160. $config = $this->_make_config($method, $args[0]);
  161. // Options
  162. $value = isset($args[1]) ? $args[1] : array();
  163. // Config
  164. $selected = isset($args[2]) ? $args[2] : NULL;
  165. }
  166. // Otherwise this is a standard call
  167. else
  168. {
  169. // Name/id
  170. $config = $this->_make_config($method, $args[0]);
  171. // Value
  172. $value = isset($args[1]) ? $args[1] : NULL;
  173. }
  174. // Setup the uid
  175. if (array_key_exists('name', $config))
  176. $uid = $config['name'];
  177. elseif (array_key_exists('id', $config))
  178. $uid = $config['id'];
  179. else
  180. $uid = $this->_generate_name();
  181. switch ($num_args)
  182. {
  183. case 0 :
  184. throw new Kohana_Exception('Morf::__call() no arguments supplied for method : '.$method, $method);
  185. break;
  186. case 1 :
  187. $this->_add_element(Morf_Element::factory($elements[$method]['type'], $config), $uid);
  188. break;
  189. case 2 :
  190. $this->_add_element(Morf_Element::factory($elements[$method]['type'], $config, $value), $uid);
  191. break;
  192. case 3 :
  193. $this->_add_element(Morf_Element::factory($elements[$method]['type'], $config, $value, $selected), $uid);
  194. break;
  195. default :
  196. throw new Kohana_Exception('Morf::__call() too many arguments supplied for method : '.$method, $method);
  197. }
  198. }
  199. elseif (array_key_exists($method, $this->attributes))
  200. {
  201. if ($num_args == 1)
  202. {
  203. $this->attributes[$method] = $args[0];
  204. }
  205. else
  206. {
  207. throw new Kohana_Exception('Morf::__call() to many arguments provided for '.$method, $method);
  208. }
  209. }
  210. else
  211. {
  212. throw new Kohana_Exception('Morf::__call() undefined method called : '.$method, $method);
  213. }
  214. return $result;
  215. }
  216. /**
  217. * Create a new fieldset
  218. *
  219. * @param string $id the id of the fieldset. Used to access the fieldset later
  220. * @param array $attributes an array of attributes ('class', 'id') to apply to the fieldset
  221. * @return Morf $this returns the parent Morf object for chaining
  222. * @author Sam Clark
  223. */
  224. public function fieldset($id, $attributes = array())
  225. {
  226. $attributes += array('id' => $id, 'post' => $this->config['post']);
  227. $fieldset = new Morf_Fieldset($attributes);
  228. $this->_add_element($fieldset, $id);
  229. return $this;
  230. }
  231. /**
  232. * Add an errors array (returned from the Validation library)
  233. *
  234. * @param string $error The field to apply the error to
  235. * @param array $error an associative array of field names and error messages to apply to this Morf object
  236. * @param Validation $error A validation object
  237. * @param string $msg [Optional] Required only if $error is a string
  238. * @return this
  239. * @author Sam Clark
  240. */
  241. public function add_error($error, $msg = NULL)
  242. {
  243. if (is_array($error))
  244. $this->errors += $error;
  245. elseif ($error instanceof Validation_Core)
  246. $this->errors += $error->errors($this->config['errors']['file']);
  247. else
  248. $this->errors[$error] = $msg;
  249. return $this;
  250. }
  251. /**
  252. * Adds a new element to Morf
  253. *
  254. * @param array $element An array of Morf_Elements, if the array isn't associative a name will be autogenerated
  255. * @param Morf_Element $element A single Morf_Element derivative
  256. * @return Morf $this returns the parent Morf object for method chaining
  257. * @author Sam Clark
  258. */
  259. public function add($element)
  260. {
  261. if (is_array($element))
  262. {
  263. foreach ($element as $key => $value)
  264. {
  265. if (ctype_digit($key))
  266. $this->_add_element($element, $this->_generate_name());
  267. else
  268. {
  269. if (isset($element->name))
  270. $this->_add_element($element, $element->name);
  271. else
  272. $this->_add_element($element, $this->_generate_name());
  273. }
  274. }
  275. }
  276. elseif (is_object($element))
  277. {
  278. if (isset($element->name))
  279. $this->_add_element($element, $element->name);
  280. else
  281. $this->_add_element($element, $this->_generate_name());
  282. }
  283. return $this;
  284. }
  285. /**
  286. * Formats the configuration array ready for passing to Morf_Element
  287. *
  288. * @param string $method The name of the element to format
  289. * @param array $config the configuration array to uses
  290. * @return array $config the parsed configuration array
  291. * @author Sam Clark
  292. */
  293. protected function _make_config($method, $config)
  294. {
  295. if (in_array($method, array('submit', 'button')))
  296. {
  297. if ( ! is_array($config))
  298. $config = array('type' => $method, 'id' => $config);
  299. else
  300. $config['type'] = $method;
  301. }
  302. else
  303. {
  304. if ( ! is_array($config))
  305. $config = array('type' => $method, 'name' => $config, 'id' => $config);
  306. else
  307. $config['type'] = $method;
  308. if ( ! array_key_exists('name', $config) AND array_key_exists('id', $config))
  309. {
  310. $config['name'] = $config['id'];
  311. }
  312. elseif ( ! array_key_exists('id', $config) AND array_key_exists('name', $config))
  313. {
  314. $config['id'] = $config['name'];
  315. }
  316. if (empty($config['name']))
  317. $config['name'] = $this->_generate_name();
  318. }
  319. $config['post'] = $this->post;
  320. return $config;
  321. }
  322. /**
  323. * Generates a new id for form elements. Only used if id or name are missing.
  324. * It is highly recommended that you never have to use this method.
  325. *
  326. * @param string $prefix the prefix to use
  327. * @return string The resulting new name
  328. * @author Sam Clark
  329. */
  330. protected function _generate_name($prefix = 'item_')
  331. {
  332. return $prefix.$this->elements->count();
  333. }
  334. /**
  335. * Adds a new element to the element array
  336. *
  337. * @param Morf_Element $element The element to add
  338. * @param Morf_Fieldset $element The fieldset to add
  339. * @param string $name The identifier name for the element
  340. * @return void
  341. * @author Sam Clark
  342. */
  343. protected function _add_element($element, $name)
  344. {
  345. $this->elements->offsetSet($name, $element);
  346. return;
  347. }
  348. /**
  349. * Automatically sets the form action if no action is required
  350. *
  351. * @return string $result The resulting formatted form action url
  352. * @author Sam Clark
  353. */
  354. protected function _set_action()
  355. {
  356. $result = url::current();
  357. if ( ! empty($this->attributes['action']))
  358. {
  359. if(preg_match('/(http:\/\/|https:\/\/)/', $this->attributes['action']))
  360. $result = $this->attributes['action'];
  361. else
  362. $result = url::site($this->attributes['action'], request::protocol());
  363. }
  364. return $result;
  365. }
  366. /**
  367. * Gets the post variables from the post settings.
  368. *
  369. * @param boolean $post TRUE will load all posted variables, FALSE will load none
  370. * @param array $post an associative array of post variables will load the listed post vars
  371. * @return array Resulting array of $_POST variables
  372. * @author Sam Clark
  373. */
  374. protected function _get_post_vars($post = FALSE)
  375. {
  376. $result = array();
  377. if ($post === TRUE)
  378. {
  379. $result = Input::instance()->post();
  380. }
  381. elseif (is_array($post))
  382. {
  383. foreach ($post as $key)
  384. {
  385. $result[$key] = Input::instance()->post($key, NULL);
  386. }
  387. }
  388. return $result;
  389. }
  390. /**
  391. * Formats attributes for the form
  392. *
  393. * @return array the resulting attributes array
  394. * @author Sam Clark
  395. */
  396. protected function _format_attributes()
  397. {
  398. // Create attributes array
  399. $result = array();
  400. foreach ($this->attributes as $key => $value)
  401. {
  402. if (isset($value) AND $key !== 'action')
  403. $result[$key] = $value;
  404. }
  405. return $result;
  406. }
  407. /**
  408. * Handles the objects transformation to string
  409. *
  410. * @return string the rendered result of this form
  411. * @author Sam Clark
  412. */
  413. public function __toString()
  414. {
  415. return $this->render();
  416. }
  417. /**
  418. * Renders the Morf object into an HTML form
  419. *
  420. * @param boolean $print If TRUE render will automatically print the form
  421. * @return string HTML form representation of the Morf object
  422. * @author Sam Clark
  423. */
  424. public function render($print = FALSE)
  425. {
  426. // Load correct view
  427. $view = new View('morf/groups/form');
  428. $element_cache = '';
  429. $render_variables = array
  430. (
  431. 'enctype' => 'application/x-www-form-urlencoded',
  432. 'submit' => FALSE
  433. );
  434. // Foreach element
  435. foreach ($this->elements as $name => $element)
  436. {
  437. $element_cache .= $element->render($render_variables, $this->errors);
  438. }
  439. $this->enctype = $render_variables['enctype'];
  440. if ( ! $render_variables['submit'] AND $this->config['autosubmit'])
  441. $element_cache .= Morf_Element::factory('input', $this->config['autosubmit']['config'], $this->config['autosubmit']['value'])->render($render_variables, $this->errors);
  442. // Create output buffer
  443. $result = form::open($this->action, $this->_format_attributes());
  444. // Apply the rendered elements to the view
  445. $view->elements = $element_cache;
  446. // Render the resulting view
  447. $result .= $view->render();
  448. // Close form
  449. $result .= form::close();
  450. // If print is true, print the output
  451. if ($print)
  452. echo $result;
  453. // Return the resulting output
  454. return $result;
  455. }
  456. } // End Morf_Core