PageRenderTime 44ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Illuminate/Html/FormBuilder.php

https://github.com/guilex/framework
PHP | 751 lines | 287 code | 111 blank | 353 comment | 29 complexity | 233a5373bee039741be5b9d9795841e8 MD5 | raw file
  1. <?php namespace Illuminate\Html;
  2. use Illuminate\Routing\UrlGenerator;
  3. use Illuminate\Html\HtmlBuilder as Html;
  4. use Illuminate\Session\Store as Session;
  5. class FormBuilder {
  6. /**
  7. * The HTML builder instance.
  8. *
  9. * @var Illuminate\Html\HtmlBuilder
  10. */
  11. protected $html;
  12. /**
  13. * The URL generator instance.
  14. *
  15. * @var Illuminate\Routing\UrlGenerator $url
  16. */
  17. protected $url;
  18. /**
  19. * The CSRF token used by the form builder.
  20. *
  21. * @var string
  22. */
  23. protected $csrfToken;
  24. /**
  25. * The session store implementation.
  26. *
  27. * @var Illuminate\Support\Contracts\SessionStoreInterface
  28. */
  29. protected $session;
  30. /**
  31. * The current model instance for the form.
  32. *
  33. * @var mixed
  34. */
  35. protected $model;
  36. /**
  37. * An array of label names we've created.
  38. *
  39. * @var array
  40. */
  41. protected $labels = array();
  42. /**
  43. * The registered form builder macros.
  44. *
  45. * @var array
  46. */
  47. protected $macros = array();
  48. /**
  49. * The reserved form open attributes.
  50. *
  51. * @var array
  52. */
  53. protected $reserved = array('method', 'url', 'route', 'action', 'files');
  54. /**
  55. * Create a new form builder instance.
  56. *
  57. * @param Illuminate\Routing\UrlGenerator $url
  58. * @param Illuminate\Html\HtmlBuilder $html
  59. * @param string $csrfToken
  60. * @return void
  61. */
  62. public function __construct(HtmlBuilder $html, UrlGenerator $url, $csrfToken)
  63. {
  64. $this->url = $url;
  65. $this->html = $html;
  66. $this->csrfToken = $csrfToken;
  67. }
  68. /**
  69. * Open up a new HTML form.
  70. *
  71. * @param array $options
  72. * @return string
  73. */
  74. public function open(array $options = array())
  75. {
  76. $method = array_get($options, 'method', 'post');
  77. // We need to extract the proper method from the attributes. If the method is
  78. // something other than GET or POST we'll use POST since we will spoof the
  79. // actual method since forms don't support PUT or DELETE as native HTML.
  80. $attributes['method'] = $this->getMethod($method);
  81. $attributes['action'] = $this->getAction($options);
  82. $attributes['accept-charset'] = 'UTF-8';
  83. // If the method is PUT or DELETE, we will need to add a spoofer hidden field
  84. // that will instruct this Symfony request to pretend that the method is a
  85. // different method than it actually is, for convenience from the forms.
  86. $append = $this->getAppendage($method);
  87. if (isset($options['files']) and $options['files'])
  88. {
  89. $options['enctype'] = 'multipart/form-data';
  90. }
  91. // Finally we're ready to create the final form HTML field. We will attribute
  92. // format the array of attributes. We will also add on the appendage which
  93. // is used to spoof the requests for PUT and DELETE requests to the app.
  94. $attributes = array_merge(
  95. $attributes, array_except($options, $this->reserved)
  96. );
  97. // Finally, we will concatenate all of the attributes into a single string so
  98. // we can build out the final form open statement. We'll also append on an
  99. // extra value for the hidden _method field if it's needed for the form.
  100. $attributes = $this->html->attributes($attributes);
  101. return '<form'.$attributes.'>'.$append;
  102. }
  103. /**
  104. * Create a new model based form builder.
  105. *
  106. * @param mixed $model
  107. * @param array $options
  108. * @return string
  109. */
  110. public function model($model, array $options = array())
  111. {
  112. $this->model = $model;
  113. return $this->open($options);
  114. }
  115. /**
  116. * Close the current form.
  117. *
  118. * @return string
  119. */
  120. public function close()
  121. {
  122. $this->labels = array();
  123. $this->model = null;
  124. return '</form>';
  125. }
  126. /**
  127. * Generate a hidden field with the current CSRF token.
  128. *
  129. * @return string
  130. */
  131. public function token()
  132. {
  133. return $this->hidden('_token', $this->csrfToken);
  134. }
  135. /**
  136. * Create a form label element.
  137. *
  138. * @param string $name
  139. * @param string $value
  140. * @param array $options
  141. * @return string
  142. */
  143. public function label($name, $value, $options = array())
  144. {
  145. $this->labels[] = $name;
  146. $options = $this->html->attributes($options);
  147. return '<label for="'.$name.'"'.$options.'>'.e($value).'</label>';
  148. }
  149. /**
  150. * Create a form input field.
  151. *
  152. * @param string $type
  153. * @param string $name
  154. * @param string $value
  155. * @param array $options
  156. * @return string
  157. */
  158. public function input($type, $name, $value = null, $options = array())
  159. {
  160. if ( ! isset($options['name'])) $options['name'] = $name;
  161. // We will get the appropriate value for the given field. We will look for the
  162. // value in the session for the value in the old input data then we'll look
  163. // in the model instance if one is set. Otherwise we will just use empty.
  164. $id = $this->getIdAttribute($name, $options);
  165. $value = $this->getValueAttribute($name, $value);
  166. $merge = compact('type', 'value', 'id');
  167. // Once we have the type, value, and ID we can marge them into the rest of the
  168. // attributes array so we can convert them into their HTML attribute format
  169. // when creating the HTML element. Then, we will return the entire input.
  170. $options = array_merge($options, $merge);
  171. return '<input'.$this->html->attributes($options).'>';
  172. }
  173. /**
  174. * Create a text input field.
  175. *
  176. * @param string $name
  177. * @param string $value
  178. * @param array $options
  179. * @return string
  180. */
  181. public function text($name, $value = null, $options = array())
  182. {
  183. return $this->input('text', $name, $value, $options);
  184. }
  185. /**
  186. * Create a password input field.
  187. *
  188. * @param string $name
  189. * @param array $options
  190. * @return string
  191. */
  192. public function password($name, $options = array())
  193. {
  194. return $this->input('password', $name, '', $options);
  195. }
  196. /**
  197. * Create a hidden input field.
  198. *
  199. * @param string $name
  200. * @param string $value
  201. * @param array $options
  202. * @return string
  203. */
  204. public function hidden($name, $value = null, $options = array())
  205. {
  206. return $this->input('hidden', $name, $value, $options);
  207. }
  208. /**
  209. * Create an e-mail input field.
  210. *
  211. * @param string $name
  212. * @param string $value
  213. * @param array $options
  214. * @return string
  215. */
  216. public function email($name, $value = null, $options = array())
  217. {
  218. return $this->input('email', $name, $value, $options);
  219. }
  220. /**
  221. * Create a file input field.
  222. *
  223. * @param string $name
  224. * @param array $options
  225. * @return string
  226. */
  227. public function file($name, $options = array())
  228. {
  229. return $this->input('file', $name, null, $options);
  230. }
  231. /**
  232. * Create a textarea input field.
  233. *
  234. * @param string $name
  235. * @param string $value
  236. * @param array $options
  237. * @return string
  238. */
  239. public function textarea($name, $value = null, $options = array())
  240. {
  241. $options['name'] = $name;
  242. // Next we will look for the rows and cols attributes, as each of these are put
  243. // on the textarea element definition. If they are not present, we will just
  244. // assume some sane default values for these attributes for the developer.
  245. $options = $this->setTextAreaSize($options);
  246. $options['id'] = $this->getIdAttribute($name, $options);
  247. $value = (string) $this->getValueAttribute($name, $value);
  248. unset($options['size']);
  249. // Next we will convert the attributes into a string form. Also we have removed
  250. // the size attribute, as it was merely a short-cut for the rows and cols on
  251. // the element. Then we'll create the final textarea elements HTML for us.
  252. $options = $this->html->attributes($options);
  253. return '<textarea'.$options.'>'.e($value).'</textarea>';
  254. }
  255. /**
  256. * Set the text area size on the attributes.
  257. *
  258. * @param array $options
  259. * @return array
  260. */
  261. protected function setTextAreaSize($options)
  262. {
  263. if (isset($options['size']))
  264. {
  265. return $this->setQuickTextAreaSize($options);
  266. }
  267. // If the "size" attribute was not specified, we will just look for the regular
  268. // columns and rows attributes, using sane defaults if these do not exist on
  269. // the attributes array. We'll then return this entire options array back.
  270. $cols = array_get($options, 'cols', 50);
  271. $rows = array_get($options, 'rows', 10);
  272. return array_merge($options, compact('cols', 'rows'));
  273. }
  274. /**
  275. * Set the text area size using the quick "size" attribute.
  276. *
  277. * @param array $options
  278. * @return array
  279. */
  280. protected function setQuickTextAreaSize($options)
  281. {
  282. $segments = explode('x', $options['size']);
  283. return array_merge($options, array('cols' => $segments[0], 'rows' => $segments[1]));
  284. }
  285. /**
  286. * Create a select box field.
  287. *
  288. * @param string $name
  289. * @param array $list
  290. * @param string $selected
  291. * @param array $options
  292. * @return string
  293. */
  294. public function select($name, $list = array(), $selected = null, $options = array())
  295. {
  296. // When building a select box the "value" attribute is really the selected one
  297. // so we will use that when checking the model or session for a value which
  298. // should provide a convenient method of re-populating the forms on post.
  299. $selected = $this->getValueAttribute($name, $selected);
  300. $options['id'] = $this->getIdAttribute($name, $options);
  301. $options['name'] = $name;
  302. // We will simply loop through the options and build an HTML value for each of
  303. // them until we have an array of HTML declarations. Then we will join them
  304. // all together into one single HTML element that can be put on the form.
  305. $html = array();
  306. foreach ($list as $value => $display)
  307. {
  308. $html[] = $this->getSelectOption($display, $value, $selected);
  309. }
  310. // Once we have all of this HTML, we can join this into a single element after
  311. // formatting the attributes into an HTML "attributes" string, then we will
  312. // build out a final select statement, which will contain all the values.
  313. $options = $this->html->attributes($options);
  314. $list = implode('', $html);
  315. return "<select{$options}>{$list}</select>";
  316. }
  317. /**
  318. * Get the select option for the given value.
  319. *
  320. * @param string $display
  321. * @param string $value
  322. * @param string $selected
  323. * @return string
  324. */
  325. protected function getSelectOption($display, $value, $selected)
  326. {
  327. if (is_array($display))
  328. {
  329. return $this->optionGroup($display, $value, $selected);
  330. }
  331. return $this->option($display, $value, $selected);
  332. }
  333. /**
  334. * Create an option group form element.
  335. *
  336. * @param array $list
  337. * @param string $label
  338. * @param string $selected
  339. * @return string
  340. */
  341. protected function optionGroup($list, $label, $selected)
  342. {
  343. $html = array();
  344. foreach ($list as $value => $display)
  345. {
  346. $html[] = $this->option($display, $value, $selected);
  347. }
  348. return '<optgroup label="'.e($label).'">'.implode('', $html).'</optgroup>';
  349. }
  350. /**
  351. * Create a select element option.
  352. *
  353. * @param string $display
  354. * @param string $value
  355. * @param string $selected
  356. * @return string
  357. */
  358. protected function option($display, $value, $selected)
  359. {
  360. $selected = $this->getSelectedValue($value, $selected);
  361. $options = array('value' => e($value), 'selected' => $selected);
  362. return '<option'.$this->html->attributes($options).'>'.e($display).'</option>';
  363. }
  364. /**
  365. * Determine if the value is selected.
  366. *
  367. * @param string $value
  368. * @param string $selected
  369. * @return string
  370. */
  371. protected function getSelectedValue($value, $selected)
  372. {
  373. if (is_array($selected))
  374. {
  375. return in_array($value, $selected) ? 'selected' : null;
  376. }
  377. return ((string) $value == (string) $selected) ? 'selected' : null;
  378. }
  379. /**
  380. * Create a checkbox input field.
  381. *
  382. * @param string $name
  383. * @param mixed $value
  384. * @param bool $checked
  385. * @param array $options
  386. * @return string
  387. */
  388. public function checkbox($name, $value = 1, $checked = null, $options = array())
  389. {
  390. return $this->checkable('checkbox', $name, $value, $checked, $options);
  391. }
  392. /**
  393. * Create a radio button input field.
  394. *
  395. * @param string $name
  396. * @param mixed $value
  397. * @param bool $checked
  398. * @param array $options
  399. * @return string
  400. */
  401. public function radio($name, $value = null, $checked = null, $options = array())
  402. {
  403. if (is_null($value)) $value = $name;
  404. return $this->checkable('radio', $name, $value, $checked, $options);
  405. }
  406. /**
  407. * Create a checkable input field.
  408. *
  409. * @param string $type
  410. * @param string $name
  411. * @param mixed $value
  412. * @param bool $checked
  413. * @param array $options
  414. * @return string
  415. */
  416. protected function checkable($type, $name, $value, $checked, $options)
  417. {
  418. if (is_null($checked)) $checked = $this->getCheckedState($type, $name, $value);
  419. if ($checked) $options['checked'] = 'checked';
  420. return $this->input($type, $name, $value, $options);
  421. }
  422. /**
  423. * Get the check state for a checkable input.
  424. *
  425. * @param string $type
  426. * @param string $name
  427. * @param mixed $value
  428. * @return void
  429. */
  430. protected function getCheckedState($type, $name, $value)
  431. {
  432. if ($type == 'checkbox') return (bool) $this->getValueAttribute($name);
  433. return $this->getValueAttribute($name) == $value;
  434. }
  435. /**
  436. * Create a submit button element.
  437. *
  438. * @param string $value
  439. * @param array $options
  440. * @return string
  441. */
  442. public function submit($value = null, $options = array())
  443. {
  444. return $this->input('submit', null, $value, $options);
  445. }
  446. /**
  447. * Create a button element.
  448. *
  449. * @param string $value
  450. * @param array $options
  451. * @return string
  452. */
  453. public function button($value = null, $options = array())
  454. {
  455. if ( ! array_key_exists('type', $options) )
  456. {
  457. $options['type'] = 'button';
  458. }
  459. return '<button'.$this->html->attributes($options).'>'.e($value).'</button>';
  460. }
  461. /**
  462. * Register a custom form macro.
  463. *
  464. * @param string $name
  465. * @param callable $macro
  466. * @return void
  467. */
  468. public function macro($name, $macro)
  469. {
  470. $this->macros[$name] = $macro;
  471. }
  472. /**
  473. * Parse the form action method.
  474. *
  475. * @param string $method
  476. * @return string
  477. */
  478. protected function getMethod($method)
  479. {
  480. return $method != 'GET' ? 'POST' : $method;
  481. }
  482. /**
  483. * Get the form action from the options.
  484. *
  485. * @param array $options
  486. * @return string
  487. */
  488. protected function getAction(array $options)
  489. {
  490. // We will also check for a "route" or "action" parameter on the array so that
  491. // developers can easily specify a route or controller action when creating
  492. // a form providing a convenient interface for creating the form actions.
  493. if (isset($options['url']))
  494. {
  495. return $this->getUrlAction($options['url']);
  496. }
  497. if (isset($options['route']))
  498. {
  499. return $this->getRouteAction($options['route']);
  500. }
  501. // If an action is available, we are attempting to open a form to a controller
  502. // action route. So, we will use the URL generator to get the path to these
  503. // actions and return them from the method. Otherwise, we'll use current.
  504. elseif (isset($options['action']))
  505. {
  506. return $this->getControllerAction($options['action']);
  507. }
  508. return $this->url->current();
  509. }
  510. /**
  511. * Get the action for a "url" option.
  512. *
  513. * @param array|string $options
  514. * @return string
  515. */
  516. protected function getUrlAction($options)
  517. {
  518. if (is_array($options))
  519. {
  520. return $this->url->to($options[0], array_slice($options, 1));
  521. }
  522. return $this->url->to($options);
  523. }
  524. /**
  525. * Get the action for a "route" option.
  526. *
  527. * @param array|string $options
  528. * @return string
  529. */
  530. protected function getRouteAction($options)
  531. {
  532. if (is_array($options))
  533. {
  534. return $this->url->route($options[0], array_slice($options, 1));
  535. }
  536. return $this->url->route($options);
  537. }
  538. /**
  539. * Get the action for an "action" option.
  540. *
  541. * @param array|string $options
  542. * @return string
  543. */
  544. protected function getControllerAction($options)
  545. {
  546. if (is_array($options))
  547. {
  548. return $this->url->action($options[0], array_slice($options, 1));
  549. }
  550. return $this->url->action($options);
  551. }
  552. /**
  553. * Get the form appendage for the given method.
  554. *
  555. * @param string $method
  556. * @return string
  557. */
  558. protected function getAppendage($method)
  559. {
  560. $method = strtoupper($method);
  561. if ($method == 'PUT' or $method == 'DELETE')
  562. {
  563. return $this->hidden('_method', $method);
  564. }
  565. return '';
  566. }
  567. /**
  568. * Get the ID attribute for a field name.
  569. *
  570. * @param string $name
  571. * @param array $attributes
  572. * @return string
  573. */
  574. protected function getIdAttribute($name, $attributes)
  575. {
  576. if (array_key_exists('id', $attributes))
  577. {
  578. return $attributes['id'];
  579. }
  580. if (in_array($name, $this->labels))
  581. {
  582. return $name;
  583. }
  584. }
  585. /**
  586. * Get the value that should be assigned to the field.
  587. *
  588. * @param string $name
  589. * @param string $value
  590. * @return string
  591. */
  592. protected function getValueAttribute($name, $value = null)
  593. {
  594. if (is_null($name)) return $value;
  595. if (isset($this->session) and $this->session->hasOldInput($name))
  596. {
  597. return $this->session->getOldInput($name);
  598. }
  599. if ( ! is_null($value)) return $value;
  600. if (isset($this->model) and isset($this->model[$name]))
  601. {
  602. return $this->model[$name];
  603. }
  604. }
  605. /**
  606. * Get the session store implementation.
  607. *
  608. * @return Illuminate\Session\Store $session
  609. */
  610. public function getSessionStore()
  611. {
  612. return $this->session;
  613. }
  614. /**
  615. * Set the session store implementation.
  616. *
  617. * @param Illuminate\Session\Store $session
  618. * @return Illuminate\Html\FormBuilder
  619. */
  620. public function setSessionStore(Session $session)
  621. {
  622. $this->session = $session;
  623. return $this;
  624. }
  625. /**
  626. * Dynamically handle calls to the form builder.
  627. *
  628. * @param string $method
  629. * @param array $parameters
  630. * @return mixed
  631. */
  632. public function __call($method, $parameters)
  633. {
  634. if (isset($this->macros[$method]))
  635. {
  636. return call_user_func_array($this->macros[$method], $parameters);
  637. }
  638. throw new \BadMethodCallException("Method {$method} does not exist.");
  639. }
  640. }