Passed
Push — master ( 008e10...57c450 )
by Jean-Christophe
02:05
created

DataTable::_associatePaginationBehavior()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 8
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 7
nc 4
nop 2
dl 0
loc 8
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace Ajax\semantic\widgets\datatable;
4
5
use Ajax\common\Widget;
6
use Ajax\JsUtils;
7
use Ajax\semantic\html\collections\table\HtmlTable;
8
use Ajax\semantic\html\elements\HtmlInput;
9
use Ajax\semantic\html\modules\checkbox\HtmlCheckbox;
10
use Ajax\semantic\html\base\constants\Direction;
11
use Ajax\service\JArray;
12
use Ajax\semantic\widgets\base\InstanceViewer;
13
use Ajax\semantic\html\collections\table\traits\TableTrait;
14
use Ajax\semantic\html\collections\HtmlMessage;
15
use Ajax\semantic\html\base\traits\BaseTrait;
16
use Ajax\service\JString;
17
use Ajax\common\html\HtmlDoubleElement;
18
use Ubiquity\utils\http\URequest;
0 ignored issues
show
Bug introduced by
The type Ubiquity\utils\http\URequest was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
20
/**
21
 * DataTable widget for displaying list of objects
22
 * @version 1.0
23
 * @author jc
24
 * @since 2.2
25
 *
26
 */
27
class DataTable extends Widget {
28
	use TableTrait,DataTableFieldAsTrait,HasCheckboxesTrait,BaseTrait;
29
	protected $_searchField;
30
	protected $_urls;
31
	protected $_pagination;
32
	protected $_compileParts;
33
	protected $_deleteBehavior;
34
	protected $_editBehavior;
35
	protected $_displayBehavior;
36
	protected $_visibleHover=false;
37
	protected $_targetSelector;
38
	protected $_refreshSelector;
39
	protected $_emptyMessage;
40
	protected $_json;
41
	protected $_rowClass="_element";
42
	protected $_sortable;
43
	protected $_hiddenColumns;
44
	protected $_colWidths;
45
46
47
	public function __construct($identifier,$model,$modelInstance=NULL) {
48
		parent::__construct($identifier, $model,$modelInstance);
49
		$this->_init(new InstanceViewer($identifier), "table", new HtmlTable($identifier, 0,0), false);
50
		$this->_urls=[];
51
		$this->_emptyMessage=new HtmlMessage("","nothing to display");
52
		$this->_emptyMessage->setIcon("info circle");
53
	}
54
55
	public function run(JsUtils $js){
56
		$offset=$js->scriptCount();
57
		if($this->_hasCheckboxes && isset($js)){
58
			$this->_runCheckboxes($js);
59
		}
60
		if($this->_visibleHover){
61
			$js->execOn("mouseover", "#".$this->identifier." tr", "$(event.target).closest('tr').find('.visibleover').css('visibility', 'visible');",["preventDefault"=>false,"stopPropagation"=>true]);
62
			$js->execOn("mouseout", "#".$this->identifier." tr", "$(event.target).closest('tr').find('.visibleover').css('visibility', 'hidden');",["preventDefault"=>false,"stopPropagation"=>true]);
63
		}
64
		if(\is_array($this->_deleteBehavior))
65
			$this->_generateBehavior("delete",$this->_deleteBehavior, $js);
66
		if(\is_array($this->_editBehavior))
67
			$this->_generateBehavior("edit",$this->_editBehavior,$js);
68
		if(\is_array($this->_displayBehavior)){
69
			$this->_generateBehavior("display",$this->_displayBehavior,$js);
70
		}
71
		parent::run($js);
72
		if(isset($this->_pagination))
73
			$this->_associatePaginationBehavior($js,$offset);
74
		$this->_associateSearchFieldBehavior($js,$offset);
75
			
76
	}
77
78
	protected function _generateBehavior($op,$params,JsUtils $js){
79
		if(isset($this->_urls[$op])){
80
			$params=\array_merge($params,["attr"=>"data-ajax"]);
81
			$js->ajaxOnClick("#".$this->identifier." ._".$op, $this->_urls[$op],$this->getTargetSelector($op),$params);
82
		}
83
	}
84
85
	/**
86
	 * {@inheritDoc}
87
	 * @see TableTrait::getTable()
88
	 */
89
	protected function getTable() {
90
		return $this->content["table"];
91
	}
92
93
94
	public function compile(JsUtils $js=NULL,&$view=NULL){
95
		if(!$this->_generated){
96
			if(isset($this->_buttonsColumn)){
97
				$this->_instanceViewer->sortColumnContent($this->_buttonsColumn, $this->_buttons);
98
			}
99
			$this->_instanceViewer->setInstance($this->_model);
100
			$captions=$this->_instanceViewer->getCaptions();
101
			$table=$this->content["table"];
102
			if($this->_hasCheckboxes){
103
				$this->_generateMainCheckbox($captions);
104
			}
105
			$table->setRowCount(0, \sizeof($captions));
106
			$this->_generateHeader($table,$captions);
107
108
			if(isset($this->_compileParts))
109
				$table->setCompileParts($this->_compileParts);
110
111
			$this->_generateContent($table);
112
113
			$this->compileExtraElements($table, $captions);
114
			$this->_compileSearchFieldBehavior($js);
115
116
			$this->content=JArray::sortAssociative($this->content, [PositionInTable::BEFORETABLE,"table",PositionInTable::AFTERTABLE]);
117
			$this->_compileForm();
118
			$this->_applyStyleAttributes($table);
119
			$this->_generated=true;
120
		}
121
		return parent::compile($js,$view);
122
	}
123
124
	protected function compileExtraElements($table,$captions){
125
126
		if($this->_hasCheckboxes && $table->hasPart("thead")){
127
			$table->getHeader()->getCell(0, 0)->addClass("no-sort");
128
		}
129
130
		if(isset($this->_toolbar)){
131
			$this->_setToolbarPosition($table, $captions);
132
		}
133
		if(isset($this->_pagination) && $this->_pagination->getVisible()){
134
			$this->_generatePagination($table);
135
		}
136
	}
137
138
	protected function _applyStyleAttributes($table){
139
		if(isset($this->_hiddenColumns))
140
			$this->_hideColumns();
141
			if(isset($this->_colWidths)){
142
				foreach ($this->_colWidths as $colIndex=>$width){
143
					$table->setColWidth($colIndex,$width);
144
				}
145
			}
146
	}
147
148
	protected function _hideColumns(){
149
		foreach ($this->_hiddenColumns as $colIndex){
150
			$this->_self->hideColumn($colIndex);
151
		}
152
		return $this;
153
	}
154
155
	protected function _generateHeader(HtmlTable $table,$captions){
156
		$table->setHeaderValues($captions);
157
		if(isset($this->_sortable)){
158
			$table->setSortable($this->_sortable);
159
		}
160
	}
161
162
163
164
	protected function _generateContent($table){
165
		$objects=$this->_modelInstance;
166
		if(isset($this->_pagination)){
167
			$objects=$this->_pagination->getObjects($this->_modelInstance);
168
		}
169
			InstanceViewer::setIndex(0);
170
			$fields=$this->_instanceViewer->getSimpleProperties();
171
			$groupByFields=$this->_instanceViewer->getGroupByFields();
172
			if(!is_array($groupByFields)){
173
				$table->fromDatabaseObjects($objects, function($instance) use($table,$fields){
174
					return $this->_generateRow($instance, $fields,$table);
175
				});
176
			}else{
177
				$activeValues=array_combine($groupByFields, array_fill(0, sizeof($groupByFields), null));
178
				$uuids=[];
179
				$table->fromDatabaseObjects($objects, function($instance) use($table,$fields,&$activeValues,$groupByFields,&$uuids){
180
					$this->_instanceViewer->setInstance($instance);
181
					foreach ($groupByFields as $index=>$gbField){
182
						$this->_generateGroupByRow($index, $gbField, $table,$fields,$activeValues, $uuids);
183
					}
184
					return $this->_generateRow($instance, $fields,$table,null,$uuids);
185
				});
186
			}
187
		if($table->getRowCount()==0){
188
			$result=$table->addRow();
189
			$result->mergeRow();
190
			$result->setValues([$this->_emptyMessage]);
191
		}
192
	}
193
	
194
	protected function _generateGroupByRow($index,$gbField,$table,$fields,&$activeValues,&$uuids){
195
		$newValue=$this->_instanceViewer->getValue($gbField);
196
		if($this->getElementContent($activeValues[$gbField])!==$this->getElementContent($newValue)){
197
			if($index==0){
198
				$uuids=[];
199
			}
200
			$uuid=uniqid("grp");
201
			$uuids[$gbField]=$uuid;
202
			$id=$this->_instanceViewer->getIdentifier();
203
			$result=$table->addMergeRow(sizeof($fields)+1,$newValue);
204
			$result->setIdentifier($this->identifier."-tr-gb-".$id);
205
			$result->setProperty("data-ajax",$id);
206
			$result->setProperty("data-group",$uuid);
207
			$result->addToProperty("class",$this->_rowClass);
208
			$activeValues[$gbField]=$newValue;
209
		}
210
	}
211
	
212
	private function getElementContent($elm){
213
		if($elm instanceof HtmlDoubleElement){
214
			return $elm->getTextContent();
215
		}
216
		return $elm;
217
	}
218
219
	protected function _generateRow($instance,$fields,&$table,$checkedClass=null,$uuids=null){
220
		$this->_instanceViewer->setInstance($instance);
221
		InstanceViewer::$index++;
222
		$values= $this->_instanceViewer->getValues();
223
		$id=$this->_instanceViewer->getIdentifier();
224
		$dataAjax=$id;
225
		$id=$this->cleanIdentifier($id);
226
		if($this->_hasCheckboxes){
227
			$ck=new HtmlCheckbox("ck-".$this->identifier."-".$id,"");
228
			$checked=false;
229
			if(isset($this->_checkedCallback)){
230
				$func=$this->_checkedCallback;
231
				$checked=$func($instance);
232
			}
233
			$ck->setChecked($checked);
234
			$ck->setOnChange("event.stopPropagation();");
235
			$field=$ck->getField();
236
			$field->setProperty("value",$dataAjax);
237
			$field->setProperty("name", "selection[]");
238
			if(isset($checkedClass))
239
				$field->setClass($checkedClass);
240
			\array_unshift($values, $ck);
241
		}
242
		$result=$table->newRow();
243
		$result->setIdentifier($this->identifier."-tr-".$id);
244
		$result->setProperty("data-ajax",$dataAjax);
245
		$result->setValues($values);
246
		$result->addToProperty("class",$this->_rowClass);
247
		$result->setPropertyValues("data-field", $fields);
248
		if(isset($uuids)){
249
			$result->setProperty("data-child",implode(" ", $uuids));
250
		}
251
		return $result;
252
	}
253
254
	protected function _generatePagination($table){
255
		if(isset($this->_toolbar)){
256
			if($this->_toolbarPosition==PositionInTable::FOOTER)
257
				$this->_toolbar->setFloated("left");
258
		}
259
		$footer=$table->getFooter();
260
		$footer->mergeCol();
261
		$footer->addValues($this->_pagination->generateMenu($this->identifier));
262
	}
263
264
	protected function _associatePaginationBehavior(JsUtils $js=NULL,$offset=null){
265
		if(isset($this->_urls["refresh"])){
266
			$menu=$this->_pagination->getMenu();
267
			if(isset($menu) && isset($js)){
268
				$js->postOnClick("#".$menu->getIdentifier()." .item",$this->_urls["refresh"],"{'p':$(this).attr('data-page'),'_model':'".JString::doubleBackSlashes($this->_model)."'}",$this->getRefreshSelector(),["preventDefault"=>false,"jqueryDone"=>"replaceWith","hasLoader"=>false,"jsCallback"=>'$("#'.$this->identifier.'").trigger("pageChange");$("#'.$this->identifier.'").trigger("activeRowChange");']);
269
				$page=URequest::post("p");
270
				if(isset($page)){
271
					$js->execAtLast('$("#'.$this->getIdentifier().' .pagination").children("a.item").removeClass("active");$("#'.$this->getIdentifier().' .pagination").children("a.item[data-page='.$page.']:not(.no-active)").addClass("active");');
272
				}
273
				
274
			}
275
		}
276
	}
277
	
278
	protected function _compileSearchFieldBehavior(JsUtils $js=NULL){
279
		if(isset($this->_searchField) && isset($js) && isset($this->_urls["refresh"])){
280
			$this->_searchField->postOn("change", $this->_urls["refresh"],"{'s':$(self).val(),'_model':'".JString::doubleBackSlashes($this->_model)."'}","#".$this->identifier." tbody",["preventDefault"=>false,"jqueryDone"=>"replaceWith","hasLoader"=>"internal","jsCallback"=>'$("#'.$this->identifier.'").trigger("searchTerminate",[$(self).val()]);']);
281
		}
282
	}
283
	protected function _associateSearchFieldBehavior(JsUtils $js=NULL,$offset=null){
284
		
285
	}
286
287
	protected function _getFieldName($index){
288
		$fieldName=parent::_getFieldName($index);
289
		if(\is_object($fieldName))
290
			$fieldName="field-".$index;
291
		return $fieldName."[]";
292
	}
293
294
	protected function _getFieldCaption($index){
295
		return null;
296
	}
297
298
	protected function _setToolbarPosition($table,$captions=NULL){
299
		switch ($this->_toolbarPosition){
300
			case PositionInTable::BEFORETABLE:
301
			case PositionInTable::AFTERTABLE:
302
				if(isset($this->_compileParts)===false){
303
					$this->content[$this->_toolbarPosition]=$this->_toolbar;
304
				}
305
				break;
306
			case PositionInTable::HEADER:
307
			case PositionInTable::FOOTER:
308
			case PositionInTable::BODY:
309
				$this->addToolbarRow($this->_toolbarPosition,$table, $captions);
310
				break;
311
		}
312
	}
313
314
	/**
315
	 * Associates a $callback function after the compilation of the field at $index position
316
	 * The $callback function can take the following arguments : $field=>the compiled field, $instance : the active instance of the object, $index: the field position
317
	 * @param int $index postion of the compiled field
318
	 * @param callable $callback function called after the field compilation
319
	 * @return DataTable
320
	 */
321
	public function afterCompile($index,$callback){
322
		$this->_instanceViewer->afterCompile($index,$callback);
323
		return $this;
324
	}
325
326
	private function addToolbarRow($part,$table,$captions){
327
		$hasPart=$table->hasPart($part);
328
		if($hasPart){
329
			$row=$table->getPart($part)->addRow(\sizeof($captions));
330
		}else{
331
			$row=$table->getPart($part)->getRow(0);
332
		}
333
		$row->mergeCol();
334
		$row->setValues([$this->_toolbar]);
335
	}
336
337
	/**
338
	 * {@inheritDoc}
339
	 * @see Widget::getHtmlComponent()
340
	 * @return HtmlTable
341
	 */
342
	public function getHtmlComponent(){
343
		return $this->content["table"];
344
	}
345
346
	public function getUrls() {
347
		return $this->_urls;
348
	}
349
350
	/**
351
	 * Sets the associative array of urls for refreshing, updating or deleting
352
	 * think of defining the update zone with the setTargetSelector method
353
	 * @param string|array $urls associative array with keys refresh: for refreshing with search field or pagination, edit : for updating a row, delete: for deleting a row
354
	 * @return DataTable
355
	 */
356
	public function setUrls($urls) {
357
		if(\is_array($urls)){
358
			$this->_urls["refresh"]=JArray::getValue($urls, "refresh",0);
359
			$this->_urls["edit"]=JArray::getValue($urls, "edit",1);
360
			$this->_urls["delete"]=JArray::getValue($urls, "delete",2);
361
			$this->_urls["display"]=JArray::getValue($urls, "display",3);
362
		}else{
363
			$this->_urls=["refresh"=>$urls,"edit"=>$urls,"delete"=>$urls,"display"=>$urls];
364
		}
365
		return $this;
366
	}
367
368
	/**
369
	 * Paginates the DataTable element with a Semantic HtmlPaginationMenu component
370
	 * @param number $page the active page number
371
	 * @param number $total_rowcount the total number of items
372
	 * @param number $items_per_page The number of items per page
373
	 * @param number $pages_visibles The number of visible pages in the Pagination component
374
	 * @return DataTable
375
	 */
376
	public function paginate($page,$total_rowcount,$items_per_page=10,$pages_visibles=null){
377
		$this->_pagination=new Pagination($items_per_page,$pages_visibles,$page,$total_rowcount);
378
		return $this;
379
	}
380
381
	/**
382
	 * Auto Paginates the DataTable element with a Semantic HtmlPaginationMenu component
383
	 * @param number $page the active page number
384
	 * @param number $items_per_page The number of items per page
385
	 * @param number $pages_visibles The number of visible pages in the Pagination component
386
	 * @return DataTable
387
	 */
388
	public function autoPaginate($page=1,$items_per_page=10,$pages_visibles=4){
389
		$this->_pagination=new Pagination($items_per_page,$pages_visibles,$page);
390
		return $this;
391
	}
392
393
394
395
	/**
396
	 * @param array $compileParts
397
	 * @return DataTable
398
	 */
399
	public function refresh($compileParts=["tbody"]){
400
		$this->_compileParts=$compileParts;
401
		return $this;
402
	}
403
404
405
	/**
406
	 * Adds a search input in toolbar
407
	 * @param string $position
408
	 * @return \Ajax\common\html\HtmlDoubleElement
409
	 */
410
	public function addSearchInToolbar($position=Direction::RIGHT){
411
		return $this->addInToolbar($this->getSearchField())->setPosition($position);
412
	}
413
414
	public function getSearchField(){
415
		if(isset($this->_searchField)===false){
416
			$this->_searchField=new HtmlInput("search-".$this->identifier,"search","","Search...");
417
			$this->_searchField->addIcon("search",Direction::RIGHT);
418
		}
419
		return $this->_searchField;
420
	}
421
422
	/**
423
	 * The callback function called after the insertion of each row when fromDatabaseObjects is called
424
	 * callback function takes the parameters $row : the row inserted and $object: the instance of model used
425
	 * @param callable $callback
426
	 * @return DataTable
427
	 */
428
	public function onNewRow($callback) {
429
		$this->content["table"]->onNewRow($callback);
430
		return $this;
431
	}
432
433
	/**
434
	 * Returns a form corresponding to the Datatable
435
	 * @return \Ajax\semantic\html\collections\form\HtmlForm
436
	 */
437
	public function asForm(){
438
		return $this->getForm();
439
	}
440
441
442
443
	protected function getTargetSelector($op) {
444
		$result=$this->_targetSelector;
445
		if(!isset($result[$op]))
446
			$result="#".$this->identifier;
447
		return $result[$op];
448
	}
449
450
	/**
451
	 * Sets the response element selector for Edit and Delete request with ajax
452
	 * @param string|array $_targetSelector string or associative array ["edit"=>"edit_selector","delete"=>"delete_selector"]
453
	 * @return DataTable
454
	 */
455
	public function setTargetSelector($_targetSelector) {
456
		if(!\is_array($_targetSelector)){
457
			$_targetSelector=["edit"=>$_targetSelector,"delete"=>$_targetSelector];
458
		}
459
		$this->_targetSelector=$_targetSelector;
460
		return $this;
461
	}
462
463
	public function getRefreshSelector() {
464
		if(isset($this->_refreshSelector))
465
			return $this->_refreshSelector;
466
		return "#".$this->identifier." tbody";
467
	}
468
469
	/**
470
	 * @param string $_refreshSelector
471
	 * @return DataTable
472
	 */
473
	public function setRefreshSelector($_refreshSelector) {
474
		$this->_refreshSelector=$_refreshSelector;
475
		return $this;
476
	}
477
478
	/**
479
	 * {@inheritDoc}
480
	 * @see \Ajax\common\Widget::show()
481
	 */
482
	public function show($modelInstance){
483
		if(\is_array($modelInstance)){
484
			if(isset($modelInstance[0]) && \is_array(array_values($modelInstance)[0]))
485
				$modelInstance=\json_decode(\json_encode($modelInstance), FALSE);
486
		}
487
		$this->_modelInstance=$modelInstance;
488
	}
489
490
	public function getRowClass() {
491
		return $this->_rowClass;
492
	}
493
494
	/**
495
	 * Sets the default row class (tr class)
496
	 * @param string $_rowClass
497
	 * @return DataTable
498
	 */
499
	public function setRowClass($_rowClass) {
500
		$this->_rowClass=$_rowClass;
501
		return $this;
502
	}
503
504
	/**
505
	 * Sets the message displayed when there is no record
506
	 * @param mixed $_emptyMessage
507
	 * @return DataTable
508
	 */
509
	public function setEmptyMessage($_emptyMessage) {
510
		$this->_emptyMessage=$_emptyMessage;
511
		return $this;
512
	}
513
514
	public function setSortable($colIndex=NULL) {
515
		$this->_sortable=$colIndex;
516
		return $this;
517
	}
518
519
	public function setActiveRowSelector($class="active",$event="click",$multiple=false){
520
		$this->_self->setActiveRowSelector($class,$event,$multiple);
521
		return $this;
522
	}
523
524
	public function hideColumn($colIndex){
525
		if(!\is_array($this->_hiddenColumns))
526
			$this->_hiddenColumns=[];
527
		$this->_hiddenColumns[]=$colIndex;
528
		return $this;
529
	}
530
531
	public function setColWidth($colIndex,$width){
532
		$this->_colWidths[$colIndex]=$width;
533
		return $this;
534
	}
535
	public function setColWidths($_colWidths) {
536
		$this->_colWidths = $_colWidths;
537
		return $this;
538
	}
539
540
	public function setColAlignment($colIndex,$alignment){
541
		$this->content["table"]->setColAlignment($colIndex,$alignment);
542
		return $this;
543
	}
544
	
545
	public function trigger($event,$params="[]"){
546
		return $this->getHtmlComponent()->trigger($event,$params);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getHtmlComponent(...rigger($event, $params) targeting Ajax\common\html\BaseHtml::trigger() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
547
	}
548
	
549
	public function onActiveRowChange($jsCode){
550
		$this->getHtmlComponent()->onActiveRowChange($jsCode);
551
		return $this;
552
	}
553
	/**
554
	 * @return mixed
555
	 */
556
	public function getDeleteBehavior() {
557
		return $this->_deleteBehavior;
558
	}
559
560
	/**
561
	 * @return mixed
562
	 */
563
	public function getEditBehavior() {
564
		return $this->_editBehavior;
565
	}
566
567
	/**
568
	 * @return mixed
569
	 */
570
	public function getDisplayBehavior() {
571
		return $this->_displayBehavior;
572
	}
573
	/**
574
	 * @param mixed $_displayBehavior
575
	 */
576
	public function setDisplayBehavior($_displayBehavior) {
577
		$this->_displayBehavior = $_displayBehavior;
578
	}
579
	/**
580
	 * @return mixed
581
	 */
582
	public function getGroupByFields() {
583
		return $this->_instanceViewer->getGroupByFields();
584
	}
585
586
	/**
587
	 * @param mixed $_groupByFields
588
	 */
589
	public function setGroupByFields($_groupByFields) {
590
		$this->_instanceViewer->setGroupByFields($_groupByFields);
591
	}
592
593
594
595
}
596