Field.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  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\HasVariables;
  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. use HasVariables;
  21. use 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. if($label) return $label;
  139. $label = admin_trans_field($this->name);
  140. return str_replace('_', ' ', $label);
  141. }
  142. /**
  143. * Get label of the column.
  144. *
  145. * @return mixed
  146. */
  147. public function getLabel()
  148. {
  149. return $this->label;
  150. }
  151. /**
  152. * Field display callback.
  153. *
  154. * @param mixed $callable
  155. *
  156. * @return $this
  157. */
  158. public function as($callable, ...$params)
  159. {
  160. $this->showAs->push([$callable, $params]);
  161. return $this;
  162. }
  163. /**
  164. * Display field using array value map.
  165. *
  166. * @param array $values
  167. * @param null $default
  168. *
  169. * @return $this
  170. */
  171. public function using(array $values, $default = null)
  172. {
  173. return $this->as(function ($value) use ($values, $default) {
  174. if (is_null($value)) {
  175. return $default;
  176. }
  177. return Arr::get($values, $value, $default);
  178. });
  179. }
  180. /**
  181. * Show field as a image.
  182. *
  183. * @param string $server
  184. * @param int $width
  185. * @param int $height
  186. *
  187. * @return $this
  188. */
  189. public function image($server = '', $width = 200, $height = 200)
  190. {
  191. return $this->unescape()->as(function ($path) use ($server, $width, $height) {
  192. if (empty($path)) {
  193. return '';
  194. }
  195. return collect((array) $path)->transform(function ($path) use ($server, $width, $height) {
  196. if (url()->isValidUrl($path)) {
  197. $src = $path;
  198. } elseif ($server) {
  199. $src = rtrim($server, '/').'/'.ltrim($path, '/');
  200. } else {
  201. $disk = config('admin.upload.disk');
  202. if (config("filesystems.disks.{$disk}")) {
  203. $src = Storage::disk($disk)->url($path);
  204. } else {
  205. return '';
  206. }
  207. }
  208. return "<img data-action='preview-img' src='$src' style='max-width:{$width}px;max-height:{$height}px' class='img' />";
  209. })->implode('&nbsp;');
  210. });
  211. }
  212. /**
  213. * Show field as a file.
  214. *
  215. * @param string $server
  216. * @param bool $download
  217. *
  218. * @return Field
  219. */
  220. public function file($server = '', $download = true)
  221. {
  222. $field = $this;
  223. return $this->unescape()->as(function ($path) use ($server , $field) {
  224. $name = basename($path);
  225. $field->wrap(false);
  226. $size = $url = '';
  227. if (url()->isValidUrl($path)) {
  228. $url = $path;
  229. } elseif ($server) {
  230. $url = $server.$path;
  231. } else {
  232. $storage = Storage::disk(config('admin.upload.disk'));
  233. if ($storage->exists($path)) {
  234. $url = $storage->url($path);
  235. $size = ($storage->size($path) / 1000).'KB';
  236. }
  237. }
  238. if (! $url) {
  239. return '';
  240. }
  241. $icon = Helper::getFileIcon($name);
  242. return <<<HTML
  243. <ul class="mailbox-attachments clearfix">
  244. <li style="margin-bottom: 0;">
  245. <span class="mailbox-attachment-icon"><i class="{$icon}"></i></span>
  246. <div class="mailbox-attachment-info">
  247. <div class="mailbox-attachment-name">
  248. <i class="fa fa-paperclip"></i> {$name}
  249. </div>
  250. <span class="mailbox-attachment-size">
  251. {$size}&nbsp;
  252. <a href="{$url}" class="btn btn-white btn-xs pull-right" target="_blank"><i class="fa fa-cloud-download"></i></a>
  253. </span>
  254. </div>
  255. </li>
  256. </ul>
  257. HTML;
  258. });
  259. }
  260. /**
  261. * Show field as a link.
  262. *
  263. * @param string $href
  264. * @param string $target
  265. *
  266. * @return Field
  267. */
  268. public function link($href = '', $target = '_blank')
  269. {
  270. return $this->unescape()->as(function ($link) use ($href, $target) {
  271. $href = $href ?: $link;
  272. return "<a href='$href' target='{$target}'>{$link}</a>";
  273. });
  274. }
  275. /**
  276. * Show field as labels.
  277. *
  278. * @param string $style
  279. *
  280. * @return Field
  281. */
  282. public function label($style = 'primary')
  283. {
  284. $self = $this;
  285. return $this->unescape()->as(function ($value) use ($self, $style) {
  286. [$class, $background] = $self->formatStyle($style);
  287. return collect($value)->map(function ($name) use ($class, $background) {
  288. return "<span class='label bg-{$class}' $background>$name</span>";
  289. })->implode('&nbsp;');
  290. });
  291. }
  292. /**
  293. * Add a `dot` before column text.
  294. *
  295. * @param array $options
  296. * @param string $default
  297. *
  298. * @return $this
  299. */
  300. public function dot($options = [], $default = 'default')
  301. {
  302. return $this->unescape()->prepend(function ($_, $original) use ($options, $default) {
  303. $style = is_null($original) ? $default : Arr::get((array) $options, $original, $default);
  304. $style = $style === 'default' ? 'dark70' : $style;
  305. $background = Admin::color()->get($style, $style);
  306. return "<i class='fa fa-circle' style='font-size: 13px;color: {$background}'></i>&nbsp;&nbsp;";
  307. });
  308. }
  309. /**
  310. * Show field as badges.
  311. *
  312. * @param string $style
  313. *
  314. * @return Field
  315. */
  316. public function badge($style = 'blue')
  317. {
  318. $self = $this;
  319. return $this->unescape()->as(function ($value) use ($self, $style) {
  320. [$class, $background] = $self->formatStyle($style);
  321. return collect($value)->map(function ($name) use ($class, $background) {
  322. return "<span class='badge bg-{$class}' $background>$name</span>";
  323. })->implode('&nbsp;');
  324. });
  325. }
  326. /**
  327. * @param $style
  328. *
  329. * @return array
  330. */
  331. public function formatStyle($style)
  332. {
  333. $class = 'default';
  334. $background = '';
  335. if ($style !== 'default') {
  336. $class = '';
  337. $style = Admin::color()->get($style, $style);
  338. $background = "style='background:{$style}'";
  339. }
  340. return [$class, $background];
  341. }
  342. /**
  343. * Show field as json code.
  344. *
  345. * @return Field
  346. */
  347. public function json()
  348. {
  349. $field = $this;
  350. return $this->unescape()->as(function ($value) use ($field) {
  351. $content = is_string($value) ? json_decode($value, true) : $value;
  352. $field->wrap(false);
  353. return Dump::make($content);
  354. });
  355. }
  356. /**
  357. * @param string $val
  358. *
  359. * @return $this
  360. */
  361. public function prepend($val)
  362. {
  363. $name = $this->name;
  364. return $this->as(function ($v) use (&$val, $name) {
  365. if ($val instanceof \Closure) {
  366. $val = $val->call($this, $v, Arr::get($this, $name));
  367. }
  368. if (is_array($v)) {
  369. array_unshift($v, $val);
  370. return $v;
  371. } elseif ($v instanceof Collection) {
  372. return $v->prepend($val);
  373. }
  374. return $val.$v;
  375. });
  376. }
  377. /**
  378. * @param string $val
  379. *
  380. * @return $this
  381. */
  382. public function append($val)
  383. {
  384. $name = $this->name;
  385. return $this->as(function ($v) use (&$val, $name) {
  386. if ($val instanceof \Closure) {
  387. $val = $val->call($this, $v, Arr::get($this, $name));
  388. }
  389. if (is_array($v)) {
  390. array_push($v, $val);
  391. return $v;
  392. } elseif ($v instanceof Collection) {
  393. return $v->push($val);
  394. }
  395. return $v.$val;
  396. });
  397. }
  398. /**
  399. * Split a string by string.
  400. *
  401. * @param string $d
  402. *
  403. * @return $this
  404. */
  405. public function explode(string $d = ',')
  406. {
  407. return $this->as(function ($v) use ($d) {
  408. if (is_array($v) || $v instanceof Arrayable) {
  409. return $v;
  410. }
  411. return $v ? explode($d, $v) : [];
  412. });
  413. }
  414. /**
  415. * Render this column with the given view.
  416. *
  417. * @param string $view
  418. *
  419. * @return $this
  420. */
  421. public function view($view)
  422. {
  423. $name = $this->name;
  424. return $this->unescape()->as(function ($value) use ($view, $name) {
  425. $model = $this;
  426. return view($view, compact('model', 'value', 'name'))->render();
  427. });
  428. }
  429. /**
  430. * Set escape or not for this field.
  431. *
  432. * @param bool $escape
  433. *
  434. * @return $this
  435. */
  436. public function escape($escape = true)
  437. {
  438. $this->escape = $escape;
  439. return $this;
  440. }
  441. /**
  442. * Unescape for this field.
  443. *
  444. * @return Field
  445. */
  446. public function unescape()
  447. {
  448. return $this->escape(false);
  449. }
  450. /**
  451. * @param Fluent|\Illuminate\Database\Eloquent\Model $model
  452. *
  453. * @return void
  454. */
  455. public function fill($model)
  456. {
  457. $this->value(Arr::get($model->toArray(), $this->name));
  458. }
  459. /**
  460. * Get or set value for this field.
  461. *
  462. * @param mixed $value
  463. *
  464. * @return $this|mixed
  465. */
  466. public function value($value = null)
  467. {
  468. if ($value === null) {
  469. return $this->value;
  470. }
  471. $this->value = $value;
  472. return $this;
  473. }
  474. /**
  475. * @return $this
  476. */
  477. public function wrap(bool $wrap = true)
  478. {
  479. $this->border = $wrap;
  480. return $this;
  481. }
  482. /**
  483. * @param mixed $value
  484. * @param callable $callback
  485. *
  486. * @return $this|mixed
  487. */
  488. public function when($value, $callback)
  489. {
  490. if ($value) {
  491. return $callback($this, $value) ?: $this;
  492. }
  493. return $this;
  494. }
  495. /**
  496. * @param string $method
  497. * @param array $arguments
  498. *
  499. * @return $this
  500. */
  501. public function __call($method, $arguments = [])
  502. {
  503. if ($class = Arr::get(static::$extendedFields, $method)) {
  504. return $this->callExtendedField($class, $arguments);
  505. }
  506. if (static::hasMacro($method)) {
  507. return $this->macroCall($method, $arguments);
  508. }
  509. return $this->callSupportDisplayer($method, $arguments);
  510. }
  511. /**
  512. * Call extended field.
  513. *
  514. * @param string|AbstractField|\Closure $abstract
  515. * @param array $arguments
  516. *
  517. * @return Field
  518. */
  519. protected function callExtendedField($abstract, $arguments = [])
  520. {
  521. if ($abstract instanceof \Closure) {
  522. return $this->as($abstract, ...$arguments);
  523. }
  524. if (is_string($abstract) && class_exists($abstract)) {
  525. /** @var AbstractField $extend */
  526. $extend = new $abstract();
  527. }
  528. if ($abstract instanceof AbstractField) {
  529. /** @var AbstractField $extend */
  530. $extend = $abstract;
  531. }
  532. if (! isset($extend)) {
  533. admin_warning("[$abstract] is not a valid Show field.");
  534. return $this;
  535. }
  536. if (! $extend->escape) {
  537. $this->unescape();
  538. }
  539. $field = $this;
  540. return $this->as(function ($value) use ($extend, $field, $arguments) {
  541. if (! $extend->border) {
  542. $field->wrap(false);
  543. }
  544. $extend->setValue($value)->setModel($this);
  545. return $extend->render(...$arguments);
  546. });
  547. }
  548. /**
  549. * Call Illuminate/Support.
  550. *
  551. * @param string $abstract
  552. * @param array $arguments
  553. *
  554. * @return $this
  555. */
  556. protected function callSupportDisplayer($abstract, $arguments)
  557. {
  558. return $this->as(function ($value) use ($abstract, $arguments) {
  559. if (is_array($value) || $value instanceof Arrayable) {
  560. return call_user_func_array([collect($value), $abstract], $arguments);
  561. }
  562. if (is_string($value)) {
  563. return call_user_func_array([Str::class, $abstract], array_merge([$value], $arguments));
  564. }
  565. return $value;
  566. });
  567. }
  568. /**
  569. * Get all variables passed to field view.
  570. *
  571. * @return array
  572. */
  573. protected function defaultVariables()
  574. {
  575. return [
  576. 'content' => $this->value,
  577. 'escape' => $this->escape,
  578. 'label' => $this->getLabel(),
  579. 'wrapped' => $this->border,
  580. 'width' => $this->width,
  581. ];
  582. }
  583. /**
  584. * Render this field.
  585. *
  586. * @return string
  587. */
  588. public function render()
  589. {
  590. if ($this->showAs->isNotEmpty()) {
  591. $this->showAs->each(function ($callable) {
  592. [$callable, $params] = $callable;
  593. if (! $callable instanceof \Closure) {
  594. $this->value = $callable;
  595. return;
  596. }
  597. $this->value = $callable->call(
  598. $this->parent->model(),
  599. $this->value,
  600. ...$params
  601. );
  602. });
  603. }
  604. return view($this->view, $this->variables());
  605. }
  606. /**
  607. * Register custom field.
  608. *
  609. * @param string $abstract
  610. * @param string $class
  611. *
  612. * @return void
  613. */
  614. public static function extend($abstract, $class)
  615. {
  616. static::$extendedFields[$abstract] = $class;
  617. }
  618. /**
  619. * @return array
  620. */
  621. public static function extensions()
  622. {
  623. return static::$extendedFields;
  624. }
  625. }