PageRenderTime 30ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/Nette/Forms/Controls/FormControl.php

https://code.google.com/
PHP | 631 lines | 248 code | 151 blank | 232 comment | 22 complexity | f93e2669bddb63895e5063b0925fa05a MD5 | raw file
Possible License(s): Apache-2.0, GPL-2.0
  1. <?php
  2. /**
  3. * This file is part of the Nette Framework.
  4. *
  5. * Copyright (c) 2004, 2010 David Grudl (http://davidgrudl.com)
  6. *
  7. * This source file is subject to the "Nette license", and/or
  8. * GPL license. For more information please see http://nette.org
  9. */
  10. namespace Nette\Forms;
  11. use Nette,
  12. Nette\Web\Html;
  13. /**
  14. * Base class that implements the basic functionality common to form controls.
  15. *
  16. * @author David Grudl
  17. *
  18. * @property-read Form $form
  19. * @property-read mixed $control
  20. * @property-read mixed $label
  21. * @property-read string $htmlName
  22. * @property string $htmlId
  23. * @property-read array $options
  24. * @property Nette\ITranslator $translator
  25. * @property mixed $value
  26. * @property-read Nette\Web\Html $controlPrototype
  27. * @property-read Nette\Web\Html $labelPrototype
  28. * @property-read Rules $rules
  29. * @property-read array $errors
  30. * @property bool $disabled
  31. * @property bool $rendered
  32. * @property bool $required
  33. */
  34. abstract class FormControl extends Nette\Component implements IFormControl
  35. {
  36. /** @var string */
  37. public static $idMask = 'frm%s-%s';
  38. /** @var string textual caption or label */
  39. public $caption;
  40. /** @var mixed unfiltered control value */
  41. protected $value;
  42. /** @var Nette\Web\Html control element template */
  43. protected $control;
  44. /** @var Nette\Web\Html label element template */
  45. protected $label;
  46. /** @var array */
  47. private $errors = array();
  48. /** @var bool */
  49. private $disabled = FALSE;
  50. /** @var string */
  51. private $htmlId;
  52. /** @var string */
  53. private $htmlName;
  54. /** @var Rules */
  55. private $rules;
  56. /** @var Nette\ITranslator */
  57. private $translator = TRUE; // means autodetect
  58. /** @var array user options */
  59. private $options = array();
  60. /**
  61. * @param string caption
  62. */
  63. public function __construct($caption = NULL)
  64. {
  65. $this->monitor('Nette\Forms\Form');
  66. parent::__construct();
  67. $this->control = Html::el('input');
  68. $this->label = Html::el('label');
  69. $this->caption = $caption;
  70. $this->rules = new Rules($this);
  71. }
  72. /**
  73. * This method will be called when the component becomes attached to Form.
  74. * @param IComponent
  75. * @return void
  76. */
  77. protected function attached($form)
  78. {
  79. if (!$this->disabled && $form instanceof Form && $form->isAnchored() && $form->isSubmitted()) {
  80. $this->htmlName = NULL;
  81. $this->loadHttpData();
  82. }
  83. }
  84. /**
  85. * Returns form.
  86. * @param bool throw exception if form doesn't exist?
  87. * @return Form
  88. */
  89. public function getForm($need = TRUE)
  90. {
  91. return $this->lookup('Nette\Forms\Form', $need);
  92. }
  93. /**
  94. * Returns name of control within a Form & INamingContainer scope.
  95. * @return string
  96. */
  97. public function getHtmlName()
  98. {
  99. if ($this->htmlName === NULL) {
  100. $s = '';
  101. $name = $this->getName();
  102. $obj = $this->lookup('Nette\Forms\INamingContainer', TRUE);
  103. while (!($obj instanceof Form)) {
  104. $s = "[$name]$s";
  105. $name = $obj->getName();
  106. $obj = $obj->lookup('Nette\Forms\INamingContainer', TRUE);
  107. }
  108. $name .= $s;
  109. if ($name === 'submit') {
  110. throw new \InvalidArgumentException("Form control name 'submit' is not allowed due to JavaScript limitations.");
  111. }
  112. $this->htmlName = $name;
  113. }
  114. return $this->htmlName;
  115. }
  116. /**
  117. * Changes control's HTML id.
  118. * @param string new ID, or FALSE or NULL
  119. * @return FormControl provides a fluent interface
  120. */
  121. public function setHtmlId($id)
  122. {
  123. $this->htmlId = $id;
  124. return $this;
  125. }
  126. /**
  127. * Returns control's HTML id.
  128. * @return string
  129. */
  130. public function getHtmlId()
  131. {
  132. if ($this->htmlId === FALSE) {
  133. return NULL;
  134. } elseif ($this->htmlId === NULL) {
  135. $this->htmlId = sprintf(self::$idMask, $this->getForm()->getName(), $this->getHtmlName());
  136. $this->htmlId = str_replace(array('[]', '[', ']'), array('', '-', ''), $this->htmlId);
  137. }
  138. return $this->htmlId;
  139. }
  140. /**
  141. * Sets user-specific option.
  142. * Common options:
  143. * - 'rendered' - indicate if method getControl() have been called
  144. * - 'required' - indicate if ':required' rule has been applied
  145. * - 'description' - textual or Html object description (recognized by ConventionalRenderer)
  146. * @param string key
  147. * @param mixed value
  148. * @return FormControl provides a fluent interface
  149. */
  150. public function setOption($key, $value)
  151. {
  152. if ($value === NULL) {
  153. unset($this->options[$key]);
  154. } else {
  155. $this->options[$key] = $value;
  156. }
  157. return $this;
  158. }
  159. /**
  160. * Returns user-specific option.
  161. * @param string key
  162. * @param mixed default value
  163. * @return mixed
  164. */
  165. final public function getOption($key, $default = NULL)
  166. {
  167. return isset($this->options[$key]) ? $this->options[$key] : $default;
  168. }
  169. /**
  170. * Returns user-specific options.
  171. * @return array
  172. */
  173. final public function getOptions()
  174. {
  175. return $this->options;
  176. }
  177. /********************* translator ****************d*g**/
  178. /**
  179. * Sets translate adapter.
  180. * @param Nette\ITranslator
  181. * @return FormControl provides a fluent interface
  182. */
  183. public function setTranslator(Nette\ITranslator $translator = NULL)
  184. {
  185. $this->translator = $translator;
  186. return $this;
  187. }
  188. /**
  189. * Returns translate adapter.
  190. * @return Nette\ITranslator|NULL
  191. */
  192. final public function getTranslator()
  193. {
  194. if ($this->translator === TRUE) {
  195. return $this->getForm(FALSE) ? $this->getForm()->getTranslator() : NULL;
  196. }
  197. return $this->translator;
  198. }
  199. /**
  200. * Returns translated string.
  201. * @param string
  202. * @param int plural count
  203. * @return string
  204. */
  205. public function translate($s, $count = NULL)
  206. {
  207. $translator = $this->getTranslator();
  208. return $translator === NULL || $s == NULL ? $s : $translator->translate($s, $count); // intentionally ==
  209. }
  210. /********************* interface IFormControl ****************d*g**/
  211. /**
  212. * Sets control's value.
  213. * @param mixed
  214. * @return FormControl provides a fluent interface
  215. */
  216. public function setValue($value)
  217. {
  218. $this->value = $value;
  219. return $this;
  220. }
  221. /**
  222. * Returns control's value.
  223. * @return mixed
  224. */
  225. public function getValue()
  226. {
  227. return $this->value;
  228. }
  229. /**
  230. * Sets control's default value.
  231. * @param mixed
  232. * @return FormControl provides a fluent interface
  233. */
  234. public function setDefaultValue($value)
  235. {
  236. $form = $this->getForm(FALSE);
  237. if (!$form || !$form->isAnchored() || !$form->isSubmitted()) {
  238. $this->setValue($value);
  239. }
  240. return $this;
  241. }
  242. /**
  243. * Loads HTTP data.
  244. * @return void
  245. */
  246. public function loadHttpData()
  247. {
  248. $path = explode('[', strtr(str_replace(array('[]', ']'), '', $this->getHtmlName()), '.', '_'));
  249. $this->setValue(Nette\ArrayTools::get($this->getForm()->getHttpData(), $path));
  250. }
  251. /**
  252. * Disables or enables control.
  253. * @param bool
  254. * @return FormControl provides a fluent interface
  255. */
  256. public function setDisabled($value = TRUE)
  257. {
  258. $this->disabled = (bool) $value;
  259. return $this;
  260. }
  261. /**
  262. * Is control disabled?
  263. * @return bool
  264. */
  265. public function isDisabled()
  266. {
  267. return $this->disabled;
  268. }
  269. /********************* rendering ****************d*g**/
  270. /**
  271. * Generates control's HTML element.
  272. * @return Nette\Web\Html
  273. */
  274. public function getControl()
  275. {
  276. $this->setOption('rendered', TRUE);
  277. $control = clone $this->control;
  278. $control->name = $this->getHtmlName();
  279. $control->disabled = $this->disabled;
  280. $control->id = $this->getHtmlId();
  281. return $control;
  282. }
  283. /**
  284. * Generates label's HTML element.
  285. * @param string
  286. * @return Nette\Web\Html
  287. */
  288. public function getLabel($caption = NULL)
  289. {
  290. $label = clone $this->label;
  291. $label->for = $this->getHtmlId();
  292. if ($caption !== NULL) {
  293. $label->setText($this->translate($caption));
  294. } elseif ($this->caption instanceof Html) {
  295. $label->add($this->caption);
  296. } else {
  297. $label->setText($this->translate($this->caption));
  298. }
  299. return $label;
  300. }
  301. /**
  302. * Returns control's HTML element template.
  303. * @return Nette\Web\Html
  304. */
  305. final public function getControlPrototype()
  306. {
  307. return $this->control;
  308. }
  309. /**
  310. * Returns label's HTML element template.
  311. * @return Nette\Web\Html
  312. */
  313. final public function getLabelPrototype()
  314. {
  315. return $this->label;
  316. }
  317. /**
  318. * Sets 'rendered' indicator.
  319. * @param bool
  320. * @return FormControl provides a fluent interface
  321. * @deprecated
  322. */
  323. public function setRendered($value = TRUE)
  324. {
  325. $this->setOption('rendered', $value);
  326. return $this;
  327. }
  328. /**
  329. * Does method getControl() have been called?
  330. * @return bool
  331. * @deprecated
  332. */
  333. public function isRendered()
  334. {
  335. return !empty($this->options['rendered']);
  336. }
  337. /********************* rules ****************d*g**/
  338. /**
  339. * Adds a validation rule.
  340. * @param mixed rule type
  341. * @param string message to display for invalid data
  342. * @param mixed optional rule arguments
  343. * @return FormControl provides a fluent interface
  344. */
  345. public function addRule($operation, $message = NULL, $arg = NULL)
  346. {
  347. $this->rules->addRule($operation, $message, $arg);
  348. return $this;
  349. }
  350. /**
  351. * Adds a validation condition a returns new branch.
  352. * @param mixed condition type
  353. * @param mixed optional condition arguments
  354. * @return Rules new branch
  355. */
  356. public function addCondition($operation, $value = NULL)
  357. {
  358. return $this->rules->addCondition($operation, $value);
  359. }
  360. /**
  361. * Adds a validation condition based on another control a returns new branch.
  362. * @param IFormControl form control
  363. * @param mixed condition type
  364. * @param mixed optional condition arguments
  365. * @return Rules new branch
  366. */
  367. public function addConditionOn(IFormControl $control, $operation, $value = NULL)
  368. {
  369. return $this->rules->addConditionOn($control, $operation, $value);
  370. }
  371. /**
  372. * @return Rules
  373. */
  374. final public function getRules()
  375. {
  376. return $this->rules;
  377. }
  378. /**
  379. * Makes control mandatory.
  380. * @param string error message
  381. * @return FormControl provides a fluent interface
  382. * @deprecated
  383. */
  384. final public function setRequired($message = NULL)
  385. {
  386. $this->rules->addRule(Form::FILLED, $message);
  387. return $this;
  388. }
  389. /**
  390. * Is control mandatory?
  391. * @return bool
  392. * @deprecated
  393. */
  394. final public function isRequired()
  395. {
  396. return !empty($this->options['required']);
  397. }
  398. /**
  399. * New rule or condition notification callback.
  400. * @param Rule
  401. * @return void
  402. */
  403. public function notifyRule(Rule $rule)
  404. {
  405. if (is_string($rule->operation) && strcasecmp($rule->operation, ':filled') === 0) {
  406. $this->setOption('required', TRUE);
  407. }
  408. }
  409. /********************* validation ****************d*g**/
  410. /**
  411. * Equal validator: are control's value and second parameter equal?
  412. * @param IFormControl
  413. * @param mixed
  414. * @return bool
  415. */
  416. public static function validateEqual(IFormControl $control, $arg)
  417. {
  418. $value = $control->getValue();
  419. foreach ((is_array($value) ? $value : array($value)) as $val) {
  420. foreach ((is_array($arg) ? $arg : array($arg)) as $item) {
  421. if ((string) $val === (string) ($item instanceof IFormControl ? $item->value : $item)) {
  422. return TRUE;
  423. }
  424. }
  425. }
  426. return FALSE;
  427. }
  428. /**
  429. * Filled validator: is control filled?
  430. * @param IFormControl
  431. * @return bool
  432. */
  433. public static function validateFilled(IFormControl $control)
  434. {
  435. return (string) $control->getValue() !== ''; // NULL, FALSE, '' ==> FALSE
  436. }
  437. /**
  438. * Valid validator: is control valid?
  439. * @param IFormControl
  440. * @return bool
  441. */
  442. public static function validateValid(IFormControl $control)
  443. {
  444. return $control->rules->validate(TRUE);
  445. }
  446. /**
  447. * Adds error message to the list.
  448. * @param string error message
  449. * @return void
  450. */
  451. public function addError($message)
  452. {
  453. if (!in_array($message, $this->errors, TRUE)) {
  454. $this->errors[] = $message;
  455. }
  456. $this->getForm()->addError($message);
  457. }
  458. /**
  459. * Returns errors corresponding to control.
  460. * @return array
  461. */
  462. public function getErrors()
  463. {
  464. return $this->errors;
  465. }
  466. /**
  467. * @return bool
  468. */
  469. public function hasErrors()
  470. {
  471. return (bool) $this->errors;
  472. }
  473. /**
  474. * @return void
  475. */
  476. public function cleanErrors()
  477. {
  478. $this->errors = array();
  479. }
  480. }