phpMv  -UI toolkit 2.4.12
jQuery, jQuery UI, Twitter Bootstrap and Semantic-UI library for php & php MVC Frameworks
DataTable.php
Go to the documentation of this file.
1 <?php
3 
4 use Ajax\JsUtils;
18 
27 class DataTable extends Widget {
29 
30  protected $_searchField;
31 
32  protected $_urls;
33 
34  protected $_pagination;
35 
36  protected $_compileParts;
37 
38  protected $_deleteBehavior;
39 
40  protected $_editBehavior;
41 
42  protected $_displayBehavior;
43 
44  protected $_visibleHover = false;
45 
46  protected $_targetSelector;
47 
48  protected $_refreshSelector;
49 
50  protected $_emptyMessage;
51 
52  protected $_json;
53 
54  protected $_rowClass = "_element";
55 
56  protected $_sortable;
57 
58  protected $_hiddenColumns;
59 
60  protected $_colWidths;
61 
63 
64  protected $_caption;
65 
66  protected $_namePrefix;
67 
68  public function __construct($identifier, $model, $modelInstance = NULL) {
69  parent::__construct($identifier, $model, $modelInstance);
70  $this->_init(new InstanceViewer($identifier), "table", new HtmlTable($identifier, 0, 0), false);
71  $this->_urls = [];
72  $this->_emptyMessage = new HtmlMessage("", "nothing to display");
73  $this->_emptyMessage->setIcon("info circle");
74  }
75 
76  public function run(JsUtils $js) {
77  if ($this->_runned !== true) {
78  $offset = $js->scriptCount();
79  if ($this->_hasCheckboxes && isset($js)) {
80  $this->_runCheckboxes($js);
81  }
82  if ($this->_visibleHover) {
83  $js->execOn("mouseover", "#" . $this->identifier . " tr", "$(event.currentTarget).closest('tr').find('.visibleover').css('visibility', 'visible');", [
84  "preventDefault" => false,
85  "stopPropagation" => true
86  ]);
87  $js->execOn("mouseout", "#" . $this->identifier . " tr", "$(event.currentTarget).closest('tr').find('.visibleover').css('visibility', 'hidden');$(event.currentTarget).trigger('visibleoverOut');", [
88  "preventDefault" => false,
89  "stopPropagation" => true
90  ]);
91  }
92  if (\is_array($this->_deleteBehavior))
93  $this->_generateBehavior("delete", $this->_deleteBehavior, $js);
94  if (\is_array($this->_editBehavior))
95  $this->_generateBehavior("edit", $this->_editBehavior, $js);
96  if (\is_array($this->_displayBehavior)) {
97  $this->_generateBehavior("display", $this->_displayBehavior, $js);
98  }
99  parent::run($js);
100  if (isset($this->_pagination)) {
101  $this->_associatePaginationBehavior($js, $offset);
102  }
103  $this->_associateSearchFieldBehavior($js, $offset);
104  $this->_runned = true;
105  }
106  }
107 
108  protected function _generateBehavior($op, $params, JsUtils $js) {
109  if (isset($this->_urls[$op])) {
110  $params = \array_merge($params, [
111  "attr" => "data-ajax"
112  ]);
113  $js->ajaxOnClick("#" . $this->identifier . " ._" . $op, $this->_urls[$op], $this->getTargetSelector($op), $params);
114  }
115  }
116 
122  protected function getTable() {
123  return $this->content["table"];
124  }
125 
126  public function refreshTR() {
127  $this->getTable()->refreshTR();
128  return $this;
129  }
130 
131  public function refreshTD($fieldName, $jquery, $view) {
132  $index = $this->_getIndex($fieldName);
133  $this->compile($jquery, $view);
134  return $this->refreshTR()
135  ->getTable()
136  ->getCell(0, $index);
137  }
138 
139  public function compile(JsUtils $js = NULL, &$view = NULL) {
140  if (! $this->_generated) {
141  if (isset($this->_buttonsColumn)) {
142  $this->_instanceViewer->sortColumnContent($this->_buttonsColumn, $this->_buttons);
143  }
144  $this->_instanceViewer->setInstance($this->_model);
145  $captions = $this->_instanceViewer->getCaptions();
146  $table = $this->content["table"];
147  if ($this->_hasCheckboxes) {
148  $this->_generateMainCheckbox($captions);
149  }
150  $table->setRowCount(0, \count($captions));
151  $this->_generateHeader($table, $captions);
152 
153  if (isset($this->_compileParts))
154  $table->setCompileParts($this->_compileParts);
155 
156  $this->_generateContent($table);
157 
158  $this->compileExtraElements($table, $captions);
159  $this->_compileSearchFieldBehavior($js);
160 
161  $this->content = JArray::sortAssociative($this->content, [
163  "table",
165  ]);
166  if ($this->_caption != null) {
167  $this->wrap("<div class='field'><label>{$this->_caption}</label>", "</div>");
168  }
169  $this->_compileForm();
170  $this->_applyStyleAttributes($table);
171  $this->_generated = true;
172  }
173  return parent::compile($js, $view);
174  }
175 
176  protected function compileExtraElements($table, $captions) {
177  if ($this->_hasCheckboxes && $table->hasPart("thead")) {
178  $table->getHeader()
179  ->getCell(0, 0)
180  ->addClass("no-sort");
181  }
182 
183  if (isset($this->_toolbar)) {
184  $this->_setToolbarPosition($table, $captions);
185  }
186  if (isset($this->_pagination) && $this->_pagination->getVisible()) {
187  $this->_generatePagination($table);
188  }
189  }
190 
191  protected function _applyStyleAttributes($table) {
192  if (isset($this->_hiddenColumns))
193  $this->_hideColumns();
194  if (isset($this->_colWidths)) {
195  foreach ($this->_colWidths as $colIndex => $width) {
196  $table->setColWidth($colIndex, $width);
197  }
198  }
199  }
200 
201  protected function _hideColumns() {
202  foreach ($this->_hiddenColumns as $colIndex) {
203  $this->_self->hideColumn($colIndex);
204  }
205  return $this;
206  }
207 
208  protected function _generateHeader(HtmlTable $table, $captions) {
209  $gbFields = $this->_instanceViewer->getGroupByFields();
210  if (\is_array($gbFields)) {
211  $captions = \array_values(JArray::removeByKeys($captions, $gbFields));
212  }
213  $table->setHeaderValues($captions);
214  if (isset($this->_sortable)) {
215  $table->setSortable($this->_sortable);
216  }
217  }
218 
219  protected function _generateContent($table) {
220  $objects = $this->_modelInstance;
221  if (isset($this->_pagination)) {
222  $objects = $this->_pagination->getObjects($this->_modelInstance);
223  }
225  $fields = $this->_instanceViewer->getSimpleProperties();
226  $groupByFields = $this->_instanceViewer->getGroupByFields();
227  if (! \is_array($groupByFields)) {
228  $table->fromDatabaseObjects($objects, function ($instance) use ($table, $fields) {
229  return $this->_generateRow($instance, $fields, $table);
230  });
231  } else {
232  $diffFields = \array_values(JArray::removeByKeys($fields, $groupByFields));
233  $activeValues = \array_combine($groupByFields, \array_fill(0, \count($groupByFields), null));
234  $uuids = [];
235  $table->fromDatabaseObjects($objects, function ($instance) use ($table, $fields, &$activeValues, $groupByFields, &$uuids, $diffFields) {
236  $this->_instanceViewer->setInstance($instance);
237  foreach ($groupByFields as $index => $gbField) {
238  $this->_generateGroupByRow($index, $gbField, $table, $fields, $activeValues, $uuids);
239  }
240  return $this->_generateRow($instance, $diffFields, $table, null, $uuids);
241  });
242  }
243  if ($table->getRowCount() == 0) {
244  $result = $table->addRow();
245  $result->mergeCol();
246  $result->setValues([
247  $this->_emptyMessage
248  ]);
249  }
250  }
251 
252  protected function _generateGroupByRow($index, $gbField, $table, $fields, &$activeValues, &$uuids) {
253  $newValue = $this->_instanceViewer->getValue($gbField);
254  if ($this->getElementContent($activeValues[$gbField]) !== $this->getElementContent($newValue)) {
255  if ($index == 0) {
256  $uuids = [];
257  }
258  $uuid = \uniqid("grp");
259  $uuids[$gbField] = $uuid;
260  $id = $this->_instanceViewer->getIdentifier();
261  $result = $table->addMergeRow(\count($fields) + 1, $newValue);
262  $result->setIdentifier($this->identifier . "-tr-gb-" . $id);
263  $result->setProperty("data-ajax", $id);
264  $result->setProperty("data-group", $uuid);
265  $result->addToProperty("class", $this->_rowClass);
266  $activeValues[$gbField] = $newValue;
267  }
268  }
269 
270  private function getElementContent($elm) {
271  if ($elm instanceof HtmlDoubleElement) {
272  return $elm->getTextContent();
273  }
274  return $elm;
275  }
276 
277  public function getFieldValue($index) {
278  $index = $this->_getIndex($index);
279  if (\is_numeric($index)) {
280  $values = $this->_instanceViewer->getValues();
281  if (isset($values[$index])) {
282  return $values[$index];
283  }
284  }
285  return null;
286  }
287 
288  protected function _generateRow($instance, $fields, &$table, $checkedClass = null, $uuids = null) {
289  $this->_instanceViewer->setInstance($instance);
291  $values = $this->_instanceViewer->getValues();
292  $id = $this->_instanceViewer->getIdentifier();
293  $dataAjax = $id;
294  $id = $this->cleanIdentifier($id);
295  if ($this->_hasCheckboxes) {
296  $ck = new HtmlCheckbox("ck-" . $this->identifier . "-" . $id, "");
297  $checked = false;
298  if (isset($this->_checkedCallback)) {
299  $func = $this->_checkedCallback;
300  $checked = $func($instance);
301  }
302  $ck->setChecked($checked);
303  $field = $ck->getField();
304  $field->setProperty("value", $dataAjax);
305  $field->setProperty("name", "selection[]");
306  if (isset($checkedClass))
307  $field->setClass($checkedClass);
308  \array_unshift($values, $ck);
309  }
310  $result = $table->newRow();
311  $result->setIdentifier($this->identifier . "-tr-" . $id);
312  $result->setProperty("data-ajax", $dataAjax);
313  $result->setValues($values);
314  $result->addToProperty("class", $this->_rowClass);
315  $result->setPropertyValues("data-field", $fields);
316  if (isset($uuids)) {
317  $result->setProperty("data-child", implode(" ", $uuids));
318  }
319  return $result;
320  }
321 
322  protected function _generatePagination($table) {
323  if (isset($this->_toolbar)) {
324  if ($this->_toolbarPosition == PositionInTable::FOOTER)
325  $this->_toolbar->setFloated("left");
326  }
327  $footer = $table->getFooter();
328  $footer->mergeCol();
329  $this->_paginationToolbar = $this->_pagination->generateMenu($this->identifier);
330  $footer->addValues($this->_paginationToolbar);
331  }
332 
333  protected function _associatePaginationBehavior(JsUtils $js = NULL, $offset = null) {
334  if (isset($this->_urls["refresh"])) {
335  $menu = $this->_pagination->getMenu();
336  if (isset($menu) && isset($js)) {
337  $js->postOnClick("#" . $menu->getIdentifier() . " .item", $this->_urls["refresh"], "{'p':$(this).attr('data-page'),'_model':'" . JString::doubleBackSlashes($this->_model) . "'}", $this->getRefreshSelector(), [
338  "preventDefault" => false,
339  "jqueryDone" => "replaceWith",
340  "hasLoader" => false,
341  "jsCallback" => '$("#' . $this->identifier . '").trigger("pageChange");$("#' . $this->identifier . '").trigger("activeRowChange");'
342  ]);
343  $page = $_POST["p"] ?? null;
344  if (isset($page)) {
345  $activeClass = $this->getActiveRowClass();
346  $js->execAtLast('$("#' . $this->getIdentifier() . ' .pagination").children("a.item").removeClass("' . $activeClass . '");$("#' . $this->getIdentifier() . ' .pagination").children("a.item[data-page=' . $page . ']:not(.no-active)").addClass("' . $activeClass . '");');
347  }
348  }
349  }
350  }
351 
352  protected function _compileSearchFieldBehavior(JsUtils $js = NULL) {
353  if (isset($this->_searchField) && isset($js) && isset($this->_urls["refresh"])) {
354  $this->_searchField->postOn("change", $this->_urls["refresh"], "{'s':$(self).val(),'_model':'" . JString::doubleBackSlashes($this->_model) . "'}", "#" . $this->identifier . " tbody", [
355  "preventDefault" => false,
356  "jqueryDone" => "replaceWith",
357  "hasLoader" => "internal",
358  "jsCallback" => '$("#' . $this->identifier . '").trigger("searchTerminate",[$(self).val()]);'
359  ]);
360  }
361  }
362 
363  protected function _associateSearchFieldBehavior(JsUtils $js = NULL, $offset = null) {}
364 
365  protected function _getFieldName($index) {
366  $fieldName = parent::_getFieldName($index);
367  if (\is_object($fieldName))
368  $fieldName = "field-" . $index;
369  if ($this->_namePrefix != null) {
370  $fieldName = $this->_namePrefix . '.' . $fieldName;
371  }
372  return $fieldName . "[]";
373  }
374 
375  protected function _getFieldCaption($index) {
376  return null;
377  }
378 
379  protected function applyToolbarPosition(string $position, $table, $captions = NULL) {
380  switch ($position) {
383  if (isset($this->_compileParts) === false) {
384  $this->content[$position] = $this->_toolbar;
385  }
386  break;
390  $this->addToolbarRow($position, $table, $captions);
391  break;
392  }
393  }
394 
395  protected function _setToolbarPosition($table, $captions = NULL) {
396  if (\is_array($this->_toolbarPosition)) {
397  foreach ($this->_toolbarPosition as $tbp) {
398  $this->applyToolbarPosition($tbp, $table, $captions);
399  }
400  } else {
401  $this->applyToolbarPosition($this->_toolbarPosition, $table, $captions);
402  }
403  }
404 
415  public function afterCompile($index, $callback) {
416  $this->_instanceViewer->afterCompile($index, $callback);
417  return $this;
418  }
419 
420  private function addToolbarRow($part, $table, $captions) {
421  $hasPart = $table->hasPart($part);
422  if ($hasPart) {
423  $row = $table->getPart($part)->addRow(\count($captions));
424  } else {
425  $row = $table->getPart($part)->getRow(0);
426  }
427  $row->mergeCol();
428  $row->setValues([
429  $this->_toolbar
430  ]);
431  }
432 
439  public function getHtmlComponent() {
440  return $this->content["table"];
441  }
442 
443  public function getUrls() {
444  return $this->_urls;
445  }
446 
455  public function setUrls($urls) {
456  if (\is_array($urls)) {
457  $this->_urls["refresh"] = JArray::getValue($urls, "refresh", 0);
458  $this->_urls["edit"] = JArray::getValue($urls, "edit", 1);
459  $this->_urls["delete"] = JArray::getValue($urls, "delete", 2);
460  $this->_urls["display"] = JArray::getValue($urls, "display", 3);
461  } else {
462  $this->_urls = [
463  "refresh" => $urls,
464  "edit" => $urls,
465  "delete" => $urls,
466  "display" => $urls
467  ];
468  }
469  return $this;
470  }
471 
485  public function paginate($page, $total_rowcount, $items_per_page = 10, $pages_visibles = null) {
486  $this->_pagination = new Pagination($items_per_page, $pages_visibles, $page, $total_rowcount);
487  return $this;
488  }
489 
501  public function autoPaginate($page = 1, $items_per_page = 10, $pages_visibles = 4) {
502  $this->_pagination = new Pagination($items_per_page, $pages_visibles, $page);
503  return $this;
504  }
505 
511  public function refresh($compileParts = [
512  'tbody'
513  ]) {
514  $this->_compileParts = $compileParts;
515  return $this;
516  }
517 
524  public function addSearchInToolbar($position = Direction::RIGHT) {
525  return $this->addInToolbar($this->getSearchField())
526  ->setPosition($position);
527  }
528 
529  public function getSearchField() {
530  if (isset($this->_searchField) === false) {
531  $this->_searchField = new HtmlInput("search-" . $this->identifier, "search", "", "Search...");
532  $this->_searchField->addIcon("search", Direction::RIGHT);
533  }
534  return $this->_searchField;
535  }
536 
544  public function onNewRow($callback) {
545  $this->content["table"]->onNewRow($callback);
546  return $this;
547  }
548 
554  public function asForm() {
555  return $this->getForm();
556  }
557 
558  protected function getTargetSelector($op) {
559  $result = $this->_targetSelector;
560  if (! isset($result[$op]))
561  $result = "#" . $this->identifier;
562  return $result[$op];
563  }
564 
573  if (! \is_array($_targetSelector)) {
574  $_targetSelector = [
575  "edit" => $_targetSelector,
576  "delete" => $_targetSelector
577  ];
578  }
579  $this->_targetSelector = $_targetSelector;
580  return $this;
581  }
582 
583  public function getRefreshSelector() {
584  if (isset($this->_refreshSelector))
586  return "#" . $this->identifier . " tbody";
587  }
588 
595  $this->_refreshSelector = $_refreshSelector;
596  return $this;
597  }
598 
604  public function show($modelInstance) {
605  if (\is_array($modelInstance)) {
606  if (isset($modelInstance[0]) && \is_array(array_values($modelInstance)[0]))
607  $modelInstance = \json_decode(\json_encode($modelInstance), FALSE);
608  }
609  $this->_modelInstance = $modelInstance;
610  }
611 
612  public function getRowClass() {
613  return $this->_rowClass;
614  }
615 
622  public function setRowClass($_rowClass) {
623  $this->_rowClass = $_rowClass;
624  return $this;
625  }
626 
633  public function setEmptyMessage($_emptyMessage) {
634  $this->_emptyMessage = $_emptyMessage;
635  return $this;
636  }
637 
638  public function setSortable($colIndex = NULL) {
639  $this->_sortable = $colIndex;
640  return $this;
641  }
642 
643  public function setActiveRowSelector($class = "active", $event = "click", $multiple = false) {
644  $this->_self->setActiveRowSelector($class, $event, $multiple);
645  return $this;
646  }
647 
648  public function getActiveRowClass() {
649  return $this->_self->getActiveRowClass();
650  }
651 
652  public function hasActiveRowSelector() {
653  return $this->_self->hasActiveRowSelector();
654  }
655 
656  public function hideColumn($colIndex) {
657  if (! \is_array($this->_hiddenColumns))
658  $this->_hiddenColumns = [];
659  $this->_hiddenColumns[] = $colIndex;
660  return $this;
661  }
662 
663  public function setColWidth($colIndex, $width) {
664  $this->_colWidths[$colIndex] = $width;
665  return $this;
666  }
667 
668  public function setColWidths($_colWidths) {
669  $this->_colWidths = $_colWidths;
670  return $this;
671  }
672 
673  public function setColAlignment($colIndex, $alignment) {
674  $this->content["table"]->setColAlignment($colIndex, $alignment);
675  return $this;
676  }
677 
678  public function trigger($event, $params = "[]") {
679  return $this->getHtmlComponent()->trigger($event, $params);
680  }
681 
682  public function onActiveRowChange($jsCode) {
683  $this->getHtmlComponent()->onActiveRowChange($jsCode);
684  return $this;
685  }
686 
691  public function getDeleteBehavior() {
692  return $this->_deleteBehavior;
693  }
694 
699  public function getEditBehavior() {
700  return $this->_editBehavior;
701  }
702 
707  public function getDisplayBehavior() {
709  }
710 
716  $this->_displayBehavior = $_displayBehavior;
717  }
718 
723  public function getGroupByFields() {
724  return $this->_instanceViewer->getGroupByFields();
725  }
726 
731  public function setGroupByFields($_groupByFields) {
732  $this->_instanceViewer->setGroupByFields($_groupByFields);
733  }
734 
735  public function addGroupBy($index) {
736  $index = $this->_getIndex($index);
737  if ($index !== false) {
738  $this->_instanceViewer->addGroupBy($index);
739  }
740  }
741 
746  public function setVisibleHover($_visibleHover) {
747  $this->_visibleHover = $_visibleHover;
748  }
749 
754  public function getPaginationToolbar() {
756  }
757 
758  public function setInverted($recursive = true) {
759  $this->getHtmlComponent()->setInverted($recursive);
760  if ($this->_emptyMessage instanceof HtmlSemDoubleElement) {
761  $this->_emptyMessage->setInverted($recursive);
762  }
763  }
764 
765  public function setFocusable(bool $focusable) {
766  $this->content["table"]->setFocusable($focusable);
767  }
768 
769  public function setFormCaption($caption) {
770  $this->_caption = $caption;
771  }
772 
777  public function getNamePrefix() {
778  return $this->_namePrefix;
779  }
780 
785  public function setNamePrefix($namePrefix): void {
786  $this->_namePrefix = $namePrefix;
787  }
788 }
_generateRow($instance, $fields, &$table, $checkedClass=null, $uuids=null)
Definition: DataTable.php:288
static getValue($array, $key, $pos)
Definition: JArray.php:10
Semantic Message component.
Definition: HtmlMessage.php:18
static doubleBackSlashes($value)
Definition: JString.php:69
setUrls($urls)
Sets the associative array of urls for refreshing, updating or deleting think of defining the update ...
Definition: DataTable.php:455
_associateSearchFieldBehavior(JsUtils $js=NULL, $offset=null)
Definition: DataTable.php:363
addInToolbar($element, $callback=NULL)
Adds a new element in toolbar.
Definition: Widget.php:269
applyToolbarPosition(string $position, $table, $captions=NULL)
Definition: DataTable.php:379
__construct($identifier, $model, $modelInstance=NULL)
Definition: DataTable.php:68
afterCompile($index, $callback)
Associates a $callback function after the compilation of the field at $index position The $callback f...
Definition: DataTable.php:415
setActiveRowSelector($class="active", $event="click", $multiple=false)
Definition: DataTable.php:643
_generateGroupByRow($index, $gbField, $table, $fields, &$activeValues, &$uuids)
Definition: DataTable.php:252
_generateBehavior($op, $params, JsUtils $js)
Definition: DataTable.php:108
_getIndex($fieldName)
Definition: Widget.php:97
DataTable widget for displaying list of objects.
Definition: DataTable.php:27
_generateHeader(HtmlTable $table, $captions)
Definition: DataTable.php:208
wrap($before, $after="")
Definition: BaseHtml.php:171
_associatePaginationBehavior(JsUtils $js=NULL, $offset=null)
Definition: DataTable.php:333
addToolbarRow($part, $table, $captions)
Definition: DataTable.php:420
paginate($page, $total_rowcount, $items_per_page=10, $pages_visibles=null)
Paginates the DataTable element with a Semantic HtmlPaginationMenu component.
Definition: DataTable.php:485
setEmptyMessage($_emptyMessage)
Sets the message displayed when there is no record.
Definition: DataTable.php:633
_setToolbarPosition($table, $captions=NULL)
Definition: DataTable.php:395
compile(JsUtils $js=NULL, &$view=NULL)
Definition: DataTable.php:139
autoPaginate($page=1, $items_per_page=10, $pages_visibles=4)
Auto Paginates the DataTable element with a Semantic HtmlPaginationMenu component.
Definition: DataTable.php:501
_init($instanceViewer, $contentKey, $content, $edition)
Definition: Widget.php:82
setHeaderValues($values=array())
Sets the header values.
Definition: HtmlTable.php:203
Semantic HTML Table component.
Definition: HtmlTable.php:21
Base class for Semantic double elements.
JQuery PHP library.
Definition: JsUtils.php:23
refreshTD($fieldName, $jquery, $view)
Definition: DataTable.php:131
onNewRow($callback)
The callback function called after the insertion of each row when fromDatabaseObjects is called callb...
Definition: DataTable.php:544
setColAlignment($colIndex, $alignment)
Definition: DataTable.php:673
setRowClass($_rowClass)
Sets the default row class (tr class)
Definition: DataTable.php:622
addSearchInToolbar($position=Direction::RIGHT)
Adds a search input in toolbar.
Definition: DataTable.php:524
static removeByKeys($array, $keys)
Definition: JArray.php:136
static sortAssociative($array, $sortedKeys=array())
Definition: JArray.php:66
refresh($compileParts=[ 'tbody'])
Definition: DataTable.php:511
asForm()
Returns a form corresponding to the Datatable.
Definition: DataTable.php:554
setTargetSelector($_targetSelector)
Sets the response element selector for Edit and Delete request with ajax.
Definition: DataTable.php:572