PageRenderTime 76ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/modules/backend/widgets/Table.php

https://gitlab.com/gideonmarked/yovelife
PHP | 291 lines | 177 code | 56 blank | 58 comment | 20 complexity | 0f5ed665c5e09f31ac41557d54ffeb7e MD5 | raw file
  1. <?php namespace Backend\Widgets;
  2. use Lang;
  3. use Input;
  4. use Request;
  5. use Backend\Classes\WidgetBase;
  6. use SystemException;
  7. /**
  8. * Table Widget.
  9. *
  10. * Represents an editable tabular control.
  11. *
  12. * @package october\backend
  13. * @author Alexey Bobkov, Samuel Georges
  14. */
  15. class Table extends WidgetBase
  16. {
  17. /**
  18. * {@inheritDoc}
  19. */
  20. protected $defaultAlias = 'table';
  21. /**
  22. * @var array Table columns
  23. */
  24. protected $columns = [];
  25. /**
  26. * @var boolean Show data table header
  27. */
  28. protected $showHeader = true;
  29. /**
  30. * @var Backend\Widgets\Table\DatasourceBase
  31. */
  32. protected $dataSource = null;
  33. /**
  34. * @var string Field name used for request data.
  35. */
  36. protected $fieldName = null;
  37. /**
  38. * @var string
  39. */
  40. protected $recordsKeyFrom;
  41. protected $dataSourceAliases = [
  42. 'client' => '\Backend\Widgets\Table\ClientMemoryDataSource',
  43. 'server' => '\Backend\Widgets\Table\ServerEventDataSource'
  44. ];
  45. /**
  46. * Initialize the widget, called by the constructor and free from its parameters.
  47. */
  48. public function init()
  49. {
  50. $this->columns = $this->getConfig('columns', []);
  51. $this->fieldName = $this->getConfig('fieldName', $this->alias);
  52. $this->recordsKeyFrom = $this->getConfig('keyFrom', 'id');
  53. $dataSourceClass = $this->getConfig('dataSource');
  54. if (!strlen($dataSourceClass)) {
  55. throw new SystemException('The Table widget data source is not specified in the configuration.');
  56. }
  57. if (array_key_exists($dataSourceClass, $this->dataSourceAliases)) {
  58. $dataSourceClass = $this->dataSourceAliases[$dataSourceClass];
  59. }
  60. if (!class_exists($dataSourceClass)) {
  61. throw new SystemException(sprintf('The Table widget data source class "%s" is could not be found.', $dataSourceClass));
  62. }
  63. $this->dataSource = new $dataSourceClass($this->recordsKeyFrom);
  64. if (Request::method() == 'POST' && $this->isClientDataSource()) {
  65. if (strpos($this->fieldName, '[') === false) {
  66. $requestDataField = $this->fieldName.'TableData';
  67. }
  68. else {
  69. $requestDataField = $this->fieldName.'[TableData]';
  70. }
  71. if (Request::exists($requestDataField)) {
  72. // Load data into the client memory data source on POST
  73. $this->dataSource->purge();
  74. $this->dataSource->initRecords(Request::input($requestDataField));
  75. }
  76. }
  77. }
  78. /**
  79. * Returns the data source object.
  80. * @return \Backend\Widgets\Table\DataSourceBase
  81. */
  82. public function getDataSource()
  83. {
  84. return $this->dataSource;
  85. }
  86. /**
  87. * Renders the widget.
  88. */
  89. public function render()
  90. {
  91. $this->prepareVars();
  92. return $this->makePartial('table');
  93. }
  94. /**
  95. * Prepares the view data
  96. */
  97. public function prepareVars()
  98. {
  99. $this->vars['columns'] = $this->prepareColumnsArray();
  100. $this->vars['recordsKeyFrom'] = $this->recordsKeyFrom;
  101. $this->vars['recordsPerPage'] = $this->getConfig('recordsPerPage', false) ?: 'false';
  102. $this->vars['postbackHandlerName'] = $this->getConfig('postbackHandlerName', 'onSave');
  103. $this->vars['adding'] = $this->getConfig('adding', true);
  104. $this->vars['deleting'] = $this->getConfig('deleting', true);
  105. $this->vars['toolbar'] = $this->getConfig('toolbar', true);
  106. $this->vars['height'] = $this->getConfig('height', false) ?: 'false';
  107. $this->vars['dynamicHeight'] = $this->getConfig('dynamicHeight', false) ?: 'false';
  108. $this->vars['btnAddRowLabel'] = Lang::get($this->getConfig('btnAddRowLabel', 'backend::lang.form.insert_row'));
  109. $this->vars['btnAddRowBelowLabel'] = Lang::get($this->getConfig('btnAddRowBelowLabel', 'backend::lang.form.insert_row_below'));
  110. $this->vars['btnDeleteRowLabel'] = Lang::get($this->getConfig('btnDeleteRowLabel', 'backend::lang.form.delete_row'));
  111. $isClientDataSource = $this->isClientDataSource();
  112. $this->vars['clientDataSourceClass'] = $isClientDataSource ? 'client' : 'server';
  113. $this->vars['data'] = json_encode($isClientDataSource
  114. ? $this->dataSource->getAllRecords()
  115. : []
  116. );
  117. }
  118. //
  119. // Internals
  120. //
  121. /**
  122. * {@inheritDoc}
  123. */
  124. protected function loadAssets()
  125. {
  126. $this->addCss('css/table.css', 'core');
  127. $this->addJs('js/build-min.js', 'core');
  128. }
  129. /**
  130. * Converts the columns associative array to a regular array and translates column headers and drop-down options.
  131. * Working with regular arrays is much faster in JavaScript.
  132. * References:
  133. * - http://www.smashingmagazine.com/2012/11/05/writing-fast-memory-efficient-javascript/
  134. * - http://jsperf.com/performance-of-array-vs-object/3
  135. */
  136. protected function prepareColumnsArray()
  137. {
  138. $result = [];
  139. foreach ($this->columns as $key=>$data) {
  140. $data['key'] = $key;
  141. if (isset($data['title']))
  142. $data['title'] = trans($data['title']);
  143. if (isset($data['options'])) {
  144. foreach ($data['options'] as &$option)
  145. $option = trans($option);
  146. }
  147. if (isset($data['validation'])) {
  148. foreach ($data['validation'] as &$validation) {
  149. if (isset($validation['message'])) {
  150. $validation['message'] = trans($validation['message']);
  151. }
  152. }
  153. }
  154. $result[] = $data;
  155. }
  156. return $result;
  157. }
  158. protected function isClientDataSource()
  159. {
  160. return $this->dataSource instanceof \Backend\Widgets\Table\ClientMemoryDataSource;
  161. }
  162. //
  163. // Event handlers
  164. //
  165. public function onServerGetRecords()
  166. {
  167. // Disable asset broadcasting
  168. $this->controller->flushAssets();
  169. if ($this->isClientDataSource()) {
  170. throw new SystemException('The Table widget is not configured to use the server data source.');
  171. }
  172. $count = post('count');
  173. // Oddly, JS may pass false as a string (@todo)
  174. if ($count === 'false') {
  175. $count = false;
  176. }
  177. return [
  178. 'records' => $this->dataSource->getRecords(post('offset'), $count),
  179. 'count' => $this->dataSource->getCount()
  180. ];
  181. }
  182. public function onServerCreateRecord()
  183. {
  184. if ($this->isClientDataSource()) {
  185. throw new SystemException('The Table widget is not configured to use the server data source.');
  186. }
  187. $this->dataSource->createRecord(
  188. post('recordData'),
  189. post('placement'),
  190. post('relativeToKey')
  191. );
  192. return $this->onServerGetRecords();
  193. }
  194. public function onServerUpdateRecord()
  195. {
  196. if ($this->isClientDataSource()) {
  197. throw new SystemException('The Table widget is not configured to use the server data source.');
  198. }
  199. $this->dataSource->updateRecord(post('key'), post('recordData'));
  200. }
  201. public function onServerDeleteRecord()
  202. {
  203. if ($this->isClientDataSource()) {
  204. throw new SystemException('The Table widget is not configured to use the server data source.');
  205. }
  206. $this->dataSource->deleteRecord(post('key'));
  207. return $this->onServerGetRecords();
  208. }
  209. public function onGetDropdownOptions()
  210. {
  211. $columnName = Input::get('column');
  212. $rowData = Input::get('rowData');
  213. $eventResults = $this->fireEvent('table.getDropdownOptions', [$columnName, $rowData]);
  214. $options = [];
  215. if (count($eventResults)) {
  216. $options = $eventResults[0];
  217. }
  218. return [
  219. 'options' => $options
  220. ];
  221. }
  222. public function onGetAutocompleteOptions()
  223. {
  224. $columnName = Input::get('column');
  225. $rowData = Input::get('rowData');
  226. $eventResults = $this->fireEvent('table.getAutocompleteOptions', [$columnName, $rowData]);
  227. $options = [];
  228. if (count($eventResults)) {
  229. $options = $eventResults[0];
  230. }
  231. return [
  232. 'options' => $options
  233. ];
  234. }
  235. }