Show.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  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. }
  154. /**
  155. * @param Fluent|null $model
  156. *
  157. * @return Fluent
  158. */
  159. public function model(Fluent $model = null)
  160. {
  161. if ($model === null) {
  162. if (! $this->model) {
  163. $this->setupModel();
  164. }
  165. return $this->model;
  166. }
  167. $this->model = $model;
  168. }
  169. protected function setupModel()
  170. {
  171. if ($this->repository) {
  172. $this->model(new Fluent($this->repository->detail($this)));
  173. } else {
  174. $this->model(new Fluent());
  175. }
  176. }
  177. /**
  178. * Set a view to render.
  179. *
  180. * @param string $view
  181. * @param array $variables
  182. *
  183. * @return $this
  184. */
  185. public function view($view, $variables = [])
  186. {
  187. if (! empty($variables)) {
  188. $this->with($variables);
  189. }
  190. $this->panel->view($view);
  191. return $this;
  192. }
  193. /**
  194. * Add variables to show view.
  195. *
  196. * @param array $variables
  197. *
  198. * @return $this
  199. */
  200. public function with($variables = [])
  201. {
  202. $this->panel->with($variables);
  203. return $this;
  204. }
  205. /**
  206. * @return $this
  207. */
  208. public function wrap(\Closure $wrapper)
  209. {
  210. $this->panel->wrap($wrapper);
  211. return $this;
  212. }
  213. /**
  214. * Initialize the contents to show.
  215. */
  216. protected function initContents()
  217. {
  218. $this->fields = new Collection();
  219. $this->relations = new Collection();
  220. }
  221. /**
  222. * Initialize panel.
  223. */
  224. protected function initPanel()
  225. {
  226. $this->panel = new Panel($this);
  227. }
  228. /**
  229. * Get panel instance.
  230. *
  231. * @return Panel
  232. */
  233. public function panel()
  234. {
  235. return $this->panel;
  236. }
  237. /**
  238. * @param \Closure|array|AbstractTool|Renderable|Htmlable|string $callback
  239. *
  240. * @return $this|Tools
  241. */
  242. public function tools($callback = null)
  243. {
  244. if ($callback === null) {
  245. return $this->panel->tools();
  246. }
  247. if ($callback instanceof \Closure) {
  248. $callback->call($this->model, $this->panel->tools());
  249. return $this;
  250. }
  251. if (! is_array($callback)) {
  252. $callback = [$callback];
  253. }
  254. foreach ($callback as $tool) {
  255. $this->panel->tools()->append($tool);
  256. }
  257. return $this;
  258. }
  259. /**
  260. * Add a model field to show.
  261. *
  262. * @param string $name
  263. * @param string $label
  264. *
  265. * @return Field
  266. */
  267. public function field($name, $label = '')
  268. {
  269. return $this->addField($name, $label);
  270. }
  271. /**
  272. * Get fields or add multiple fields.
  273. *
  274. * @param array $fields
  275. *
  276. * @return $this|Collection
  277. */
  278. public function fields(array $fields = null)
  279. {
  280. if ($fields === null) {
  281. return $this->fields;
  282. }
  283. if (! Arr::isAssoc($fields)) {
  284. $fields = array_combine($fields, $fields);
  285. }
  286. foreach ($fields as $field => $label) {
  287. $this->field($field, $label);
  288. }
  289. return $this;
  290. }
  291. /**
  292. * @return Collection
  293. */
  294. public function relations()
  295. {
  296. return $this->relations;
  297. }
  298. /**
  299. * Show all fields.
  300. *
  301. * @return Show
  302. */
  303. public function all()
  304. {
  305. $fields = array_keys($this->model()->toArray());
  306. return $this->fields($fields);
  307. }
  308. /**
  309. * Add a relation to show.
  310. *
  311. * @param string $name
  312. * @param string|\Closure $label
  313. * @param null|\Closure $builder
  314. *
  315. * @return Relation
  316. */
  317. public function relation($name, $label, $builder = null)
  318. {
  319. if (is_null($builder)) {
  320. $builder = $label;
  321. $label = '';
  322. }
  323. return $this->addRelation($name, $builder, $label);
  324. }
  325. /**
  326. * Add a model field to show.
  327. *
  328. * @param string $name
  329. * @param string $label
  330. *
  331. * @return Field
  332. */
  333. protected function addField($name, $label = '')
  334. {
  335. $field = new Field($name, $label);
  336. $field->setParent($this);
  337. $this->overwriteExistingField($name);
  338. $this->fields->push($field);
  339. return $field;
  340. }
  341. /**
  342. * Add a relation panel to show.
  343. *
  344. * @param string $name
  345. * @param \Closure $builder
  346. * @param string $label
  347. *
  348. * @return Relation
  349. */
  350. protected function addRelation($name, $builder, $label = '')
  351. {
  352. $relation = new Relation($name, $builder, $label);
  353. $relation->setParent($this);
  354. $this->overwriteExistingRelation($name);
  355. $this->relations->push($relation);
  356. return $relation;
  357. }
  358. /**
  359. * Overwrite existing field.
  360. *
  361. * @param string $name
  362. */
  363. protected function overwriteExistingField($name)
  364. {
  365. if ($this->fields->isEmpty()) {
  366. return;
  367. }
  368. $this->fields = $this->fields->filter(
  369. function (Field $field) use ($name) {
  370. return $field->getName() != $name;
  371. }
  372. );
  373. }
  374. /**
  375. * Overwrite existing relation.
  376. *
  377. * @param string $name
  378. */
  379. protected function overwriteExistingRelation($name)
  380. {
  381. if ($this->relations->isEmpty()) {
  382. return;
  383. }
  384. $this->relations = $this->relations->filter(
  385. function (Relation $relation) use ($name) {
  386. return $relation->getName() != $name;
  387. }
  388. );
  389. }
  390. /**
  391. * @return Repository
  392. */
  393. public function getRepository(): Repository
  394. {
  395. return $this->repository;
  396. }
  397. /**
  398. * Show a divider.
  399. */
  400. public function divider()
  401. {
  402. $this->fields->push(new Divider());
  403. }
  404. /**
  405. * Show a divider.
  406. */
  407. public function newline()
  408. {
  409. $this->fields->push(new Newline());
  410. }
  411. /**
  412. * Disable `list` tool.
  413. *
  414. * @return $this
  415. */
  416. public function disableListButton(bool $disable = true)
  417. {
  418. $this->panel->tools()->disableList($disable);
  419. return $this;
  420. }
  421. /**
  422. * Disable `delete` tool.
  423. *
  424. * @return $this
  425. */
  426. public function disableDeleteButton(bool $disable = true)
  427. {
  428. $this->panel->tools()->disableDelete($disable);
  429. return $this;
  430. }
  431. /**
  432. * Disable `edit` tool.
  433. *
  434. * @return $this
  435. */
  436. public function disableEditButton(bool $disable = true)
  437. {
  438. $this->panel->tools()->disableEdit($disable);
  439. return $this;
  440. }
  441. /**
  442. * Show quick edit tool.
  443. *
  444. * @param null|string $width
  445. * @param null|string $height
  446. *
  447. * @return $this
  448. */
  449. public function showQuickEdit(?string $width = null, ?string $height = null)
  450. {
  451. $this->panel->tools()->showQuickEdit($width, $height);
  452. return $this;
  453. }
  454. /**
  455. * Disable quick edit tool.
  456. *
  457. * @return $this
  458. */
  459. public function disableQuickEdit()
  460. {
  461. $this->panel->tools()->disableQuickEdit();
  462. return $this;
  463. }
  464. /**
  465. * Set resource path.
  466. *
  467. * @param string $resource
  468. *
  469. * @return $this
  470. */
  471. public function resource(string $resource)
  472. {
  473. if ($resource) {
  474. $this->resource = admin_url($resource);
  475. }
  476. return $this;
  477. }
  478. /**
  479. * Get resource path.
  480. *
  481. * @return string
  482. */
  483. public function getResource()
  484. {
  485. if (empty($this->resource)) {
  486. $path = request()->path();
  487. $segments = explode('/', $path);
  488. array_pop($segments);
  489. $this->resource = url(implode('/', $segments));
  490. }
  491. return $this->resource;
  492. }
  493. /**
  494. * Add field and relation dynamically.
  495. *
  496. * @param string $method
  497. * @param array $arguments
  498. *
  499. * @return Field
  500. */
  501. public function __call($method, $arguments = [])
  502. {
  503. if (static::hasMacro($method)) {
  504. return $this->macroCall($method, $arguments);
  505. }
  506. return $this->call($method, $arguments);
  507. }
  508. /**
  509. * @param $method
  510. * @param array $arguments
  511. *
  512. * @return bool|Show|Field|Relation
  513. */
  514. protected function call($method, $arguments = [])
  515. {
  516. $label = isset($arguments[0]) ? $arguments[0] : '';
  517. if ($field = $this->handleRelationField($method, $arguments)) {
  518. return $field;
  519. }
  520. return $this->addField($method, $label);
  521. }
  522. /**
  523. * Handle relation field.
  524. *
  525. * @param string $method
  526. * @param array $arguments
  527. *
  528. * @return $this|bool|Relation|Field
  529. */
  530. protected function handleRelationField($method, $arguments)
  531. {
  532. if (count($arguments) == 1 && $arguments[0] instanceof \Closure) {
  533. return $this->addRelation($method, $arguments[0]);
  534. } elseif (count($arguments) == 2 && $arguments[1] instanceof \Closure) {
  535. return $this->addRelation($method, $arguments[1], $arguments[0]);
  536. }
  537. return false;
  538. }
  539. /**
  540. * Render the show panels.
  541. *
  542. * @return string
  543. */
  544. public function render()
  545. {
  546. try {
  547. $model = $this->model();
  548. if (is_callable($this->builder)) {
  549. call_user_func($this->builder, $this);
  550. }
  551. if ($this->fields->isEmpty()) {
  552. $this->all();
  553. }
  554. if (is_array($this->builder)) {
  555. $this->fields($this->builder);
  556. }
  557. $this->fields->each->fill($model);
  558. $this->relations->each->model($model);
  559. $this->callComposing();
  560. $data = [
  561. 'panel' => $this->panel->fill($this->fields),
  562. 'relations' => $this->relations,
  563. ];
  564. return view($this->view, $data)->render();
  565. } catch (\Throwable $e) {
  566. return Admin::makeExceptionHandler()->renderException($e);
  567. }
  568. }
  569. /**
  570. * Add a model field to show.
  571. *
  572. * @param string $name
  573. *
  574. * @return Field|Collection
  575. */
  576. public function __get($name)
  577. {
  578. return $this->call($name);
  579. }
  580. }