Show.php 15 KB

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