Passed
Push — master ( 331007...008e10 )
by Jean-Christophe
02:18
created

DataTable::_generateHeader()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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