PageRenderTime 53ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/modules/backend/behaviors/ListController.php

https://gitlab.com/gideonmarked/atls-express
PHP | 486 lines | 235 code | 76 blank | 175 comment | 25 complexity | 5db15b4110b861cb0e7570dc6ac9f318 MD5 | raw file
  1. <?php namespace Backend\Behaviors;
  2. use Str;
  3. use Lang;
  4. use Event;
  5. use Flash;
  6. use ApplicationException;
  7. use Backend\Classes\ControllerBehavior;
  8. /**
  9. * List Controller Behavior
  10. * Adds features for working with backend lists.
  11. *
  12. * @package october\backend
  13. * @author Alexey Bobkov, Samuel Georges
  14. */
  15. class ListController extends ControllerBehavior
  16. {
  17. /**
  18. * @var array List definitions, keys for alias and value for configuration.
  19. */
  20. protected $listDefinitions;
  21. /**
  22. * @var string The primary list alias to use. Default: list
  23. */
  24. protected $primaryDefinition;
  25. /**
  26. * @var \Backend\Classes\WidgetBase Reference to the list widget object.
  27. */
  28. protected $listWidgets = [];
  29. /**
  30. * @var \Backend\Classes\WidgetBase Reference to the toolbar widget objects.
  31. */
  32. protected $toolbarWidgets = [];
  33. /**
  34. * @var \Backend\Classes\WidgetBase Reference to the filter widget objects.
  35. */
  36. protected $filterWidgets = [];
  37. /**
  38. * {@inheritDoc}
  39. */
  40. protected $requiredProperties = ['listConfig'];
  41. /**
  42. * @var array Configuration values that must exist when applying the primary config file.
  43. * - modelClass: Class name for the model
  44. * - list: List column definitions
  45. */
  46. protected $requiredConfig = ['modelClass', 'list'];
  47. /**
  48. * Behavior constructor
  49. * @param \Backend\Classes\Controller $controller
  50. */
  51. public function __construct($controller)
  52. {
  53. parent::__construct($controller);
  54. /*
  55. * Extract list definitions
  56. */
  57. if (is_array($controller->listConfig)) {
  58. $this->listDefinitions = $controller->listConfig;
  59. $this->primaryDefinition = key($this->listDefinitions);
  60. }
  61. else {
  62. $this->listDefinitions = ['list' => $controller->listConfig];
  63. $this->primaryDefinition = 'list';
  64. }
  65. /*
  66. * Build configuration
  67. */
  68. $this->setConfig($this->listDefinitions[$this->primaryDefinition], $this->requiredConfig);
  69. }
  70. /**
  71. * Creates all the list widgets based on the definitions.
  72. * @return array
  73. */
  74. public function makeLists()
  75. {
  76. foreach ($this->listDefinitions as $definition => $config) {
  77. $this->listWidgets[$definition] = $this->makeList($definition);
  78. }
  79. return $this->listWidgets;
  80. }
  81. /**
  82. * Prepare the widgets used by this action
  83. * @return void
  84. */
  85. public function makeList($definition = null)
  86. {
  87. if (!$definition || !isset($this->listDefinitions[$definition])) {
  88. $definition = $this->primaryDefinition;
  89. }
  90. $listConfig = $this->makeConfig($this->listDefinitions[$definition], $this->requiredConfig);
  91. /*
  92. * Create the model
  93. */
  94. $class = $listConfig->modelClass;
  95. $model = new $class;
  96. $model = $this->controller->listExtendModel($model, $definition);
  97. /*
  98. * Prepare the list widget
  99. */
  100. $columnConfig = $this->makeConfig($listConfig->list);
  101. $columnConfig->model = $model;
  102. $columnConfig->alias = $definition;
  103. /*
  104. * Prepare the columns configuration
  105. */
  106. $configFieldsToTransfer = [
  107. 'recordUrl',
  108. 'recordOnClick',
  109. 'recordsPerPage',
  110. 'noRecordsMessage',
  111. 'defaultSort',
  112. 'showSorting',
  113. 'showSetup',
  114. 'showCheckboxes',
  115. 'showTree',
  116. 'treeExpanded',
  117. ];
  118. foreach ($configFieldsToTransfer as $field) {
  119. if (isset($listConfig->{$field})) {
  120. $columnConfig->{$field} = $listConfig->{$field};
  121. }
  122. }
  123. /*
  124. * List Widget with extensibility
  125. */
  126. $widget = $this->makeWidget('Backend\Widgets\Lists', $columnConfig);
  127. $widget->bindEvent('list.extendColumns', function () use ($widget) {
  128. $this->controller->listExtendColumns($widget);
  129. });
  130. $widget->bindEvent('list.extendQueryBefore', function ($query) use ($definition) {
  131. $this->controller->listExtendQueryBefore($query, $definition);
  132. });
  133. $widget->bindEvent('list.extendQuery', function ($query) use ($definition) {
  134. $this->controller->listExtendQuery($query, $definition);
  135. });
  136. $widget->bindEvent('list.injectRowClass', function ($record) use ($definition) {
  137. return $this->controller->listInjectRowClass($record, $definition);
  138. });
  139. $widget->bindEvent('list.overrideColumnValue', function ($record, $column, $value) use ($definition) {
  140. return $this->controller->listOverrideColumnValue($record, $column->columnName, $definition);
  141. });
  142. $widget->bindEvent('list.overrideHeaderValue', function ($column, $value) use ($definition) {
  143. return $this->controller->listOverrideHeaderValue($column->columnName, $definition);
  144. });
  145. $widget->bindToController();
  146. /*
  147. * Prepare the toolbar widget (optional)
  148. */
  149. if (isset($listConfig->toolbar)) {
  150. $toolbarConfig = $this->makeConfig($listConfig->toolbar);
  151. $toolbarConfig->alias = $widget->alias . 'Toolbar';
  152. $toolbarWidget = $this->makeWidget('Backend\Widgets\Toolbar', $toolbarConfig);
  153. $toolbarWidget->bindToController();
  154. $toolbarWidget->cssClasses[] = 'list-header';
  155. /*
  156. * Link the Search Widget to the List Widget
  157. */
  158. if ($searchWidget = $toolbarWidget->getSearchWidget()) {
  159. $searchWidget->bindEvent('search.submit', function () use ($widget, $searchWidget) {
  160. $widget->setSearchTerm($searchWidget->getActiveTerm());
  161. return $widget->onRefresh();
  162. });
  163. $widget->setSearchOptions([
  164. 'mode' => $searchWidget->mode,
  165. 'scope' => $searchWidget->scope,
  166. ]);
  167. // Find predefined search term
  168. $widget->setSearchTerm($searchWidget->getActiveTerm());
  169. }
  170. $this->toolbarWidgets[$definition] = $toolbarWidget;
  171. }
  172. /*
  173. * Prepare the filter widget (optional)
  174. */
  175. if (isset($listConfig->filter)) {
  176. $widget->cssClasses[] = 'list-flush';
  177. $filterConfig = $this->makeConfig($listConfig->filter);
  178. $filterConfig->alias = $widget->alias . 'Filter';
  179. $filterWidget = $this->makeWidget('Backend\Widgets\Filter', $filterConfig);
  180. $filterWidget->bindToController();
  181. /*
  182. * Filter the list when the scopes are changed
  183. */
  184. $filterWidget->bindEvent('filter.update', function () use ($widget, $filterWidget) {
  185. return $widget->onRefresh();
  186. });
  187. /*
  188. * Extend the query of the list of options
  189. */
  190. $filterWidget->bindEvent('filter.extendQuery', function($query, $scope) {
  191. $this->controller->listFilterExtendQuery($query, $scope);
  192. });
  193. // Apply predefined filter values
  194. $widget->addFilter([$filterWidget, 'applyAllScopesToQuery']);
  195. $this->filterWidgets[$definition] = $filterWidget;
  196. }
  197. return $widget;
  198. }
  199. /**
  200. * Index Controller action.
  201. * @return void
  202. */
  203. public function index()
  204. {
  205. $this->controller->pageTitle = $this->controller->pageTitle ?: Lang::get($this->getConfig(
  206. 'title',
  207. 'backend::lang.list.default_title'
  208. ));
  209. $this->controller->bodyClass = 'slim-container';
  210. $this->makeLists();
  211. }
  212. /**
  213. * Bulk delete records.
  214. * @return void
  215. */
  216. public function index_onDelete()
  217. {
  218. if (method_exists($this->controller, 'onDelete')) {
  219. return call_user_func_array([$this->controller, 'onDelete'], func_get_args());
  220. }
  221. /*
  222. * Validate checked identifiers
  223. */
  224. $checkedIds = post('checked');
  225. if (!$checkedIds || !is_array($checkedIds) || !count($checkedIds)) {
  226. Flash::error(Lang::get('backend::lang.list.delete_selected_empty'));
  227. return $this->controller->listRefresh();
  228. }
  229. /*
  230. * Establish the list definition
  231. */
  232. $definition = post('definition', $this->primaryDefinition);
  233. if (!isset($this->listDefinitions[$definition])) {
  234. throw new ApplicationException(Lang::get('backend::lang.list.missing_parent_definition', compact('definition')));
  235. }
  236. $listConfig = $this->makeConfig($this->listDefinitions[$definition], $this->requiredConfig);
  237. /*
  238. * Create the model
  239. */
  240. $class = $listConfig->modelClass;
  241. $model = new $class;
  242. $model = $this->controller->listExtendModel($model, $definition);
  243. /*
  244. * Create the query
  245. */
  246. $query = $model->newQuery();
  247. $this->controller->listExtendQueryBefore($query, $definition);
  248. $query->whereIn($model->getKeyName(), $checkedIds);
  249. $this->controller->listExtendQuery($query, $definition);
  250. /*
  251. * Delete records
  252. */
  253. $records = $query->get();
  254. if ($records->count()) {
  255. foreach ($records as $record) {
  256. $record->delete();
  257. }
  258. Flash::success(Lang::get('backend::lang.list.delete_selected_success'));
  259. }
  260. else {
  261. Flash::error(Lang::get('backend::lang.list.delete_selected_empty'));
  262. }
  263. return $this->controller->listRefresh($definition);
  264. }
  265. /**
  266. * Renders the widget collection.
  267. * @param string $definition Optional list definition.
  268. * @return string Rendered HTML for the list.
  269. */
  270. public function listRender($definition = null)
  271. {
  272. if (!count($this->listWidgets)) {
  273. throw new ApplicationException(Lang::get('backend::lang.list.behavior_not_ready'));
  274. }
  275. if (!$definition || !isset($this->listDefinitions[$definition])) {
  276. $definition = $this->primaryDefinition;
  277. }
  278. $collection = [];
  279. if (isset($this->toolbarWidgets[$definition])) {
  280. $collection[] = $this->toolbarWidgets[$definition]->render();
  281. }
  282. if (isset($this->filterWidgets[$definition])) {
  283. $collection[] = $this->filterWidgets[$definition]->render();
  284. }
  285. $collection[] = $this->listWidgets[$definition]->render();
  286. return implode(PHP_EOL, $collection);
  287. }
  288. /**
  289. * Refreshes the list container only, useful for returning in custom AJAX requests.
  290. * @param string $definition Optional list definition.
  291. * @return array The list element selector as the key, and the list contents are the value.
  292. */
  293. public function listRefresh($definition = null)
  294. {
  295. if (!count($this->listWidgets)) {
  296. $this->makeLists();
  297. }
  298. if (!$definition || !isset($this->listDefinitions[$definition])) {
  299. $definition = $this->primaryDefinition;
  300. }
  301. return $this->listWidgets[$definition]->onRefresh();
  302. }
  303. /**
  304. * Returns the widget used by this behavior.
  305. * @return \Backend\Classes\WidgetBase
  306. */
  307. public function listGetWidget($definition = null)
  308. {
  309. if (!$definition) {
  310. $definition = $this->primaryDefinition;
  311. }
  312. return array_get($this->listWidgets, $definition);
  313. }
  314. //
  315. // Overrides
  316. //
  317. /**
  318. * Called before the list columns are defined.
  319. * @param Backend\Widgets\List $host The hosting list widget
  320. * @return void
  321. */
  322. // public function listExtendColumnsBefore($host)
  323. // {
  324. // }
  325. /**
  326. * Called after the list columns are defined.
  327. * @param \Backend\Widgets\List $host The hosting list widget
  328. * @return void
  329. */
  330. public function listExtendColumns($host)
  331. {
  332. }
  333. /**
  334. * Controller override: Extend supplied model
  335. * @param Model $model
  336. * @return Model
  337. */
  338. public function listExtendModel($model, $definition = null)
  339. {
  340. return $model;
  341. }
  342. /**
  343. * Controller override: Extend the query used for populating the list
  344. * before the default query is processed.
  345. * @param \October\Rain\Database\Builder $query
  346. */
  347. public function listExtendQueryBefore($query, $definition = null)
  348. {
  349. }
  350. /**
  351. * Controller override: Extend the query used for populating the list
  352. * after the default query is processed.
  353. * @param \October\Rain\Database\Builder $query
  354. */
  355. public function listExtendQuery($query, $definition = null)
  356. {
  357. }
  358. /**
  359. * Controller override: Extend the query used for populating the filter
  360. * options before the default query is processed.
  361. * @param \October\Rain\Database\Builder $query
  362. * @param array $scope
  363. */
  364. public function listFilterExtendQuery($query, $scope)
  365. {
  366. }
  367. /**
  368. * Returns a CSS class name for a list row (<tr class="...">).
  369. * @param Model $record The populated model used for the column
  370. * @param string $definition List definition (optional)
  371. * @return string HTML view
  372. */
  373. public function listInjectRowClass($record, $definition = null)
  374. {
  375. }
  376. /**
  377. * Replace a table column value (<td>...</td>)
  378. * @param Model $record The populated model used for the column
  379. * @param string $columnName The column name to override
  380. * @param string $definition List definition (optional)
  381. * @return string HTML view
  382. */
  383. public function listOverrideColumnValue($record, $columnName, $definition = null)
  384. {
  385. }
  386. /**
  387. * Replace the entire table header contents (<th>...</th>) with custom HTML
  388. * @param string $columnName The column name to override
  389. * @param string $definition List definition (optional)
  390. * @return string HTML view
  391. */
  392. public function listOverrideHeaderValue($columnName, $definition = null)
  393. {
  394. }
  395. /**
  396. * Static helper for extending list columns.
  397. * @param callable $callback
  398. * @return void
  399. */
  400. public static function extendListColumns($callback)
  401. {
  402. $calledClass = self::getCalledExtensionClass();
  403. Event::listen('backend.list.extendColumns', function ($widget) use ($calledClass, $callback) {
  404. if (!is_a($widget->getController(), $calledClass)) {
  405. return;
  406. }
  407. call_user_func_array($callback, [$widget, $widget->model]);
  408. });
  409. }
  410. }