Show.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. <?php
  2. namespace Dcat\Admin;
  3. use Dcat\Admin\Contracts\Repository;
  4. use Dcat\Admin\Show\AbstractTool;
  5. use Dcat\Admin\Show\Divider;
  6. use Dcat\Admin\Show\Field;
  7. use Dcat\Admin\Show\Newline;
  8. use Dcat\Admin\Show\Panel;
  9. use Dcat\Admin\Show\Relation;
  10. use Dcat\Admin\Show\Tools;
  11. use Dcat\Admin\Traits\HasBuilderEvents;
  12. use Illuminate\Contracts\Support\Arrayable;
  13. use Illuminate\Contracts\Support\Htmlable;
  14. use Illuminate\Contracts\Support\Renderable;
  15. use Illuminate\Database\Eloquent\Builder;
  16. use Illuminate\Database\Eloquent\Model;
  17. use Illuminate\Support\Arr;
  18. use Illuminate\Support\Collection;
  19. use Illuminate\Support\Fluent;
  20. use Illuminate\Support\Traits\Macroable;
  21. class Show implements Renderable
  22. {
  23. use HasBuilderEvents,
  24. Macroable {
  25. __call as macroCall;
  26. }
  27. /**
  28. * @var string
  29. */
  30. protected $view = 'admin::show';
  31. /**
  32. * @var Repository
  33. */
  34. protected $repository;
  35. /**
  36. * @var mixed
  37. */
  38. protected $__id;
  39. /**
  40. * @var string
  41. */
  42. protected $keyName = 'id';
  43. /**
  44. * @var Fluent
  45. */
  46. protected $model;
  47. /**
  48. * Show panel builder.
  49. *
  50. * @var callable
  51. */
  52. protected $builder;
  53. /**
  54. * Resource path for this show page.
  55. *
  56. * @var string
  57. */
  58. protected $resource;
  59. /**
  60. * Fields to be show.
  61. *
  62. * @var Collection
  63. */
  64. protected $fields;
  65. /**
  66. * Relations to be show.
  67. *
  68. * @var Collection
  69. */
  70. protected $relations;
  71. /**
  72. * @var Panel
  73. */
  74. protected $panel;
  75. /**
  76. * Show constructor.
  77. *
  78. * @param Model|Builder|Repository|array|Arrayable $model
  79. * @param \Closure $builder
  80. */
  81. public function __construct($model = null, ?\Closure $builder = null)
  82. {
  83. $this->initModel($model);
  84. $this->builder = $builder;
  85. $this->initPanel();
  86. $this->initContents();
  87. $this->callResolving();
  88. }
  89. protected function initModel($model)
  90. {
  91. if ($model instanceof Repository || $model instanceof Builder) {
  92. $this->repository = Admin::repository($model);
  93. } elseif ($model instanceof Model) {
  94. if ($key = $model->getKey()) {
  95. $this->key = $model->getKey();
  96. $this->keyName = $model->getKeyName();
  97. $this->model(new Fluent($model->toArray()));
  98. } else {
  99. $this->repository = Admin::repository($model);
  100. }
  101. } elseif ($model instanceof Arrayable) {
  102. $this->model(new Fluent($model->toArray()));
  103. } elseif (is_array($model)) {
  104. $this->model(new Fluent($model));
  105. } else {
  106. $this->model(new Fluent());
  107. }
  108. }
  109. /**
  110. * Create a show instance.
  111. *
  112. * @param mixed ...$params
  113. *
  114. * @return $this
  115. */
  116. public static function make(...$params)
  117. {
  118. return new static(...$params);
  119. }
  120. /**
  121. * @param string $value
  122. *
  123. * @return $this
  124. */
  125. public function setKeyName(string $value)
  126. {
  127. $this->keyName = $value;
  128. return $this;
  129. }
  130. /**
  131. * Get primary key name of model.
  132. *
  133. * @return string
  134. */
  135. public function getKeyName()
  136. {
  137. if (! $this->repository) {
  138. return $this->keyName;
  139. }
  140. return $this->keyName ?: $this->repository->getKeyName();
  141. }
  142. /**
  143. * @param mixed $id
  144. *
  145. * @return mixed
  146. */
  147. public function key($id = null)
  148. {
  149. if ($id === null) {
  150. return $this->__id;
  151. }
  152. $this->__id = $id;
  153. return $this;
  154. }
  155. /**
  156. * @param Fluent|null $model
  157. *
  158. * @return Fluent|$this
  159. */
  160. public function model(Fluent $model = null)
  161. {
  162. if ($model === null) {
  163. if (! $this->model) {
  164. $this->setupModel();
  165. }
  166. return $this->model;
  167. }
  168. $this->model = $model;
  169. return $this;
  170. }
  171. protected function setupModel()
  172. {
  173. if ($this->repository) {
  174. $this->model(new Fluent($this->repository->detail($this)));
  175. } else {
  176. $this->model(new Fluent());
  177. }
  178. }
  179. /**
  180. * Set a view to render.
  181. *
  182. * @param string $view
  183. * @param array $variables
  184. *
  185. * @return $this
  186. */
  187. public function view($view, $variables = [])
  188. {
  189. if (! empty($variables)) {
  190. $this->with($variables);
  191. }
  192. $this->panel->view($view);
  193. return $this;
  194. }
  195. /**
  196. * Add variables to show view.
  197. *
  198. * @param array $variables
  199. *
  200. * @return $this
  201. */
  202. public function with($variables = [])
  203. {
  204. $this->panel->with($variables);
  205. return $this;
  206. }
  207. /**
  208. * @return $this
  209. */
  210. public function wrap(\Closure $wrapper)
  211. {
  212. $this->panel->wrap($wrapper);
  213. return $this;
  214. }
  215. /**
  216. * Initialize the contents to show.
  217. */
  218. protected function initContents()
  219. {
  220. $this->fields = new Collection();
  221. $this->relations = new Collection();
  222. }
  223. /**
  224. * Initialize panel.
  225. */
  226. protected function initPanel()
  227. {
  228. $this->panel = new Panel($this);
  229. }
  230. /**
  231. * Get panel instance.
  232. *
  233. * @return Panel
  234. */
  235. public function panel()
  236. {
  237. return $this->panel;
  238. }
  239. /**
  240. * @param \Closure|array|AbstractTool|Renderable|Htmlable|string $callback
  241. *
  242. * @return $this|Tools
  243. */
  244. public function tools($callback = null)
  245. {
  246. if ($callback === null) {
  247. return $this->panel->tools();
  248. }
  249. if ($callback instanceof \Closure) {
  250. $callback->call($this->model, $this->panel->tools());
  251. return $this;
  252. }
  253. if (! is_array($callback)) {
  254. $callback = [$callback];
  255. }
  256. foreach ($callback as $tool) {
  257. $this->panel->tools()->append($tool);
  258. }
  259. return $this;
  260. }
  261. /**
  262. * Add a model field to show.
  263. *
  264. * @param string $name
  265. * @param string $label
  266. *
  267. * @return Field
  268. */
  269. public function field($name, $label = '')
  270. {
  271. return $this->addField($name, $label);
  272. }
  273. /**
  274. * Get fields or add multiple fields.
  275. *
  276. * @param array $fields
  277. *
  278. * @return $this|Collection
  279. */
  280. public function fields(array $fields = null)
  281. {
  282. if ($fields === null) {
  283. return $this->fields;
  284. }
  285. if (! Arr::isAssoc($fields)) {
  286. $fields = array_combine($fields, $fields);
  287. }
  288. foreach ($fields as $field => $label) {
  289. $this->field($field, $label);
  290. }
  291. return $this;
  292. }
  293. /**
  294. * @return Collection
  295. */
  296. public function relations()
  297. {
  298. return $this->relations;
  299. }
  300. /**
  301. * Show all fields.
  302. *
  303. * @return Show
  304. */
  305. public function all()
  306. {
  307. $fields = array_keys($this->model()->toArray());
  308. return $this->fields($fields);
  309. }
  310. /**
  311. * Add a relation to show.
  312. *
  313. * @param string $name
  314. * @param string|\Closure $label
  315. * @param null|\Closure $builder
  316. *
  317. * @return Relation
  318. */
  319. public function relation($name, $label, $builder = null)
  320. {
  321. if (is_null($builder)) {
  322. $builder = $label;
  323. $label = '';
  324. }
  325. return $this->addRelation($name, $builder, $label);
  326. }
  327. /**
  328. * Add a model field to show.
  329. *
  330. * @param string $name
  331. * @param string $label
  332. *
  333. * @return Field
  334. */
  335. protected function addField($name, $label = '')
  336. {
  337. $field = new Field($name, $label);
  338. $field->setParent($this);
  339. $this->overwriteExistingField($name);
  340. $this->fields->push($field);
  341. return $field;
  342. }
  343. /**
  344. * Add a relation panel to show.
  345. *
  346. * @param string $name
  347. * @param \Closure $builder
  348. * @param string $label
  349. *
  350. * @return Relation
  351. */
  352. protected function addRelation($name, $builder, $label = '')
  353. {
  354. $relation = new Relation($name, $builder, $label);
  355. $relation->setParent($this);
  356. $this->overwriteExistingRelation($name);
  357. $this->relations->push($relation);
  358. return $relation;
  359. }
  360. /**
  361. * Overwrite existing field.
  362. *
  363. * @param string $name
  364. */
  365. protected function overwriteExistingField($name)
  366. {
  367. if ($this->fields->isEmpty()) {
  368. return;
  369. }
  370. $this->fields = $this->fields->filter(
  371. function (Field $field) use ($name) {
  372. return $field->getName() != $name;
  373. }
  374. );
  375. }
  376. /**
  377. * Overwrite existing relation.
  378. *
  379. * @param string $name
  380. */
  381. protected function overwriteExistingRelation($name)
  382. {
  383. if ($this->relations->isEmpty()) {
  384. return;
  385. }
  386. $this->relations = $this->relations->filter(
  387. function (Relation $relation) use ($name) {
  388. return $relation->getName() != $name;
  389. }
  390. );
  391. }
  392. /**
  393. * @return Repository
  394. */
  395. public function getRepository(): Repository
  396. {
  397. return $this->repository;
  398. }
  399. /**
  400. * Show a divider.
  401. */
  402. public function divider()
  403. {
  404. $this->fields->push(new Divider());
  405. }
  406. /**
  407. * Show a divider.
  408. */
  409. public function newline()
  410. {
  411. $this->fields->push(new Newline());
  412. }
  413. /**
  414. * Disable `list` tool.
  415. *
  416. * @return $this
  417. */
  418. public function disableListButton(bool $disable = true)
  419. {
  420. $this->panel->tools()->disableList($disable);
  421. return $this;
  422. }
  423. /**
  424. * Disable `delete` tool.
  425. *
  426. * @return $this
  427. */
  428. public function disableDeleteButton(bool $disable = true)
  429. {
  430. $this->panel->tools()->disableDelete($disable);
  431. return $this;
  432. }
  433. /**
  434. * Disable `edit` tool.
  435. *
  436. * @return $this
  437. */
  438. public function disableEditButton(bool $disable = true)
  439. {
  440. $this->panel->tools()->disableEdit($disable);
  441. return $this;
  442. }
  443. /**
  444. * Show quick edit tool.
  445. *
  446. * @param null|string $width
  447. * @param null|string $height
  448. *
  449. * @return $this
  450. */
  451. public function showQuickEdit(?string $width = null, ?string $height = null)
  452. {
  453. $this->panel->tools()->showQuickEdit($width, $height);
  454. return $this;
  455. }
  456. /**
  457. * Disable quick edit tool.
  458. *
  459. * @return $this
  460. */
  461. public function disableQuickEdit()
  462. {
  463. $this->panel->tools()->disableQuickEdit();
  464. return $this;
  465. }
  466. /**
  467. * Set resource path.
  468. *
  469. * @param string $resource
  470. *
  471. * @return $this
  472. */
  473. public function resource(string $resource)
  474. {
  475. if ($resource) {
  476. $this->resource = admin_url($resource);
  477. }
  478. return $this;
  479. }
  480. /**
  481. * Get resource path.
  482. *
  483. * @return string
  484. */
  485. public function getResource()
  486. {
  487. if (empty($this->resource)) {
  488. $path = request()->path();
  489. $segments = explode('/', $path);
  490. array_pop($segments);
  491. $this->resource = url(implode('/', $segments));
  492. }
  493. return $this->resource;
  494. }
  495. /**
  496. * Add field and relation dynamically.
  497. *
  498. * @param string $method
  499. * @param array $arguments
  500. *
  501. * @return Field
  502. */
  503. public function __call($method, $arguments = [])
  504. {
  505. if (static::hasMacro($method)) {
  506. return $this->macroCall($method, $arguments);
  507. }
  508. return $this->call($method, $arguments);
  509. }
  510. /**
  511. * @param $method
  512. * @param array $arguments
  513. *
  514. * @return bool|Show|Field|Relation
  515. */
  516. protected function call($method, $arguments = [])
  517. {
  518. $label = isset($arguments[0]) ? $arguments[0] : '';
  519. if ($field = $this->handleRelationField($method, $arguments)) {
  520. return $field;
  521. }
  522. return $this->addField($method, $label);
  523. }
  524. /**
  525. * Handle relation field.
  526. *
  527. * @param string $method
  528. * @param array $arguments
  529. *
  530. * @return $this|bool|Relation|Field
  531. */
  532. protected function handleRelationField($method, $arguments)
  533. {
  534. if (count($arguments) == 1 && $arguments[0] instanceof \Closure) {
  535. return $this->addRelation($method, $arguments[0]);
  536. } elseif (count($arguments) == 2 && $arguments[1] instanceof \Closure) {
  537. return $this->addRelation($method, $arguments[1], $arguments[0]);
  538. }
  539. return false;
  540. }
  541. /**
  542. * Render the show panels.
  543. *
  544. * @return string
  545. */
  546. public function render()
  547. {
  548. try {
  549. $model = $this->model();
  550. if (is_callable($this->builder)) {
  551. call_user_func($this->builder, $this);
  552. }
  553. if ($this->fields->isEmpty()) {
  554. $this->all();
  555. }
  556. if (is_array($this->builder)) {
  557. $this->fields($this->builder);
  558. }
  559. $this->fields->each->fill($model);
  560. $this->relations->each->model($model);
  561. $this->callComposing();
  562. $data = [
  563. 'panel' => $this->panel->fill($this->fields),
  564. 'relations' => $this->relations,
  565. ];
  566. return view($this->view, $data)->render();
  567. } catch (\Throwable $e) {
  568. return Admin::makeExceptionHandler()->renderException($e);
  569. }
  570. }
  571. /**
  572. * Add a model field to show.
  573. *
  574. * @param string $name
  575. *
  576. * @return Field|Collection
  577. */
  578. public function __get($name)
  579. {
  580. return $this->call($name);
  581. }
  582. }