PageRenderTime 465ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Stato/Webflow/Forms/Form.php

https://github.com/goldoraf/stato
PHP | 302 lines | 281 code | 21 blank | 0 comment | 5 complexity | dae264d0814c0530282e1095f6e6fd3a MD5 | raw file
  1. <?php
  2. namespace Stato\Webflow\Forms;
  3. require_once 'Inputs.php';
  4. require_once 'Fields.php';
  5. class Exception extends \Exception {}
  6. class Errors extends \ArrayObject
  7. {
  8. protected $form;
  9. public function __construct(Form $form)
  10. {
  11. $this->form = $form;
  12. parent::__construct(array());
  13. }
  14. public function __toString()
  15. {
  16. $html = "<ul class=\"errorlist\">\n";
  17. foreach ($this as $k => $v) {
  18. if ($k == Form::FORM_WIDE_ERRORS) {
  19. $html.= "<li>$v</li>\n";
  20. } else {
  21. $html.= "<li>".$this->form->{$k}->labelTag." - $v</li>\n";
  22. }
  23. }
  24. $html.= "</ul>";
  25. return $html;
  26. }
  27. }
  28. class Form implements \Iterator
  29. {
  30. const FORM_WIDE_ERRORS = '_all_';
  31. public $errors;
  32. protected $data;
  33. protected $files;
  34. protected $multipart = false;
  35. protected $isBound = false;
  36. protected $fields = array();
  37. protected $cleanedData = array();
  38. protected $initialValues = array();
  39. protected $prefix = null;
  40. protected $fieldDecorator = null;
  41. public function __construct(array $data = null, array $files = null)
  42. {
  43. $this->bind($data, $files);
  44. }
  45. public function __set($name, Field $field)
  46. {
  47. $this->addField($name, $field);
  48. }
  49. public function __get($name)
  50. {
  51. if (!isset($this->{$name}))
  52. throw new Exception('Field not found:'.$name);
  53. return new BoundField($this, $this->fields[$name], $name);
  54. }
  55. public function __isset($name)
  56. {
  57. return array_key_exists($name, $this->fields);
  58. }
  59. public function __toString()
  60. {
  61. return $this->render();
  62. }
  63. public function current()
  64. {
  65. return new BoundField($this, current($this->fields), $this->key());
  66. }
  67. public function key()
  68. {
  69. return key($this->fields);
  70. }
  71. public function next()
  72. {
  73. next($this->fields);
  74. }
  75. public function rewind()
  76. {
  77. reset($this->fields);
  78. }
  79. public function valid()
  80. {
  81. return current($this->fields) !== false;
  82. }
  83. public function addField($name, $field, $options = array())
  84. {
  85. if (!$field instanceof Field) {
  86. $fieldClass = __NAMESPACE__.'\\'.$field.'Field';
  87. if (!class_exists($fieldClass, false))
  88. throw new Exception($fieldClass.' class not found');
  89. $field = new $fieldClass($options);
  90. }
  91. if ($field instanceof FileField) $this->multipart = true;
  92. $this->fields[$name] = $field;
  93. }
  94. public function visibleFields()
  95. {
  96. $visible = array();
  97. foreach ($this as $field) if (!$field->isHidden) $visible[] = $field;
  98. return $visible;
  99. }
  100. public function hiddenFields()
  101. {
  102. $hidden = array();
  103. foreach ($this as $field) if ($field->isHidden) $hidden[] = $field;
  104. return $hidden;
  105. }
  106. public function isBound()
  107. {
  108. return $this->isBound;
  109. }
  110. public function isMultipart()
  111. {
  112. return $this->multipart;
  113. }
  114. public function render($tag = 'p')
  115. {
  116. $openTag = '<'.$tag.'>';
  117. $closeTag = '</'.$tag.'>';
  118. $html = array();
  119. $hiddenFields = array();
  120. foreach ($this->fields as $name => $field) {
  121. $bf = new BoundField($this, $field, $name);
  122. if ($bf->isHidden) {
  123. $hiddenFields[] = $bf->render();
  124. } else {
  125. $err = (!$bf->error) ? '' : '<span class="error">'.$bf->error.'</span>';
  126. $html[] = $openTag.$bf->labelTag.$bf->render().$err.$closeTag;
  127. }
  128. }
  129. if (!empty($hiddenFields)) $html[] = implode("\n", $hiddenFields);
  130. return implode("\n", $html);
  131. }
  132. public function getCleanedData()
  133. {
  134. return $this->cleanedData;
  135. }
  136. public function getCleanedValue($name)
  137. {
  138. return (array_key_exists($name, $this->cleanedData)) ? $this->cleanedData[$name] : null;
  139. }
  140. public function setPrefix($prefix)
  141. {
  142. $this->prefix = $prefix;
  143. }
  144. public function getPrefix()
  145. {
  146. return $this->prefix;
  147. }
  148. public function setInitialValues(array $values)
  149. {
  150. $this->initialValues = $values;
  151. }
  152. public function getInitialValue($name)
  153. {
  154. return (array_key_exists($name, $this->initialValues)) ? $this->initialValues[$name] : null;
  155. }
  156. public function isValid(array $data = null, array $files = null)
  157. {
  158. if (!$this->isBound) $this->bind($data, $files);
  159. if (!$this->isBound) return;
  160. $this->cleanedData = array();
  161. $this->errors = new Errors($this);
  162. foreach ($this->fields as $name => $field) {
  163. $value = (array_key_exists($name, $this->data)) ? $this->data[$name] : null;
  164. try {
  165. $value = $field->clean($value);
  166. $this->cleanedData[$name] = $value;
  167. $cleanMethod = 'clean'.$name;
  168. if (method_exists($this, $cleanMethod)) {
  169. $value = $this->$cleanMethod($value);
  170. $this->cleanedData[$name] = $value;
  171. }
  172. } catch (ValidationError $e) {
  173. $this->errors[$name] = vsprintf(\__($e->getMessage()), $e->getArgs());
  174. $this->cleanedData[$name] = $e->getCleanedValue();
  175. }
  176. }
  177. try {
  178. $this->clean();
  179. } catch (ValidationError $e) {
  180. $this->errors[self::FORM_WIDE_ERRORS] = $e->getMessage();
  181. }
  182. return count($this->errors) === 0;
  183. }
  184. /**
  185. * Hook for doing any extra form-wide cleaning after every field been
  186. * cleaned. Any ValidationError raised by this method will not be
  187. * associated with a particular field.
  188. */
  189. protected function clean()
  190. {
  191. }
  192. protected function bind(array $data = null, array $files = null)
  193. {
  194. $this->isBound = (!is_null($data) || !is_null($files));
  195. $this->data = (!is_null($data)) ? $data : array();
  196. $this->files = (!is_null($files)) ? $files : array();
  197. }
  198. }
  199. class BoundField
  200. {
  201. public $label;
  202. public $labelTag;
  203. public $htmlName;
  204. public $error;
  205. public $helpText;
  206. public $isHidden;
  207. protected $form;
  208. protected $field;
  209. protected $name;
  210. protected $id;
  211. public function __construct(Form $form, Field $field, $name)
  212. {
  213. $this->form = $form;
  214. $this->field = $field;
  215. $this->name = $name;
  216. $this->id = (is_null($prefix = $this->form->getPrefix())) ? $this->name : "{$prefix}_{$name}";
  217. $this->htmlName = (is_null($prefix = $this->form->getPrefix())) ? $this->name : "{$prefix}[{$name}]";
  218. $this->label = (is_null($this->field->label)) ? $this->getLabel() : $this->field->label;
  219. $this->labelTag = $this->getLabelTag();
  220. $this->error = (isset($this->form->errors[$name])) ? $this->form->errors[$name] : false;
  221. $this->helpText = $this->field->helpText;
  222. $this->isHidden = $this->field->getInput()->isHidden();
  223. }
  224. public function __toString()
  225. {
  226. return $this->render();
  227. }
  228. public function render()
  229. {
  230. if (!$this->form->isBound()) {
  231. $value = (!is_null($initial = $this->form->getInitialValue($this->name)))
  232. ? $initial : $this->field->initial;
  233. } else {
  234. $value = $this->form->getCleanedValue($this->name);
  235. }
  236. return $this->field->render($this->htmlName, $value, array('id' => $this->id));
  237. }
  238. protected function getLabel()
  239. {
  240. $label = \__($this->name);
  241. if ($label == $this->name) $label = $this->humanize($this->name);
  242. return $label;
  243. }
  244. protected function getLabelTag()
  245. {
  246. return "<label for=\"{$this->id}\">{$this->label}</label>";
  247. }
  248. protected function humanize($word)
  249. {
  250. return ucfirst(preg_replace('/_/', ' ', preg_replace('/_id/', '', $word)));
  251. }
  252. }