Show.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. <?php
  2. namespace Encore\Admin;
  3. use Closure;
  4. use Encore\Admin\Show\Divider;
  5. use Encore\Admin\Show\Field;
  6. use Encore\Admin\Show\Panel;
  7. use Encore\Admin\Show\Relation;
  8. use Illuminate\Contracts\Support\Renderable;
  9. use Illuminate\Database\Eloquent\Model;
  10. use Illuminate\Database\Eloquent\Relations\BelongsTo;
  11. use Illuminate\Database\Eloquent\Relations\BelongsToMany;
  12. use Illuminate\Database\Eloquent\Relations\HasMany;
  13. use Illuminate\Database\Eloquent\Relations\HasManyThrough;
  14. use Illuminate\Database\Eloquent\Relations\HasOne;
  15. use Illuminate\Database\Eloquent\Relations\MorphMany;
  16. use Illuminate\Database\Eloquent\Relations\MorphOne;
  17. use Illuminate\Database\Eloquent\Relations\Relation as EloquentRelation;
  18. use Illuminate\Support\Arr;
  19. use Illuminate\Support\Collection;
  20. class Show implements Renderable
  21. {
  22. /**
  23. * The Eloquent model to show.
  24. *
  25. * @var Model
  26. */
  27. protected $model;
  28. /**
  29. * Show panel builder.
  30. *
  31. * @var callable
  32. */
  33. protected $builder;
  34. /**
  35. * Resource path for this show page.
  36. *
  37. * @var string
  38. */
  39. protected $resource;
  40. /**
  41. * Fields to be show.
  42. *
  43. * @var Collection
  44. */
  45. protected $fields;
  46. /**
  47. * Relations to be show.
  48. *
  49. * @var Collection
  50. */
  51. protected $relations;
  52. /**
  53. * @var Panel
  54. */
  55. protected $panel;
  56. /**
  57. * @var Closure
  58. */
  59. protected static $initCallback;
  60. /**
  61. * Show constructor.
  62. *
  63. * @param Model $model
  64. * @param mixed $builder
  65. */
  66. public function __construct($model, $builder = null)
  67. {
  68. $this->model = $model;
  69. $this->builder = $builder;
  70. $this->initPanel();
  71. $this->initContents();
  72. if (static::$initCallback instanceof Closure) {
  73. call_user_func(static::$initCallback, $this);
  74. }
  75. }
  76. /**
  77. * Initialize with user pre-defined default disables, etc.
  78. *
  79. * @param Closure $callback
  80. */
  81. public static function init(Closure $callback = null)
  82. {
  83. static::$initCallback = $callback;
  84. }
  85. /**
  86. * Initialize the contents to show.
  87. */
  88. protected function initContents()
  89. {
  90. $this->fields = new Collection();
  91. $this->relations = new Collection();
  92. }
  93. /**
  94. * Initialize panel.
  95. */
  96. protected function initPanel()
  97. {
  98. $this->panel = new Panel($this);
  99. }
  100. /**
  101. * Get panel instance.
  102. *
  103. * @return Panel
  104. */
  105. public function panel()
  106. {
  107. return $this->panel;
  108. }
  109. /**
  110. * Add a model field to show.
  111. *
  112. * @param string $name
  113. * @param string $label
  114. *
  115. * @return Field
  116. */
  117. public function field($name, $label = '')
  118. {
  119. return $this->addField($name, $label);
  120. }
  121. /**
  122. * Add multiple fields.
  123. *
  124. * @param array $fields
  125. *
  126. * @return $this
  127. */
  128. public function fields(array $fields = [])
  129. {
  130. if (!Arr::isAssoc($fields)) {
  131. $fields = array_combine($fields, $fields);
  132. }
  133. foreach ($fields as $field => $label) {
  134. $this->field($field, $label);
  135. }
  136. return $this;
  137. }
  138. /**
  139. * Show all fields.
  140. *
  141. * @return Show
  142. */
  143. public function all()
  144. {
  145. $fields = array_keys($this->model->getAttributes());
  146. return $this->fields($fields);
  147. }
  148. /**
  149. * Add a relation to show.
  150. *
  151. * @param string $name
  152. * @param string|\Closure $label
  153. * @param null|\Closure $builder
  154. *
  155. * @return Relation
  156. */
  157. public function relation($name, $label, $builder = null)
  158. {
  159. if (is_null($builder)) {
  160. $builder = $label;
  161. $label = '';
  162. }
  163. return $this->addRelation($name, $builder, $label);
  164. }
  165. /**
  166. * Add a model field to show.
  167. *
  168. * @param string $name
  169. * @param string $label
  170. *
  171. * @return Field
  172. */
  173. protected function addField($name, $label = '')
  174. {
  175. $field = new Field($name, $label);
  176. $field->setParent($this);
  177. $this->overwriteExistingField($name);
  178. return tap($field, function ($field) {
  179. $this->fields->push($field);
  180. });
  181. }
  182. /**
  183. * Add a relation panel to show.
  184. *
  185. * @param string $name
  186. * @param \Closure $builder
  187. * @param string $label
  188. *
  189. * @return Relation
  190. */
  191. protected function addRelation($name, $builder, $label = '')
  192. {
  193. $relation = new Relation($name, $builder, $label);
  194. $relation->setParent($this);
  195. $this->overwriteExistingRelation($name);
  196. return tap($relation, function ($relation) {
  197. $this->relations->push($relation);
  198. });
  199. }
  200. /**
  201. * Overwrite existing field.
  202. *
  203. * @param string $name
  204. */
  205. protected function overwriteExistingField($name)
  206. {
  207. if ($this->fields->isEmpty()) {
  208. return;
  209. }
  210. $this->fields = $this->fields->filter(
  211. function (Field $field) use ($name) {
  212. return $field->getName() != $name;
  213. }
  214. );
  215. }
  216. /**
  217. * Overwrite existing relation.
  218. *
  219. * @param string $name
  220. */
  221. protected function overwriteExistingRelation($name)
  222. {
  223. if ($this->relations->isEmpty()) {
  224. return;
  225. }
  226. $this->relations = $this->relations->filter(
  227. function (Relation $relation) use ($name) {
  228. return $relation->getName() != $name;
  229. }
  230. );
  231. }
  232. /**
  233. * Show a divider.
  234. */
  235. public function divider()
  236. {
  237. $this->fields->push(new Divider());
  238. }
  239. /**
  240. * Set resource path.
  241. *
  242. * @param string $resource
  243. *
  244. * @return $this
  245. */
  246. public function setResource($resource)
  247. {
  248. $this->resource = $resource;
  249. return $this;
  250. }
  251. /**
  252. * Get resource path.
  253. *
  254. * @return string
  255. */
  256. public function getResourcePath()
  257. {
  258. if (empty($this->resource)) {
  259. $path = request()->path();
  260. $segments = explode('/', $path);
  261. array_pop($segments);
  262. $this->resource = implode('/', $segments);
  263. }
  264. return $this->resource;
  265. }
  266. /**
  267. * Set the model instance.
  268. *
  269. * @param Model $model
  270. *
  271. * @return $this
  272. */
  273. public function setModel($model)
  274. {
  275. $this->model = $model;
  276. return $this;
  277. }
  278. /**
  279. * Get the model instance being queried.
  280. *
  281. * @return Model
  282. */
  283. public function getModel()
  284. {
  285. return $this->model;
  286. }
  287. /**
  288. * Add field and relation dynamically.
  289. *
  290. * @param string $method
  291. * @param array $arguments
  292. *
  293. * @return bool|mixed
  294. */
  295. public function __call($method, $arguments = [])
  296. {
  297. $label = isset($arguments[0]) ? $arguments[0] : ucfirst($method);
  298. if ($field = $this->handleGetMutatorField($method, $label)) {
  299. return $field;
  300. }
  301. if ($field = $this->handleRelationField($method, $arguments)) {
  302. return $field;
  303. }
  304. if ($field = $this->handleModelField($method, $label)) {
  305. return $field;
  306. }
  307. return $this->addField($method, $label);
  308. }
  309. /**
  310. * Handle the get mutator field.
  311. *
  312. * @param string $method
  313. * @param string $label
  314. *
  315. * @return bool|Field
  316. */
  317. protected function handleGetMutatorField($method, $label)
  318. {
  319. if (is_null($this->model)) {
  320. return false;
  321. }
  322. if ($this->model->hasGetMutator($method)) {
  323. return $this->addField($method, $label);
  324. }
  325. return false;
  326. }
  327. /**
  328. * Handle relation field.
  329. *
  330. * @param string $method
  331. * @param array $arguments
  332. *
  333. * @return $this|bool|Relation|Field
  334. */
  335. protected function handleRelationField($method, $arguments)
  336. {
  337. if (!method_exists($this->model, $method)) {
  338. return false;
  339. }
  340. if (!($relation = $this->model->$method()) instanceof EloquentRelation) {
  341. return false;
  342. }
  343. if ($relation instanceof HasOne
  344. || $relation instanceof BelongsTo
  345. || $relation instanceof MorphOne
  346. ) {
  347. $this->model->with($method);
  348. if (count($arguments) == 1 && $arguments[0] instanceof \Closure) {
  349. return $this->addRelation($method, $arguments[0]);
  350. }
  351. if (count($arguments) == 2 && $arguments[1] instanceof \Closure) {
  352. return $this->addRelation($method, $arguments[1], $arguments[0]);
  353. }
  354. return $this->addField($method, array_get($arguments, 0))->setRelation(snake_case($method));
  355. }
  356. if ($relation instanceof HasMany
  357. || $relation instanceof MorphMany
  358. || $relation instanceof BelongsToMany
  359. || $relation instanceof HasManyThrough
  360. ) {
  361. if (empty($arguments) || (count($arguments) == 1 && is_string($arguments[0]))) {
  362. return $this->showRelationAsField($method, $arguments[0] ?? '');
  363. }
  364. $this->model->with($method);
  365. if (count($arguments) == 1 && is_callable($arguments[0])) {
  366. return $this->addRelation($method, $arguments[0]);
  367. } elseif (count($arguments) == 2 && is_callable($arguments[1])) {
  368. return $this->addRelation($method, $arguments[1], $arguments[0]);
  369. }
  370. throw new \InvalidArgumentException('Invalid eloquent relation');
  371. }
  372. return false;
  373. }
  374. protected function showRelationAsField($relation = '', $label = '')
  375. {
  376. return $this->addField($relation, $label);
  377. }
  378. /**
  379. * Handle model field.
  380. *
  381. * @param string $method
  382. * @param string $label
  383. *
  384. * @return bool|Field
  385. */
  386. protected function handleModelField($method, $label)
  387. {
  388. if (in_array($method, $this->model->getAttributes())) {
  389. return $this->addField($method, $label);
  390. }
  391. return false;
  392. }
  393. /**
  394. * Render the show panels.
  395. *
  396. * @return string
  397. */
  398. public function render()
  399. {
  400. if (is_callable($this->builder)) {
  401. call_user_func($this->builder, $this);
  402. }
  403. if ($this->fields->isEmpty()) {
  404. $this->all();
  405. }
  406. if (is_array($this->builder)) {
  407. $this->fields($this->builder);
  408. }
  409. $this->fields->each->setValue($this->model);
  410. $this->relations->each->setModel($this->model);
  411. $data = [
  412. 'panel' => $this->panel->fill($this->fields),
  413. 'relations' => $this->relations,
  414. ];
  415. return view('admin::show', $data)->render();
  416. }
  417. }