PageRenderTime 62ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/fuel/core/classes/fieldset.php

https://bitbucket.org/arkross/venus
PHP | 600 lines | 345 code | 75 blank | 180 comment | 41 complexity | 8ad1e31746ff141152e883043f78cdcd MD5 | raw file
Possible License(s): MIT, BSD-3-Clause
  1. <?php
  2. /**
  3. * Part of the Fuel framework.
  4. *
  5. * @package Fuel
  6. * @version 1.0
  7. * @author Fuel Development Team
  8. * @license MIT License
  9. * @copyright 2010 - 2011 Fuel Development Team
  10. * @link http://fuelphp.com
  11. */
  12. namespace Fuel\Core;
  13. // ------------------------------------------------------------------------
  14. /**
  15. * Fieldset Class
  16. *
  17. * Define a set of fields that can be used to generate a form or to validate input.
  18. *
  19. * @package Fuel
  20. * @category Core
  21. */
  22. class Fieldset
  23. {
  24. /**
  25. * @var Fieldset
  26. */
  27. protected static $_instance;
  28. /**
  29. * @var array contains references to all instantiations of Fieldset
  30. */
  31. protected static $_instances = array();
  32. /**
  33. * This method is deprecated...use forge() instead.
  34. *
  35. * @deprecated until 1.2
  36. */
  37. public static function factory($name = 'default', array $config = array())
  38. {
  39. logger(\Fuel::L_WARNING, 'This method is deprecated. Please use a forge() instead.', __METHOD__);
  40. return static::forge($name, $config);
  41. }
  42. public static function forge($name = 'default', array $config = array())
  43. {
  44. if ($exists = static::instance($name))
  45. {
  46. \Error::notice('Fieldset with this name exists already, cannot be overwritten.');
  47. return $exists;
  48. }
  49. static::$_instances[$name] = new static($name, $config);
  50. if ($name == 'default')
  51. {
  52. static::$_instance = static::$_instances[$name];
  53. }
  54. return static::$_instances[$name];
  55. }
  56. /**
  57. * Return a specific instance, or the default instance (is created if necessary)
  58. *
  59. * @param string driver id
  60. * @return Fieldset
  61. */
  62. public static function instance($instance = null)
  63. {
  64. if ($instance !== null)
  65. {
  66. if ( ! array_key_exists($instance, static::$_instances))
  67. {
  68. return false;
  69. }
  70. return static::$_instances[$instance];
  71. }
  72. if (static::$_instance === null)
  73. {
  74. static::$_instance = static::forge();
  75. }
  76. return static::$_instance;
  77. }
  78. /**
  79. * @var string instance id
  80. */
  81. protected $name;
  82. /**
  83. * @var string tag used to wrap this instance
  84. */
  85. protected $fieldset_tag = null;
  86. /**
  87. * @var Fieldset instance to which this instance belongs
  88. */
  89. protected $fieldset_parent = null;
  90. /**
  91. * @var array instances that belong to this one
  92. */
  93. protected $fieldset_children = array();
  94. /**
  95. * @var array array of Fieldset_Field objects
  96. */
  97. protected $fields = array();
  98. /**
  99. * @var Validation instance of validation
  100. */
  101. protected $validation;
  102. /**
  103. * @var Form instance of form
  104. */
  105. protected $form;
  106. /**
  107. * @var array configuration array
  108. */
  109. protected $config = array();
  110. /**
  111. * Object constructor
  112. *
  113. * @param string
  114. * @param array
  115. */
  116. protected function __construct($name, array $config = array())
  117. {
  118. if (isset($config['validation_instance']))
  119. {
  120. $this->validation($config['validation_instance']);
  121. unset($config['validation_instance']);
  122. }
  123. if (isset($config['form_instance']))
  124. {
  125. $this->form($config['form_instance']);
  126. unset($config['form_instance']);
  127. }
  128. $this->name = (string) $name;
  129. $this->config = $config;
  130. }
  131. /**
  132. * Get related Validation instance or create it
  133. *
  134. * @param bool|Validation
  135. * @return Validation
  136. */
  137. public function validation($instance = true)
  138. {
  139. if ($instance instanceof Validation)
  140. {
  141. $this->validation = $instance;
  142. return $instance;
  143. }
  144. if (empty($this->validation) and $instance === true)
  145. {
  146. $this->validation = \Validation::forge($this);
  147. }
  148. return $this->validation;
  149. }
  150. /**
  151. * Get related Form instance or create it
  152. *
  153. * @param bool|Form
  154. * @return Form
  155. */
  156. public function form($instance = true)
  157. {
  158. if ($instance instanceof Form)
  159. {
  160. $this->form = $instance;
  161. return $instance;
  162. }
  163. if (empty($this->form) and $instance === true)
  164. {
  165. $this->form = \Form::forge($this);
  166. }
  167. return $this->form;
  168. }
  169. /**
  170. * Set the parent Fieldset instance
  171. *
  172. * @param Fieldset parent fieldset to which this belongs
  173. * @return Fieldset
  174. */
  175. public function set_parent(Fieldset $fieldset)
  176. {
  177. if ( ! empty($this->fieldset_parent))
  178. {
  179. throw new \RuntimeException('Fieldset already has a parent, belongs to "'.$this->parent()->name.'".');
  180. }
  181. $children = $fieldset->children();
  182. while ($child = array_shift($children))
  183. {
  184. if ($child === $this)
  185. {
  186. throw new \RuntimeException('Circular reference detected, adding a Fieldset that\'s already a child as a parent.');
  187. }
  188. $children = array_merge($child->children(), $children);
  189. }
  190. $this->fieldset_parent = $fieldset;
  191. $fieldset->add_child($this);
  192. return $this;
  193. }
  194. /**
  195. * Add a child Fieldset instance
  196. *
  197. * @param Fieldset $fieldset
  198. * @return Fieldset
  199. */
  200. protected function add_child(Fieldset $fieldset)
  201. {
  202. if (is_null($fieldset->fieldset_tag))
  203. {
  204. $fieldset->fieldset_tag = 'fieldset';
  205. }
  206. $this->fieldset_children[$fieldset->name] = $fieldset;
  207. return $this;
  208. }
  209. /**
  210. * Factory for Fieldset_Field objects
  211. *
  212. * @param string
  213. * @param string
  214. * @param array
  215. * @param array
  216. * @return Fieldset_Field
  217. */
  218. public function add($name, $label = '', array $attributes = array(), array $rules = array())
  219. {
  220. if ($name instanceof Fieldset_Field)
  221. {
  222. if ($name->name == '' or $this->field($name->name) !== false)
  223. {
  224. throw new \RuntimeException('Fieldname empty or already exists in this Fieldset: "'.$name->name.'".');
  225. }
  226. $name->set_fieldset($this);
  227. $this->fields[$name->name] = $name;
  228. return $name;
  229. }
  230. elseif ($name instanceof Fieldset)
  231. {
  232. if (empty($name->name) or $this->field($name->name) !== false)
  233. {
  234. throw new \RuntimeException('Fieldset name empty or already exists in this Fieldset: "'.$name->name.'".');
  235. }
  236. $name->set_parent($this);
  237. $this->fields[$name->name] = $name;
  238. return $name;
  239. }
  240. if (empty($name) || (is_array($name) and empty($name['name'])))
  241. {
  242. throw new \InvalidArgumentException('Cannot create field without name.');
  243. }
  244. // Allow passing the whole config in an array, will overwrite other values if that's the case
  245. if (is_array($name))
  246. {
  247. $attributes = $name;
  248. $label = isset($name['label']) ? $name['label'] : '';
  249. $rules = isset($name['rules']) ? $name['rules'] : array();
  250. $name = $name['name'];
  251. }
  252. // Check if it exists already, if so: return and give notice
  253. if ($field = $this->field($name))
  254. {
  255. \Error::notice('Field with this name exists already in this fieldset: "'.$name.'".');
  256. return $field;
  257. }
  258. $field = new \Fieldset_Field($name, $label, $attributes, $rules, $this);
  259. $this->fields[$name] = $field;
  260. return $field;
  261. }
  262. /**
  263. * Get Field instance
  264. *
  265. * @param string|null field name or null to fetch an array of all
  266. * @param bool whether to get the fields array or flattened array
  267. * @return Fieldset_Field|false returns false when field wasn't found
  268. */
  269. public function field($name = null, $flatten = false)
  270. {
  271. if ($name === null)
  272. {
  273. if ( ! $flatten)
  274. {
  275. return $this->fields;
  276. }
  277. $fields = $this->fields;
  278. foreach ($this->fieldset_children as $fs_name => $fieldset)
  279. {
  280. \Arr::insert_after_key($fields, $fieldset->field(null, true), $fs_name);
  281. unset($fields[$fs_name]);
  282. }
  283. return $fields;
  284. }
  285. if ( ! array_key_exists($name, $this->fields))
  286. {
  287. foreach ($this->fieldset_children as $fieldset)
  288. {
  289. if (($field = $fieldset->field($name) !== false))
  290. {
  291. return $field;
  292. }
  293. }
  294. return false;
  295. }
  296. return $this->fields[$name];
  297. }
  298. /**
  299. * Add a model's fields
  300. * The model must have a method "set_form_fields" that takes this Fieldset instance
  301. * and adds fields to it.
  302. *
  303. * @param string|Object either a full classname (including full namespace) or object instance
  304. * @param array|Object array or object that has the exactly same named properties to populate the fields
  305. * @param string method name to call on model for field fetching
  306. * @return Fieldset this, to allow chaining
  307. */
  308. public function add_model($class, $instance = null, $method = 'set_form_fields')
  309. {
  310. // Add model to validation callables for validation rules
  311. $this->validation()->add_callable($class);
  312. if ((is_string($class) and is_callable($callback = array('\\'.$class, $method)))
  313. || is_callable($callback = array($class, $method)))
  314. {
  315. $instance ? call_user_func($callback, $this, $instance) : call_user_func($callback, $this);
  316. }
  317. return $this;
  318. }
  319. /**
  320. * Sets a config value on the fieldset
  321. *
  322. * @param string
  323. * @param mixed
  324. * @return Fieldset this, to allow chaining
  325. */
  326. public function set_config($config, $value = null)
  327. {
  328. $config = is_array($config) ? $config : array($config => $value);
  329. foreach ($config as $key => $value)
  330. {
  331. $this->config[$key] = $value;
  332. }
  333. return $this;
  334. }
  335. /**
  336. * Get a single or multiple config values by key
  337. *
  338. * @param string|array a single key or multiple in an array, empty to fetch all
  339. * @param mixed default output when config wasn't set
  340. * @return mixed|array a single config value or multiple in an array when $key input was an array
  341. */
  342. public function get_config($key = null, $default = null)
  343. {
  344. if ($key === null)
  345. {
  346. return $this->config;
  347. }
  348. if (is_array($key))
  349. {
  350. $output = array();
  351. foreach ($key as $k)
  352. {
  353. $output[$k] = array_key_exists($k, $this->config) ? $this->config[$k] : $default;
  354. }
  355. return $output;
  356. }
  357. return array_key_exists($key, $this->config) ? $this->config[$key] : $default;
  358. }
  359. /**
  360. * Populate the form's values using an input array or object
  361. *
  362. * @param array|object
  363. * @param bool
  364. * @return Fieldset this, to allow chaining
  365. */
  366. public function populate($input, $repopulate = false)
  367. {
  368. $fields = $this->field(null, true);
  369. foreach ($fields as $f)
  370. {
  371. if (is_array($input) or $input instanceof \ArrayAccess)
  372. {
  373. if (isset($input[$f->name]))
  374. {
  375. $f->set_value($input[$f->name], true);
  376. }
  377. }
  378. elseif (is_object($input) and property_exists($input, $f->name))
  379. {
  380. $f->set_value($input->{$f->name}, true);
  381. }
  382. }
  383. // Optionally overwrite values using post/get
  384. if ($repopulate)
  385. {
  386. $this->repopulate();
  387. }
  388. return $this;
  389. }
  390. /**
  391. * Set all fields to the input from get or post (depends on the form method attribute)
  392. *
  393. * @param array|object input for initial population of fields, this is deprecated - you should use populate() instea
  394. * @return Fieldset this, to allow chaining
  395. */
  396. public function repopulate($deprecated = null)
  397. {
  398. // The following usage will be deprecated in Fuel 1.1
  399. if ( ! is_null($deprecated))
  400. {
  401. return $this->populate($deprecated, true);
  402. }
  403. $fields = $this->field(null, true);
  404. foreach ($fields as $f)
  405. {
  406. // Don't repopulate the CSRF field
  407. if ($f->name === \Config::get('security.csrf_token_key', 'fuel_csrf_token'))
  408. {
  409. continue;
  410. }
  411. if (($value = $f->input()) !== null)
  412. {
  413. $f->set_value($value, true);
  414. }
  415. }
  416. return $this;
  417. }
  418. /**
  419. * Build the fieldset HTML
  420. *
  421. * @return string
  422. */
  423. public function build($action = null)
  424. {
  425. $attributes = $this->get_config('form_attributes');
  426. if ($action and ($this->fieldset_tag == 'form' or empty($this->fieldset_tag)))
  427. {
  428. $attributes['action'] = $action;
  429. }
  430. $open = ($this->fieldset_tag == 'form' or empty($this->fieldset_tag))
  431. ? $this->form()->open($attributes).PHP_EOL
  432. : $this->form()->{$this->fieldset_tag.'_open'}($attributes);
  433. $fields_output = '';
  434. foreach ($this->field() as $f)
  435. {
  436. $fields_output .= $f->build().PHP_EOL;
  437. }
  438. $close = ($this->fieldset_tag == 'form' or empty($this->fieldset_tag))
  439. ? $this->form()->close($attributes).PHP_EOL
  440. : $this->form()->{$this->fieldset_tag.'_close'}($attributes);
  441. $template = $this->form()->get_config((empty($this->fieldset_tag) ? 'form' : $this->fieldset_tag).'_template',
  442. "\n\t\t{open}\n\t\t<table>\n{fields}\n\t\t</table>\n\t\t{close}\n");
  443. $template = str_replace(array('{form_open}', '{open}', '{fields}', '{form_close}', '{close}'),
  444. array($open, $open, $fields_output, $close, $close),
  445. $template);
  446. return $template;
  447. }
  448. /**
  449. * Magic method toString that will build this as a form
  450. *
  451. * @return string
  452. */
  453. public function __toString()
  454. {
  455. return $this->build();
  456. }
  457. /**
  458. * Return parent Fieldset
  459. *
  460. * @return Fieldset
  461. */
  462. public function parent()
  463. {
  464. return $this->fieldset_parent;
  465. }
  466. /**
  467. * Return the child fieldset instances
  468. *
  469. * @return array
  470. */
  471. public function children()
  472. {
  473. return $this->fieldset_children;
  474. }
  475. /**
  476. * Alias for $this->validation()->input()
  477. *
  478. * @return mixed
  479. */
  480. public function input($field = null)
  481. {
  482. return $this->validation()->input($field);
  483. }
  484. /**
  485. * Alias for $this->validation()->validated()
  486. *
  487. * @return mixed
  488. */
  489. public function validated($field = null)
  490. {
  491. return $this->validation()->validated($field);
  492. }
  493. /**
  494. * Alias of $this->error() for backwards compatibility
  495. *
  496. * @depricated Remove in v1.2
  497. */
  498. public function errors($field = null)
  499. {
  500. logger(\Fuel::L_WARNING, 'This method is deprecated. Please use Fieldset::error() instead.', __METHOD__);
  501. return $this->error($field);
  502. }
  503. /**
  504. * Alias for $this->validation()->error()
  505. *
  506. * @return Validation_Error|array
  507. */
  508. public function error($field = null)
  509. {
  510. return $this->validation()->error($field);
  511. }
  512. /**
  513. * Alias for $this->validation()->show_errors()
  514. *
  515. * @return string
  516. */
  517. public function show_errors(array $config = array())
  518. {
  519. return $this->validation()->show_errors($config);
  520. }
  521. }