Field.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. <?php
  2. namespace Dcat\Admin\Show;
  3. use Dcat\Admin\Admin;
  4. use Dcat\Admin\Show;
  5. use Dcat\Admin\Support\Helper;
  6. use Dcat\Admin\Traits\HasBuilderEvents;
  7. use Dcat\Admin\Traits\HasDefinitions;
  8. use Dcat\Admin\Widgets\Dump;
  9. use Illuminate\Contracts\Support\Arrayable;
  10. use Illuminate\Contracts\Support\Renderable;
  11. use Illuminate\Support\Arr;
  12. use Illuminate\Support\Collection;
  13. use Illuminate\Support\Facades\Storage;
  14. use Illuminate\Support\Fluent;
  15. use Illuminate\Support\Str;
  16. use Illuminate\Support\Traits\Macroable;
  17. class Field implements Renderable
  18. {
  19. use HasBuilderEvents,
  20. HasDefinitions,
  21. Macroable {
  22. __call as macroCall;
  23. }
  24. /**
  25. * @var array
  26. */
  27. protected static $extendedFields = [];
  28. /**
  29. * @var string
  30. */
  31. protected $view = 'admin::show.field';
  32. /**
  33. * Name of column.
  34. *
  35. * @var string
  36. */
  37. protected $name;
  38. /**
  39. * Label of column.
  40. *
  41. * @var string
  42. */
  43. protected $label;
  44. /**
  45. * Escape field value or not.
  46. *
  47. * @var bool
  48. */
  49. protected $escape = true;
  50. /**
  51. * Field value.
  52. *
  53. * @var mixed
  54. */
  55. protected $value;
  56. /**
  57. * @var Collection
  58. */
  59. protected $showAs = [];
  60. /**
  61. * Parent show instance.
  62. *
  63. * @var Show
  64. */
  65. protected $parent;
  66. /**
  67. * Relation name.
  68. *
  69. * @var string
  70. */
  71. protected $relation;
  72. /**
  73. * If show contents in box.
  74. *
  75. * @var bool
  76. */
  77. protected $border = true;
  78. /**
  79. * @var int
  80. */
  81. protected $width = 8;
  82. /**
  83. * Field constructor.
  84. *
  85. * @param string $name
  86. * @param string $label
  87. */
  88. public function __construct($name = '', $label = '')
  89. {
  90. $this->name = $name;
  91. $this->label = $this->formatLabel($label);
  92. $this->showAs = new Collection();
  93. $this->callResolving();
  94. }
  95. /**
  96. * Set parent show instance.
  97. *
  98. * @param Show $show
  99. *
  100. * @return $this
  101. */
  102. public function setParent(Show $show)
  103. {
  104. $this->parent = $show;
  105. return $this;
  106. }
  107. /**
  108. * Get name of this column.
  109. *
  110. * @return mixed
  111. */
  112. public function getName()
  113. {
  114. return $this->name;
  115. }
  116. /**
  117. * @param int $width
  118. *
  119. * @return $this|int
  120. */
  121. public function width(int $width = null)
  122. {
  123. if ($width === null) {
  124. return $this->width;
  125. }
  126. $this->width = $width;
  127. return $this;
  128. }
  129. /**
  130. * Format label.
  131. *
  132. * @param $label
  133. *
  134. * @return mixed
  135. */
  136. protected function formatLabel($label)
  137. {
  138. $label = $label ?: admin_trans_field($this->name);
  139. return str_replace(['.', '_'], ' ', $label);
  140. }
  141. /**
  142. * Get label of the column.
  143. *
  144. * @return mixed
  145. */
  146. public function getLabel()
  147. {
  148. return $this->label;
  149. }
  150. /**
  151. * Field display callback.
  152. *
  153. * @param mixed $callable
  154. *
  155. * @return $this
  156. */
  157. public function as($callable, ...$params)
  158. {
  159. $this->showAs->push([$callable, $params]);
  160. return $this;
  161. }
  162. /**
  163. * Display field using array value map.
  164. *
  165. * @param array $values
  166. * @param null $default
  167. *
  168. * @return $this
  169. */
  170. public function using(array $values, $default = null)
  171. {
  172. return $this->as(function ($value) use ($values, $default) {
  173. if (is_null($value)) {
  174. return $default;
  175. }
  176. return Arr::get($values, $value, $default);
  177. });
  178. }
  179. /**
  180. * Show field as a image.
  181. *
  182. * @param string $server
  183. * @param int $width
  184. * @param int $height
  185. *
  186. * @return $this
  187. */
  188. public function image($server = '', $width = 200, $height = 200)
  189. {
  190. return $this->unescape()->as(function ($path) use ($server, $width, $height) {
  191. if (empty($path)) {
  192. return '';
  193. }
  194. return collect((array) $path)->transform(function ($path) use ($server, $width, $height) {
  195. if (url()->isValidUrl($path)) {
  196. $src = $path;
  197. } elseif ($server) {
  198. $src = $server.$path;
  199. } else {
  200. $disk = config('admin.upload.disk');
  201. if (config("filesystems.disks.{$disk}")) {
  202. $src = Storage::disk($disk)->url($path);
  203. } else {
  204. return '';
  205. }
  206. }
  207. return "<img data-action='preview-img' src='$src' style='max-width:{$width}px;max-height:{$height}px' class='img' />";
  208. })->implode('&nbsp;');
  209. });
  210. }
  211. /**
  212. * Show field as a file.
  213. *
  214. * @param string $server
  215. * @param bool $download
  216. *
  217. * @return Field
  218. */
  219. public function file($server = '', $download = true)
  220. {
  221. $field = $this;
  222. return $this->unescape()->as(function ($path) use ($server, $download, $field) {
  223. $name = basename($path);
  224. $field->wrap(false);
  225. $size = $url = '';
  226. if (url()->isValidUrl($path)) {
  227. $url = $path;
  228. } elseif ($server) {
  229. $url = $server.$path;
  230. } else {
  231. $storage = Storage::disk(config('admin.upload.disk'));
  232. if ($storage->exists($path)) {
  233. $url = $storage->url($path);
  234. $size = ($storage->size($path) / 1000).'KB';
  235. }
  236. }
  237. if (! $url) {
  238. return '';
  239. }
  240. $icon = Helper::getFileIcon($name);
  241. return <<<HTML
  242. <ul class="mailbox-attachments clearfix">
  243. <li style="margin-bottom: 0;">
  244. <span class="mailbox-attachment-icon"><i class="{$icon}"></i></span>
  245. <div class="mailbox-attachment-info">
  246. <div class="mailbox-attachment-name">
  247. <i class="fa fa-paperclip"></i> {$name}
  248. </div>
  249. <span class="mailbox-attachment-size">
  250. {$size}&nbsp;
  251. <a href="{$url}" class="btn btn-white btn-xs pull-right" target="_blank"><i class="fa fa-cloud-download"></i></a>
  252. </span>
  253. </div>
  254. </li>
  255. </ul>
  256. HTML;
  257. });
  258. }
  259. /**
  260. * Show field as a link.
  261. *
  262. * @param string $href
  263. * @param string $target
  264. *
  265. * @return Field
  266. */
  267. public function link($href = '', $target = '_blank')
  268. {
  269. return $this->unescape()->as(function ($link) use ($href, $target) {
  270. $href = $href ?: $link;
  271. return "<a href='$href' target='{$target}'>{$link}</a>";
  272. });
  273. }
  274. /**
  275. * Show field as labels.
  276. *
  277. * @param string $style
  278. *
  279. * @return Field
  280. */
  281. public function label($style = 'primary')
  282. {
  283. $self = $this;
  284. return $this->unescape()->as(function ($value) use ($self, $style) {
  285. [$class, $background] = $self->formatStyle($style);
  286. return collect($value)->map(function ($name) use ($class, $background) {
  287. return "<span class='label bg-{$class}' $background>$name</span>";
  288. })->implode('&nbsp;');
  289. });
  290. }
  291. /**
  292. * Show field as badges.
  293. *
  294. * @param string $style
  295. *
  296. * @return Field
  297. */
  298. public function badge($style = 'blue')
  299. {
  300. $self = $this;
  301. return $this->unescape()->as(function ($value) use ($self, $style) {
  302. [$class, $background] = $self->formatStyle($style);
  303. return collect($value)->map(function ($name) use ($class, $background) {
  304. return "<span class='badge bg-{$class}' $background>$name</span>";
  305. })->implode('&nbsp;');
  306. });
  307. }
  308. /**
  309. * @param $style
  310. *
  311. * @return array
  312. */
  313. public function formatStyle($style)
  314. {
  315. $class = 'default';
  316. $background = '';
  317. if ($style !== 'default') {
  318. $class = '';
  319. $style = Admin::color()->get($style);
  320. $background = "style='background:{$style}'";
  321. }
  322. return [$class, $background];
  323. }
  324. /**
  325. * Show field as json code.
  326. *
  327. * @return Field
  328. */
  329. public function json()
  330. {
  331. $field = $this;
  332. return $this->unescape()->as(function ($value) use ($field) {
  333. $content = is_string($value) ? json_decode($value, true) : $value;
  334. $field->wrap(false);
  335. return Dump::make($content);
  336. });
  337. }
  338. /**
  339. * @param string $val
  340. *
  341. * @return $this
  342. */
  343. public function prepend($val)
  344. {
  345. return $this->as(function ($v) use (&$val) {
  346. if (is_array($v)) {
  347. array_unshift($v, $val);
  348. return $v;
  349. } elseif ($v instanceof Collection) {
  350. return $v->prepend($val);
  351. }
  352. return $val.$v;
  353. });
  354. }
  355. /**
  356. * @param string $val
  357. *
  358. * @return $this
  359. */
  360. public function append($val)
  361. {
  362. return $this->as(function ($v) use (&$val) {
  363. if (is_array($v)) {
  364. array_push($v, $val);
  365. return $v;
  366. } elseif ($v instanceof Collection) {
  367. return $v->push($val);
  368. }
  369. return $v.$val;
  370. });
  371. }
  372. /**
  373. * Split a string by string.
  374. *
  375. * @param string $d
  376. *
  377. * @return $this
  378. */
  379. public function explode(string $d = ',')
  380. {
  381. return $this->as(function ($v) use ($d) {
  382. if (is_array($v) || $v instanceof Arrayable) {
  383. return $v;
  384. }
  385. return $v ? explode($d, $v) : [];
  386. });
  387. }
  388. /**
  389. * Render this column with the given view.
  390. *
  391. * @param string $view
  392. *
  393. * @return $this
  394. */
  395. public function view($view)
  396. {
  397. $name = $this->name;
  398. return $this->unescape()->as(function ($value) use ($view, $name) {
  399. $model = $this;
  400. return view($view, compact('model', 'value', 'name'))->render();
  401. });
  402. }
  403. /**
  404. * Set escape or not for this field.
  405. *
  406. * @param bool $escape
  407. *
  408. * @return $this
  409. */
  410. public function escape($escape = true)
  411. {
  412. $this->escape = $escape;
  413. return $this;
  414. }
  415. /**
  416. * Unescape for this field.
  417. *
  418. * @return Field
  419. */
  420. public function unescape()
  421. {
  422. return $this->escape(false);
  423. }
  424. /**
  425. * @param Fluent $model
  426. *
  427. * @return void
  428. */
  429. public function fill(Fluent $model)
  430. {
  431. $this->value($model->get($this->name));
  432. }
  433. /**
  434. * Get or set value for this field.
  435. *
  436. * @param mixed $value
  437. *
  438. * @return $this|mixed
  439. */
  440. public function value($value = null)
  441. {
  442. if ($value === null) {
  443. return $this->value;
  444. }
  445. $this->value = $value;
  446. return $this;
  447. }
  448. /**
  449. * @return $this
  450. */
  451. public function wrap(bool $wrap = true)
  452. {
  453. $this->border = $wrap;
  454. return $this;
  455. }
  456. /**
  457. * @param string $method
  458. * @param array $arguments
  459. *
  460. * @return $this
  461. */
  462. public function __call($method, $arguments = [])
  463. {
  464. if ($class = Arr::get(static::$extendedFields, $method)) {
  465. return $this->callExtendedField($class, $arguments);
  466. }
  467. if (static::hasMacro($method)) {
  468. return $this->macroCall($method, $arguments);
  469. }
  470. return $this->callSupportDisplayer($method, $arguments);
  471. }
  472. /**
  473. * Call extended field.
  474. *
  475. * @param string|AbstractField|\Closure $abstract
  476. * @param array $arguments
  477. *
  478. * @return Field
  479. */
  480. protected function callExtendedField($abstract, $arguments = [])
  481. {
  482. if ($abstract instanceof \Closure) {
  483. return $this->as($abstract, ...$arguments);
  484. }
  485. if (is_string($abstract) && class_exists($abstract)) {
  486. /** @var AbstractField $extend */
  487. $extend = new $abstract();
  488. }
  489. if ($abstract instanceof AbstractField) {
  490. /** @var AbstractField $extend */
  491. $extend = $abstract;
  492. }
  493. if (! isset($extend)) {
  494. admin_warning("[$abstract] is not a valid Show field.");
  495. return $this;
  496. }
  497. if (! $extend->escape) {
  498. $this->unescape();
  499. }
  500. $field = $this;
  501. return $this->as(function ($value) use ($extend, $field, $arguments) {
  502. if (! $extend->border) {
  503. $field->wrap(false);
  504. }
  505. $extend->setValue($value)->setModel($this);
  506. return $extend->render(...$arguments);
  507. });
  508. }
  509. /**
  510. * Call Illuminate/Support.
  511. *
  512. * @param string $abstract
  513. * @param array $arguments
  514. *
  515. * @return $this
  516. */
  517. protected function callSupportDisplayer($abstract, $arguments)
  518. {
  519. return $this->as(function ($value) use ($abstract, $arguments) {
  520. if (is_array($value) || $value instanceof Arrayable) {
  521. return call_user_func_array([collect($value), $abstract], $arguments);
  522. }
  523. if (is_string($value)) {
  524. return call_user_func_array([Str::class, $abstract], array_merge([$value], $arguments));
  525. }
  526. return $value;
  527. });
  528. }
  529. /**
  530. * Get all variables passed to field view.
  531. *
  532. * @return array
  533. */
  534. protected function variables()
  535. {
  536. return [
  537. 'content' => $this->value,
  538. 'escape' => $this->escape,
  539. 'label' => $this->getLabel(),
  540. 'wrapped' => $this->border,
  541. 'width' => $this->width,
  542. ];
  543. }
  544. /**
  545. * Render this field.
  546. *
  547. * @return string
  548. */
  549. public function render()
  550. {
  551. if (static::hasDefinition($this->name)) {
  552. $this->useDefinedColumn();
  553. }
  554. if ($this->showAs->isNotEmpty()) {
  555. $this->showAs->each(function ($callable) {
  556. [$callable, $params] = $callable;
  557. if (! $callable instanceof \Closure) {
  558. $this->value = $callable;
  559. return;
  560. }
  561. $this->value = $callable->call(
  562. $this->parent->model(),
  563. $this->value,
  564. ...$params
  565. );
  566. });
  567. }
  568. return view($this->view, $this->variables());
  569. }
  570. /**
  571. * Use a defined column.
  572. *
  573. * @throws \Exception
  574. */
  575. protected function useDefinedColumn()
  576. {
  577. $class = static::$definitions[$this->name];
  578. if (! $class instanceof \Closure) {
  579. throw new \Exception("Invalid column definition [$class]");
  580. }
  581. $this->as($class);
  582. }
  583. /**
  584. * Register custom field.
  585. *
  586. * @param string $abstract
  587. * @param string $class
  588. *
  589. * @return void
  590. */
  591. public static function extend($abstract, $class)
  592. {
  593. static::$extendedFields[$abstract] = $class;
  594. }
  595. /**
  596. * @return array
  597. */
  598. public static function extensions()
  599. {
  600. return static::$extendedFields;
  601. }
  602. }