Show.php 13 KB

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