PageRenderTime 48ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/Nette/Forms/Controls/FormControl.php

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