説明なし
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

FormBuilder.php 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | Yzncms [ 御宅男工作室 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2018 http://yzncms.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: 御宅男 <530765310@qq.com>
  10. // +----------------------------------------------------------------------
  11. // +----------------------------------------------------------------------
  12. // | 表单生成类 https://github.com/LaravelCollective/html
  13. // +----------------------------------------------------------------------
  14. namespace form;
  15. use think\facade\Request;
  16. use think\helper\Arr;
  17. class FormBuilder
  18. {
  19. /**
  20. * 我们创建的标签名称数组
  21. *
  22. * @var array
  23. */
  24. protected $labels = [];
  25. /**
  26. * 输入类型
  27. *
  28. * @var null
  29. */
  30. protected $type = null;
  31. /**
  32. * 默认情况下不填充value的输入类型
  33. *
  34. * @var array
  35. */
  36. protected $skipValueTypes = ['file', 'password', 'checkbox', 'radio'];
  37. /**
  38. * 创建一个CSRF令牌生成隐藏字段
  39. *
  40. * @param string $name
  41. * @param string $type
  42. *
  43. * @return string
  44. */
  45. public function token($name = '__token__', $type = 'md5')
  46. {
  47. $token = Request::token($name, $type);
  48. return '<input type="hidden" name="' . $name . '" value="' . $token . '" />';
  49. }
  50. /**
  51. * 创建一个表单标签元素。
  52. *
  53. * @param $name
  54. * @param null $value
  55. * @param array $options
  56. * @param bool $escape_html
  57. *
  58. * @return string
  59. */
  60. public function label($name, $value = null, $options = [], $escape_html = true)
  61. {
  62. $this->labels[] = $name;
  63. $options = $this->attributes($options);
  64. $value = $this->formatLabel($name, $value);
  65. if ($escape_html) {
  66. $value = $this->entities($value);
  67. }
  68. return '<label for="' . $name . '"' . $options . '>' . $value . '</label>';
  69. }
  70. /**
  71. * 创建一个表单输入字段
  72. *
  73. * @param $type
  74. * @param $name
  75. * @param null $value
  76. * @param array $options
  77. *
  78. * @return string
  79. */
  80. public function input($type, $name, $value = null, $options = [])
  81. {
  82. $this->type = $type;
  83. if (!isset($options['name'])) {
  84. $options['name'] = $name;
  85. }
  86. //我们将得到给定字段的适当值。我们将寻找
  87. //会话中的值查找旧输入数据中的值,然后我们将查找
  88. //在模型实例中(如果已设置)。否则我们只会使用空的
  89. $id = $this->getIdAttribute($name, $options);
  90. if (!in_array($type, $this->skipValueTypes)) {
  91. $value = $this->getValueAttribute($name, $value);
  92. $options['class'] = isset($options['class']) ? $options['class'] . (stripos($options['class'], 'layui-input') !== false ? '' : ' layui-input') : 'layui-input';
  93. }
  94. //一旦我们有了类型、值和ID,我们就可以将它们合并到
  95. //属性数组,以便将它们转换为HTML属性格式
  96. //创建HTML元素时。然后,我们将返回整个输入。
  97. $merge = compact('type', 'value', 'id');
  98. $options = array_merge($options, $merge);
  99. return '<input' . $this->attributes($options) . '>';
  100. }
  101. /**
  102. * 创建一个文本输入字段
  103. *
  104. * @param string $name
  105. * @param null $value
  106. * @param array $options
  107. *
  108. * @return string
  109. */
  110. public function text($name, $value = null, $options = [])
  111. {
  112. return $this->input('text', $name, $value, $options);
  113. }
  114. /**
  115. * 创建一个密码输入字段
  116. *
  117. * @param string $name
  118. * @param array $options
  119. *
  120. * @return string
  121. */
  122. public function password(string $name, array $options = [])
  123. {
  124. return $this->input('password', $name, '', $options);
  125. }
  126. /**
  127. * 创建一个范围输入选择器
  128. *
  129. * @param string $name
  130. * @param null $value
  131. * @param array $options
  132. *
  133. * @return string
  134. */
  135. public function range($name, $value = null, $options = [])
  136. {
  137. return $this->input('range', $name, $value, $options);
  138. }
  139. /**
  140. * 创建一个隐藏的输入字段
  141. *
  142. * @param string $name
  143. * @param null $value
  144. * @param array $options
  145. *
  146. * @return string
  147. */
  148. public function hidden($name, $value = null, $options = [])
  149. {
  150. return $this->input('hidden', $name, $value, $options);
  151. }
  152. /**
  153. * 创建一个电子邮件输入字段
  154. *
  155. * @param string $name
  156. * @param null $value
  157. * @param array $options
  158. *
  159. * @return string
  160. */
  161. public function email($name, $value = null, $options = [])
  162. {
  163. return $this->input('email', $name, $value, $options);
  164. }
  165. /**
  166. * 创建一个tel输入字段
  167. *
  168. * @param string $name
  169. * @param null $value
  170. * @param array $options
  171. *
  172. * @return string
  173. */
  174. public function tel($name, $value = null, $options = [])
  175. {
  176. return $this->input('tel', $name, $value, $options);
  177. }
  178. /**
  179. * 创建一个数字输入字段
  180. *
  181. * @param string $name
  182. * @param null $value
  183. * @param array $options
  184. *
  185. * @return string
  186. */
  187. public function number($name, $value = null, $options = [])
  188. {
  189. return $this->input('number', $name, $value, $options);
  190. }
  191. /**
  192. * 创建一个url输入字段
  193. *
  194. * @param string $name
  195. * @param null $value
  196. * @param array $options
  197. *
  198. * @return string
  199. */
  200. public function url($name, $value = null, $options = [])
  201. {
  202. return $this->input('url', $name, $value, $options);
  203. }
  204. /**
  205. * 创建一个textarea输入字段
  206. *
  207. * @param string $name
  208. * @param null $value
  209. * @param array $options
  210. *
  211. * @return string
  212. */
  213. public function textarea($name, $value = null, $options = [])
  214. {
  215. $this->type = 'textarea';
  216. if (!isset($options['name'])) {
  217. $options['name'] = $name;
  218. }
  219. //接下来,我们将查找rows和cols属性,因为每个属性都是
  220. //在textarea元素定义上。如果他们不在场,我们就
  221. //为开发人员的这些属性假设一些合理的默认值。
  222. $options = $this->setTextAreaSize($options);
  223. $options['id'] = $this->getIdAttribute($name, $options);
  224. $value = (string) $this->getValueAttribute($name, $value);
  225. unset($options['size']);
  226. //接下来,我们将把属性转换成字符串形式。我们还移除了
  227. //“大小”属性,因为它只是一条通往行和列的捷径
  228. //元素。然后我们将为我们创建最终的textarea元素HTML。
  229. $options['class'] = isset($options['class']) ? $options['class'] . (stripos($options['class'], 'layui-textarea') !== false ? '' : ' layui-textarea') : 'layui-textarea';
  230. $options = $this->attributes($options);
  231. return '<textarea' . $options . '>' . $value . '</textarea>';
  232. }
  233. /**
  234. * 创建百度富文本编辑器字段
  235. *
  236. * @param string $name
  237. * @param string $value
  238. * @param array $options
  239. *
  240. * @return string
  241. */
  242. public function ueditor($name, $value = null, $options = [])
  243. {
  244. $domname = str_replace(['[', ']', '.'], '', $name);
  245. if (!isset($options['name'])) {
  246. $options['name'] = $name;
  247. }
  248. if (isset($options['class'])) {
  249. $options['class'][] = 'js-ueditor';
  250. } else {
  251. $options['class'] = 'js-ueditor';
  252. }
  253. $value = (string) $this->getValueAttribute($name, $value);
  254. $options = $this->attributes(array_merge(['id' => "c-{$domname}"], $options));
  255. return '<script type="text/plain" ' . $options . '>' . $value . '</script>';
  256. }
  257. /**
  258. * 创建一个选择框字段
  259. *
  260. * @param $name
  261. * @param array $list
  262. * @param null $selected
  263. * @param array $selectAttributes
  264. * @param array $optionsAttributes
  265. * @param array $optgroupsAttributes
  266. *
  267. * @return string
  268. */
  269. public function select(
  270. $name,
  271. $list = [],
  272. $selected = null,
  273. array $selectAttributes = [],
  274. array $optionsAttributes = [],
  275. array $optgroupsAttributes = []
  276. ) {
  277. $this->type = 'select';
  278. //在构建选择框时,“值”属性实际上就是所选的
  279. //因此,我们将在检查模型或会话时使用该值
  280. //应该提供一种方便的方法来重新填充post上的表格。
  281. $selected = $this->getValueAttribute($name, $selected);
  282. $selectAttributes['id'] = $this->getIdAttribute($name, $selectAttributes);
  283. if (!isset($selectAttributes['name'])) {
  284. $selectAttributes['name'] = $name;
  285. }
  286. //我们将简单地遍历这些选项,并为每个选项构建一个HTML值
  287. //直到我们有一个HTML声明数组。然后我们会加入他们
  288. //所有这些都整合到一个可以放在表单上的HTML元素中。
  289. $html = [];
  290. if (isset($selectAttributes['placeholder'])) {
  291. $html[] = $this->placeholderOption($selectAttributes['placeholder'], $selected);
  292. unset($selectAttributes['placeholder']);
  293. }
  294. foreach ($list as $value => $display) {
  295. $optionAttributes = $optionsAttributes[$value] ?? [];
  296. $optgroupAttributes = $optgroupsAttributes[$value] ?? [];
  297. $html[] = $this->getSelectOption($display, $value, $selected, $optionAttributes, $optgroupAttributes);
  298. }
  299. //一旦我们拥有了所有这些HTML,我们就可以在之后将其加入到单个元素中
  300. //将属性格式化为HTML“attributes”字符串,然后
  301. //构建一个最终的select语句,它将包含所有的值。
  302. $selectAttributes = $this->attributes($selectAttributes);
  303. $list = implode('', $html);
  304. return "<select{$selectAttributes}>{$list}</select>";
  305. }
  306. /**
  307. * 创建一个按钮字段
  308. *
  309. * @param string $value
  310. * @param array $options
  311. *
  312. * @return string
  313. */
  314. public function button($value = null, $options = [])
  315. {
  316. if (!array_key_exists('type', $options)) {
  317. $options['type'] = 'button';
  318. }
  319. return '<button' . $this->attributes($options) . '>' . $value . '</button>';
  320. }
  321. /**
  322. * 创建单选按钮输入字段
  323. *
  324. * @param string $name
  325. * @param mixed $value
  326. * @param bool $checked
  327. * @param array $options
  328. *
  329. * @return string
  330. */
  331. public function radio($name, $value = null, $checked = null, $options = [])
  332. {
  333. if (is_null($value)) {
  334. $value = $name;
  335. }
  336. if ($checked) {
  337. $options['checked'] = 'checked';
  338. }
  339. return $this->input('radio', $name, $value, $options);
  340. }
  341. /**
  342. * 创建一组单选框字段
  343. *
  344. * @param string $name
  345. * @param array $list
  346. * @param mixed $checked
  347. * @param array $title
  348. * @param array $options
  349. *
  350. * @return string
  351. */
  352. public function radios($name, $list, $checked = null, $title = [], $options = [])
  353. {
  354. if (is_array($list)) {
  355. $html = [];
  356. $checked = is_null($checked) ? key($list) : $checked;
  357. $checked = is_array($checked) ? $checked : explode(',', $checked);
  358. foreach ($list as $k => $v) {
  359. $options['id'] = "{$name}-{$k}";
  360. $options['title'] = $title[$k] ?? $v;
  361. $html[] = Form::radio($name, $k, in_array($k, $checked), $options);
  362. }
  363. return '<div class="radio-group">' . implode(' ', $html) . '</div>';
  364. }
  365. return '';
  366. }
  367. /**
  368. * 创建复选按钮字段
  369. *
  370. * @param string $name
  371. * @param mixed $value
  372. * @param bool $checked
  373. * @param array $options
  374. *
  375. * @return string
  376. */
  377. public function checkbox($name, $value = 1, $checked = null, $options = [])
  378. {
  379. if ($checked) {
  380. $options['checked'] = 'checked';
  381. }
  382. return $this->input('checkbox', $name, $value, $options);
  383. }
  384. /**
  385. * 创建一组复选按钮框字段
  386. *
  387. * @param string $name
  388. * @param array $list
  389. * @param mixed $checked
  390. * @param array $title
  391. * @param array $options
  392. *
  393. * @return string
  394. */
  395. public function checkboxs($name, $list, $checked, $title = [], $options = [])
  396. {
  397. if (is_array($list)) {
  398. $html = [];
  399. $checked = is_null($checked) ? [] : $checked;
  400. $checked = is_array($checked) ? $checked : explode(',', $checked);
  401. foreach ($list as $k => $v) {
  402. $options['id'] = "{$name}-{$k}";
  403. $options['title'] = $title[$k] ?? $v;
  404. $html[] = Form::checkbox("{$name}[{$k}]", $k, in_array($k, $checked), $options);
  405. }
  406. return '<div class="checkbox">' . implode(' ', $html) . '</div>';
  407. }
  408. return '';
  409. }
  410. /**
  411. * 创建一个上传图片组件(单图)字段
  412. *
  413. * @param string $name
  414. * @param string $value
  415. * @param array $inputAttr
  416. * @param array $uploadAttr
  417. * @param array $chooseAttr
  418. * @param array $previewAttr
  419. *
  420. * @return string
  421. */
  422. public function image($name = null, $value = null, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = [])
  423. {
  424. $default = [
  425. 'data-type' => "image",
  426. 'data-mimetype' => 'image/gif,image/jpeg,image/png,image/jpg,image/bmp',
  427. ];
  428. $uploadAttr = is_array($uploadAttr) ? array_merge($default, $uploadAttr) : $uploadAttr;
  429. $chooseAttr = is_array($chooseAttr) ? array_merge($default, $chooseAttr) : $chooseAttr;
  430. return $this->uploader($name, $value, $inputAttr, $uploadAttr, $chooseAttr, $previewAttr);
  431. }
  432. /**
  433. * 创建一个上传图片组件(多图)字段
  434. *
  435. * @param string $name
  436. * @param string $value
  437. * @param array $inputAttr
  438. * @param array $uploadAttr
  439. * @param array $chooseAttr
  440. * @param array $previewAttr
  441. *
  442. * @return string
  443. */
  444. public function images($name = null, $value = null, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = [])
  445. {
  446. $default = [
  447. 'data-type' => "image",
  448. 'data-multiple' => 'true',
  449. 'data-mimetype' => 'image/gif,image/jpeg,image/png,image/jpg,image/bmp',
  450. ];
  451. $uploadAttr = is_array($uploadAttr) ? array_merge($default, $uploadAttr) : $uploadAttr;
  452. $chooseAttr = is_array($chooseAttr) ? array_merge($default, $chooseAttr) : $chooseAttr;
  453. return $this->uploader($name, $value, $inputAttr, $uploadAttr, $chooseAttr, $previewAttr);
  454. }
  455. /**
  456. * 创建上传文件组件(单文件)字段
  457. *
  458. * @param string $name
  459. * @param string $value
  460. * @param array $inputAttr
  461. * @param array $uploadAttr
  462. * @param array $chooseAttr
  463. * @param array $previewAttr
  464. *
  465. * @return string
  466. */
  467. public function upload($name = null, $value = null, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = [])
  468. {
  469. $default = [
  470. 'data-type' => "file",
  471. ];
  472. $uploadAttr = is_array($uploadAttr) ? array_merge($default, $uploadAttr) : $uploadAttr;
  473. $chooseAttr = is_array($chooseAttr) ? array_merge($default, $chooseAttr) : $chooseAttr;
  474. return $this->uploader($name, $value, $inputAttr, $uploadAttr, $chooseAttr, $previewAttr);
  475. }
  476. /**
  477. * 创建上传文件组件(多文件)字段
  478. *
  479. * @param string $name
  480. * @param string $value
  481. * @param array $inputAttr
  482. * @param array $uploadAttr
  483. * @param array $chooseAttr
  484. * @param array $previewAttr
  485. *
  486. * @return string
  487. */
  488. public function uploads($name = null, $value = null, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = [])
  489. {
  490. $default = [
  491. 'data-type' => "file",
  492. 'data-multiple' => 'true',
  493. ];
  494. $uploadAttr = is_array($uploadAttr) ? array_merge($default, $uploadAttr) : $uploadAttr;
  495. $chooseAttr = is_array($chooseAttr) ? array_merge($default, $chooseAttr) : $chooseAttr;
  496. return $this->uploader($name, $value, $inputAttr, $uploadAttr, $chooseAttr, $previewAttr);
  497. }
  498. /**
  499. * 创建颜色选择字段
  500. *
  501. * @param string $name
  502. * @param string $value
  503. * @param array $options
  504. *
  505. * @return string
  506. */
  507. public function color($name = null, $value = null, $options = [])
  508. {
  509. $domname = str_replace(['[', ']', '.'], '', $name);
  510. $input = $this->text($name, $value, array_merge(['id' => "c-{$domname}", 'placeholder' => '请选择颜色'], $options));
  511. $html = <<<EOD
  512. <div class="layui-input-inline" style="width: 120px;">
  513. {$input}
  514. </div>
  515. <div class="layui-inline" style="left: -11px;">
  516. <div class="colorpicker" data-input-id="c-{$domname}"></div>
  517. </div>
  518. EOD;
  519. return $html;
  520. }
  521. /**
  522. * 创建日期时间选择器字段
  523. *
  524. * @param string $name
  525. * @param string $value
  526. * @param array $options
  527. *
  528. * @return string
  529. */
  530. public function datetime($name = null, $value = null, $options = [])
  531. {
  532. $value = is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
  533. return $this->datetimepicker($name, $value, $options);
  534. }
  535. /**
  536. * 日期时间选择器
  537. *
  538. * @param string $name
  539. * @param mixed $value
  540. * @param array $options
  541. * @return string
  542. */
  543. public function datetimepicker($name, $value, $options = [])
  544. {
  545. $value = is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
  546. $options['class'] = isset($options['class']) ? $options['class'] . ' datetime' : 'datetime';
  547. return $this->text($name, $value, $options);
  548. }
  549. /**
  550. * 创建动态下拉列表字段
  551. *
  552. * @param string $name 名称
  553. * @param mixed $value
  554. * @param string $url 数据源地址
  555. * @param string $field 显示的字段名称,默认为name
  556. * @param string $primaryKey 主键,数据库中保存的值,默认为id
  557. * @param array $options
  558. *
  559. * @return string
  560. */
  561. public function selectpage($name, $value, $url, $field = null, $primaryKey = null, $options = [])
  562. {
  563. $options = array_merge($options, ['data-source' => $url, 'data-field' => $field ? $field : 'name', 'data-primary-key' => $primaryKey ? $primaryKey : 'id']);
  564. if (isset($options['class'])) {
  565. $options['class'][] = 'selectpage';
  566. } else {
  567. $options['class'] = 'selectpage';
  568. }
  569. return $this->text($name, $value, $options);
  570. }
  571. /**
  572. * 创建动态下拉列表(复选)字段
  573. *
  574. * @param string $name 名称
  575. * @param mixed $value
  576. * @param string $url 数据源地址
  577. * @param string $field 显示的字段名称,默认为name
  578. * @param string $primaryKey 主键,数据库中保存的值,默认为id
  579. * @param array $options
  580. *
  581. * @return string
  582. */
  583. public function selectpages($name, $value, $url, $field = null, $primaryKey = null, $options = [])
  584. {
  585. $options['data-multiple'] = "true";
  586. return $this->selectpage($name, $value, $url, $field, $primaryKey, $options);
  587. }
  588. protected function uploader($name = null, $value = null, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = [])
  589. {
  590. $domname = str_replace(['[', ']', '.'], '', $name);
  591. $upload = $uploadAttr === false ? false : true;
  592. $choose = $chooseAttr === false ? false : true;
  593. $preview = $previewAttr === false ? false : true;
  594. $options = [
  595. 'id' => "faupload_{$domname}",
  596. 'class' => "layui-btn faupload",
  597. 'data-input-id' => "c-{$domname}",
  598. ];
  599. if ($preview) {
  600. $options['data-preview-id'] = "p-{$domname}";
  601. }
  602. $uploadBtn = $upload ? $this->button('<i class="layui-icon layui-icon-upload"></i> ' . '上传', array_merge($options, $uploadAttr)) : '';
  603. $options = [
  604. 'id' => "fachoose-{$domname}",
  605. 'class' => "layui-btn fachoose",
  606. 'data-input-id' => "c-{$domname}",
  607. ];
  608. if ($preview) {
  609. $options['data-preview-id'] = "p-{$domname}";
  610. }
  611. $chooseBtn = $choose ? $this->button('<i class="iconfont icon-other"></i> ' . '选择', array_merge($options, $chooseAttr)) : '';
  612. $previewAttrHtml = $this->attributes($previewAttr);
  613. $previewArea = $preview ? '<ul class="layui-row list-inline plupload-preview" id="p-' . $domname . '" ' . $previewAttrHtml . '></ul>' : '';
  614. $input = $this->text($name, $value, array_merge(['id' => "c-{$domname}", 'class' => 'layui-input'], $inputAttr));
  615. $html = <<<EOD
  616. <div class="layui-col-xs4">{$input}</div>
  617. {$uploadBtn}{$chooseBtn}
  618. {$previewArea}
  619. EOD;
  620. return $html;
  621. }
  622. /**
  623. * 获取给定值的选择选项
  624. *
  625. * @param string $display
  626. * @param string $value
  627. * @param string $selected
  628. * @param array $attributes
  629. * @param array $optgroupAttributes
  630. *
  631. * @return mixed
  632. */
  633. protected function getSelectOption($display, $value, $selected, array $attributes = [], array $optgroupAttributes = [])
  634. {
  635. if (is_array($display)) {
  636. //if (is_iterable($display)) {
  637. return $this->optionGroup($display, $value, $selected, $optgroupAttributes, $attributes);
  638. }
  639. return $this->option($display, $value, $selected, $attributes);
  640. }
  641. /**
  642. * 创建选项组表单元素
  643. *
  644. * @param array $list
  645. * @param string $label
  646. * @param string $selected
  647. * @param array $attributes
  648. * @param array $optionsAttributes
  649. * @param integer $level
  650. *
  651. * @return string
  652. */
  653. protected function optionGroup($list, $label, $selected, array $attributes = [], array $optionsAttributes = [], $level = 0)
  654. {
  655. $html = [];
  656. $space = str_repeat("&nbsp;", $level);
  657. foreach ($list as $value => $display) {
  658. $optionAttributes = $optionsAttributes[$value] ?? [];
  659. if (is_array($display)) {
  660. //if (is_iterable($display)) {
  661. $html[] = $this->optionGroup($display, $value, $selected, $attributes, $optionAttributes, $level + 5);
  662. } else {
  663. $html[] = $this->option($space . $display, $value, $selected, $optionAttributes);
  664. }
  665. }
  666. return '<optgroup label="' . e($space . $label, false) . '"' . $this->attributes($attributes) . '>' . implode('', $html) . '</optgroup>';
  667. }
  668. /**
  669. * 创建一个选择元素选项
  670. *
  671. * @param string $display
  672. * @param string $value
  673. * @param string $selected
  674. * @param array $attributes
  675. *
  676. * @return mixed
  677. */
  678. protected function option($display, $value, $selected, array $attributes = [])
  679. {
  680. $selected = $this->getSelectedValue($value, $selected);
  681. $options = array_merge(['value' => $value, 'selected' => $selected], $attributes);
  682. $string = '<option' . $this->attributes($options) . '>';
  683. if ($display !== null) {
  684. $string .= e($display, false) . '</option>';
  685. }
  686. return $string;
  687. }
  688. /**
  689. * 创建占位符选择元素选项
  690. *
  691. * @param $display
  692. * @param $selected
  693. *
  694. * @return mixed
  695. */
  696. protected function placeholderOption($display, $selected)
  697. {
  698. $selected = $this->getSelectedValue(null, $selected);
  699. $options = [
  700. 'selected' => $selected,
  701. 'value' => '',
  702. ];
  703. return '<option' . $this->attributes($options) . '>' . e($display, false) . '</option>';
  704. }
  705. /**
  706. * 确定是否选择了该值
  707. *
  708. * @param string $value
  709. * @param string $selected
  710. *
  711. * @return null|string
  712. */
  713. protected function getSelectedValue($value, $selected)
  714. {
  715. if (is_array($selected)) {
  716. return in_array($value, $selected, true) || in_array((string) $value, $selected, true) ? 'selected' : null;
  717. }
  718. if (is_int($value) && is_bool($selected)) {
  719. return (bool) $value === $selected;
  720. }
  721. return ((string) $value === (string) $selected) ? 'selected' : null;
  722. }
  723. /**
  724. * 在属性上设置文本区域大小
  725. *
  726. * @param array $options
  727. *
  728. * @return array
  729. */
  730. protected function setTextAreaSize($options)
  731. {
  732. if (isset($options['size'])) {
  733. return $this->setQuickTextAreaSize($options);
  734. }
  735. //如果没有指定“size”属性,我们将只查找常规
  736. //列和行属性,如果这些属性在
  737. //属性数组。然后我们将返回整个选项数组。
  738. $cols = Arr::get($options, 'cols', 50);
  739. $rows = Arr::get($options, 'rows', 10);
  740. return array_merge($options, compact('cols', 'rows'));
  741. }
  742. /**
  743. * 使用快速“大小”属性设置文本区域大小
  744. *
  745. * @param array $options
  746. *
  747. * @return array
  748. */
  749. protected function setQuickTextAreaSize($options)
  750. {
  751. $segments = explode('x', $options['size']);
  752. return array_merge($options, ['cols' => $segments[0], 'rows' => $segments[1]]);
  753. }
  754. /**
  755. * 获取应分配给字段的值
  756. *
  757. * @param string $name
  758. * @param string $value
  759. *
  760. * @return mixed
  761. */
  762. protected function getValueAttribute($name, $value = null)
  763. {
  764. if (is_null($name)) {
  765. return $value;
  766. }
  767. if (!is_null($value)) {
  768. return $value;
  769. }
  770. }
  771. /**
  772. * 将HTML字符串转换为实体
  773. *
  774. * @param string $value
  775. *
  776. * @return string
  777. */
  778. protected function entities($value)
  779. {
  780. return htmlentities($value, ENT_QUOTES, 'UTF-8', false);
  781. }
  782. /**
  783. * 获取字段名的ID属性
  784. *
  785. * @param string $name
  786. * @param array $attributes
  787. *
  788. * @return string
  789. */
  790. public function getIdAttribute($name, $attributes)
  791. {
  792. if (array_key_exists('id', $attributes)) {
  793. return $attributes['id'];
  794. }
  795. if (in_array($name, $this->labels)) {
  796. return $name;
  797. }
  798. }
  799. /**
  800. * 设置标签值的格式。
  801. *
  802. * @param string $name
  803. * @param string|null $value
  804. *
  805. * @return string
  806. */
  807. protected function formatLabel($name, $value)
  808. {
  809. return $value ?: ucwords(str_replace('_', ' ', $name));
  810. }
  811. /**
  812. * 从数组生成HTML属性字符串
  813. *
  814. * @param array $attributes
  815. *
  816. * @return string
  817. */
  818. public function attributes($attributes)
  819. {
  820. $html = [];
  821. foreach ((array) $attributes as $key => $value) {
  822. $element = $this->attributeElement($key, $value);
  823. if (!is_null($element)) {
  824. $html[] = $element;
  825. }
  826. }
  827. return count($html) > 0 ? ' ' . implode(' ', $html) : '';
  828. }
  829. /**
  830. * 构建单个属性元素
  831. *
  832. * @param string $key
  833. * @param string $value
  834. *
  835. * @return string
  836. */
  837. protected function attributeElement($key, $value)
  838. {
  839. //[required]之类的HTML属性转换为正确的形式,而不是使用错误的数字。
  840. if (is_numeric($key)) {
  841. return $value;
  842. }
  843. // 将布尔属性视为HTML属性
  844. if (is_bool($value) && $key !== 'value') {
  845. return $value ? $key : '';
  846. }
  847. //多个class属性
  848. if (is_array($value) && $key === 'class') {
  849. return 'class="' . implode(' ', $value) . '"';
  850. }
  851. if (!is_null($value)) {
  852. if (is_array($value) || stripos($value, '"') !== false) {
  853. $value = is_array($value) ? json_encode($value, JSON_UNESCAPED_UNICODE) : $value;
  854. return $key . "='" . $value . "'";
  855. } else {
  856. return $key . '="' . $value . '"';
  857. }
  858. }
  859. }
  860. }
  861. if (!function_exists('e')) {
  862. /**
  863. * 将HTML特殊字符编码为字符串
  864. *
  865. * @param string $value
  866. * @param bool $doubleEncode
  867. *
  868. * @return string
  869. */
  870. function e($value, $doubleEncode = true)
  871. {
  872. if (is_array($value)) {
  873. $value = json_encode($value, JSON_UNESCAPED_UNICODE);
  874. }
  875. return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', $doubleEncode);
  876. }
  877. }