Passed
Push — master ( 42337f...468e47 )
by Jean-Christophe
02:24
created

DataTable::addSearchInToolbar()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 2
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
17
/**
18
 * DataTable widget for displaying list of objects
19
 * @version 1.0
20
 * @author jc
21
 * @since 2.2
22
 *
23
 */
24
class DataTable extends Widget {
25
	use TableTrait,DataTableFieldAsTrait,HasCheckboxesTrait,BaseTrait;
26
	protected $_searchField;
27
	protected $_urls;
28
	protected $_pagination;
29
	protected $_compileParts;
30
	protected $_deleteBehavior;
31
	protected $_editBehavior;
32
	protected $_displayBehavior;
33
	protected $_visibleHover=false;
34
	protected $_targetSelector;
35
	protected $_refreshSelector;
36
	protected $_emptyMessage;
37
	protected $_json;
38
	protected $_rowClass="";
39
	protected $_sortable;
40
	protected $_hiddenColumns;
41
	protected $_colWidths;
42
43
44
	public function __construct($identifier,$model,$modelInstance=NULL) {
45
		parent::__construct($identifier, $model,$modelInstance);
46
		$this->_init(new InstanceViewer($identifier), "table", new HtmlTable($identifier, 0,0), false);
47
		$this->_urls=[];
48
		$this->_emptyMessage=new HtmlMessage("","nothing to display");
49
		$this->_emptyMessage->setIcon("info circle");
50
	}
51
52
	public function run(JsUtils $js){
53
		$offset=$js->scriptCount();
54
		if($this->_hasCheckboxes && isset($js)){
55
			$this->_runCheckboxes($js);
56
		}
57
		if($this->_visibleHover){
58
			$js->execOn("mouseover", "#".$this->identifier." tr", "$(event.target).closest('tr').find('.visibleover').css('visibility', 'visible');",["preventDefault"=>false,"stopPropagation"=>true]);
59
			$js->execOn("mouseout", "#".$this->identifier." tr", "$(event.target).closest('tr').find('.visibleover').css('visibility', 'hidden');",["preventDefault"=>false,"stopPropagation"=>true]);
60
		}
61
		if(\is_array($this->_deleteBehavior))
62
			$this->_generateBehavior("delete",$this->_deleteBehavior, $js);
63
		if(\is_array($this->_editBehavior))
64
			$this->_generateBehavior("edit",$this->_editBehavior,$js);
65
		if(\is_array($this->_displayBehavior)){
66
			$this->_displayBehavior["jsCallback"]='$("#dataTable").hide();';
67
			$this->_generateBehavior("display",$this->_displayBehavior,$js);
68
		}
69
		parent::run($js);
70
		$this->_associateSearchFieldBehavior($js,$offset);
71
		if(isset($this->_pagination))
72
			$this->_associatePaginationBehavior($js,$offset);
73
	}
74
75
	protected function _generateBehavior($op,$params,JsUtils $js){
76
		if(isset($this->_urls[$op])){
77
			$params=\array_merge($params,["attr"=>"data-ajax"]);
78
			$js->ajaxOnClick("#".$this->identifier." ._".$op, $this->_urls[$op],$this->getTargetSelector($op),$params);
79
		}
80
	}
81
82
	/**
83
	 * {@inheritDoc}
84
	 * @see TableTrait::getTable()
85
	 */
86
	protected function getTable() {
87
		return $this->content["table"];
88
	}
89
90
91
	public function compile(JsUtils $js=NULL,&$view=NULL){
92
		if(!$this->_generated){
93
			if(isset($this->_buttonsColumn)){
94
				$this->_instanceViewer->sortColumnContent($this->_buttonsColumn, $this->_buttons);
95
			}
96
			$this->_instanceViewer->setInstance($this->_model);
97
			$captions=$this->_instanceViewer->getCaptions();
98
			$table=$this->content["table"];
99
			if($this->_hasCheckboxes){
100
				$this->_generateMainCheckbox($captions);
101
			}
102
			$table->setRowCount(0, \sizeof($captions));
103
			$this->_generateHeader($table,$captions);
104
105
			if(isset($this->_compileParts))
106
				$table->setCompileParts($this->_compileParts);
107
108
			$this->_generateContent($table);
109
110
			$this->compileExtraElements($table, $captions,$js);
111
			$this->_compileSearchFieldBehavior($js);
112
113
			$this->content=JArray::sortAssociative($this->content, [PositionInTable::BEFORETABLE,"table",PositionInTable::AFTERTABLE]);
114
			$this->_compileForm();
115
			$this->_applyStyleAttributes($table);
116
			$this->_generated=true;
117
		}
118
		return parent::compile($js,$view);
119
	}
120
121
	protected function compileExtraElements($table,$captions,JsUtils $js=NULL){
122
123
		if($this->_hasCheckboxes && $table->hasPart("thead")){
124
			$table->getHeader()->getCell(0, 0)->addClass("no-sort");
125
		}
126
127
		if(isset($this->_toolbar)){
128
			$this->_setToolbarPosition($table, $captions);
129
		}
130
		if(isset($this->_pagination) && $this->_pagination->getVisible()){
131
			$this->_generatePagination($table,$js);
132
		}
133
	}
134
135
	protected function _applyStyleAttributes($table){
136
		if(isset($this->_hiddenColumns))
137
			$this->_hideColumns();
138
			if(isset($this->_colWidths)){
139
				foreach ($this->_colWidths as $colIndex=>$width){
140
					$table->setColWidth($colIndex,$width);
141
				}
142
			}
143
	}
144
145
	protected function _hideColumns(){
146
		foreach ($this->_hiddenColumns as $colIndex){
147
			$this->_self->hideColumn($colIndex);
148
		}
149
		return $this;
150
	}
151
152
	protected function _generateHeader(HtmlTable $table,$captions){
153
		$table->setHeaderValues($captions);
154
		if(isset($this->_sortable)){
155
			$table->setSortable($this->_sortable);
156
		}
157
	}
158
159
160
161
	protected function _generateContent($table){
162
		$objects=$this->_modelInstance;
163
		if(isset($this->_pagination)){
164
			$objects=$this->_pagination->getObjects($this->_modelInstance);
165
		}
166
			InstanceViewer::setIndex(0);
167
			$table->fromDatabaseObjects($objects, function($instance) use($table){
168
				return $this->_generateRow($instance, $table);
169
			});
170
		if($table->getRowCount()==0){
171
			$result=$table->addRow();
172
			$result->mergeRow();
173
			$result->setValues([$this->_emptyMessage]);
174
		}
175
	}
176
177
	protected function _generateRow($instance,&$table,$checkedClass=null){
178
		$this->_instanceViewer->setInstance($instance);
179
		InstanceViewer::$index++;
180
		$values= $this->_instanceViewer->getValues();
181
		$id=$this->_instanceViewer->getIdentifier();
182
		$dataAjax=$id;
183
		$id=$this->cleanIdentifier($id);
184
		if($this->_hasCheckboxes){
185
			$ck=new HtmlCheckbox("ck-".$this->identifier."-".$id,"");
186
			$checked=false;
187
			if(isset($this->_checkedCallback)){
188
				$func=$this->_checkedCallback;
189
				$checked=$func($instance);
190
			}
191
			$ck->setChecked($checked);
192
			$ck->setOnChange("event.stopPropagation();");
193
			$field=$ck->getField();
194
			$field->setProperty("value",$dataAjax);
195
			$field->setProperty("name", "selection[]");
196
			if(isset($checkedClass))
197
				$field->setClass($checkedClass);
198
			\array_unshift($values, $ck);
199
		}
200
		$result=$table->newRow();
201
		$result->setIdentifier($this->identifier."-tr-".$id);
202
		$result->setProperty("data-ajax",$dataAjax);
203
		$result->setValues($values);
204
		$result->addToProperty("class",$this->_rowClass);
205
		return $result;
206
	}
207
208
	protected function _generatePagination($table,$js=NULL){
0 ignored issues
show
Unused Code introduced by
The parameter $js is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

208
	protected function _generatePagination($table,/** @scrutinizer ignore-unused */ $js=NULL){

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
209
		if(isset($this->_toolbar)){
210
			if($this->_toolbarPosition==PositionInTable::FOOTER)
211
				$this->_toolbar->setFloated("left");
212
		}
213
		$footer=$table->getFooter();
214
		$footer->mergeCol();
215
		$footer->addValues($this->_pagination->generateMenu($this->identifier));
216
	}
217
218
	protected function _associatePaginationBehavior(JsUtils $js=NULL,$offset=null){
1 ignored issue
show
Unused Code introduced by
The parameter $offset is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

218
	protected function _associatePaginationBehavior(JsUtils $js=NULL,/** @scrutinizer ignore-unused */ $offset=null){

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
219
		if(isset($this->_urls["refresh"])){
220
			$this->_pagination->getMenu()->postOnClick($this->_urls["refresh"],"{'p':$(this).attr('data-page')}",$this->getRefreshSelector(),["preventDefault"=>false,"jqueryDone"=>"replaceWith","hasLoader"=>false,"jsCallback"=>'$("#'.$this->identifier.'").trigger("pageChange");']);
221
		}
222
	}
223
	
224
	protected function _compileSearchFieldBehavior(JsUtils $js=NULL){
225
		if(isset($this->_searchField) && isset($js) && isset($this->_urls["refresh"])){
226
			$this->_searchField->postOn("change", $this->_urls["refresh"],"{'s':$(self).val()}","#".$this->identifier." tbody",["preventDefault"=>false,"jqueryDone"=>"replaceWith","hasLoader"=>"internal","jsCallback"=>'$("#'.$this->identifier.'").trigger("searchTerminate",[$(self).val()]);']);
227
		}
228
	}
229
	protected function _associateSearchFieldBehavior(JsUtils $js=NULL,$offset=null){
1 ignored issue
show
Unused Code introduced by
The parameter $offset is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

229
	protected function _associateSearchFieldBehavior(JsUtils $js=NULL,/** @scrutinizer ignore-unused */ $offset=null){

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
230
		
231
	}
232
233
	protected function _getFieldName($index){
234
		$fieldName=parent::_getFieldName($index);
235
		if(\is_object($fieldName))
236
			$fieldName="field-".$index;
237
		return $fieldName."[]";
238
	}
239
240
	protected function _getFieldCaption($index){
241
		return null;
242
	}
243
244
	protected function _setToolbarPosition($table,$captions=NULL){
245
		switch ($this->_toolbarPosition){
246
			case PositionInTable::BEFORETABLE:
247
			case PositionInTable::AFTERTABLE:
248
				if(isset($this->_compileParts)===false){
249
					$this->content[$this->_toolbarPosition]=$this->_toolbar;
250
				}
251
				break;
252
			case PositionInTable::HEADER:
253
			case PositionInTable::FOOTER:
254
			case PositionInTable::BODY:
255
				$this->addToolbarRow($this->_toolbarPosition,$table, $captions);
256
				break;
257
		}
258
	}
259
260
	/**
261
	 * Associates a $callback function after the compilation of the field at $index position
262
	 * The $callback function can take the following arguments : $field=>the compiled field, $instance : the active instance of the object, $index: the field position
263
	 * @param int $index postion of the compiled field
264
	 * @param callable $callback function called after the field compilation
265
	 * @return DataTable
266
	 */
267
	public function afterCompile($index,$callback){
268
		$this->_instanceViewer->afterCompile($index,$callback);
269
		return $this;
270
	}
271
272
	private function addToolbarRow($part,$table,$captions){
273
		$hasPart=$table->hasPart($part);
274
		if($hasPart){
275
			$row=$table->getPart($part)->addRow(\sizeof($captions));
276
		}else{
277
			$row=$table->getPart($part)->getRow(0);
278
		}
279
		$row->mergeCol();
280
		$row->setValues([$this->_toolbar]);
281
	}
282
283
	/**
284
	 * {@inheritDoc}
285
	 * @see Widget::getHtmlComponent()
286
	 * @return HtmlTable
287
	 */
288
	public function getHtmlComponent(){
289
		return $this->content["table"];
290
	}
291
292
	public function getUrls() {
293
		return $this->_urls;
294
	}
295
296
	/**
297
	 * Sets the associative array of urls for refreshing, updating or deleting
298
	 * think of defining the update zone with the setTargetSelector method
299
	 * @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
300
	 * @return DataTable
301
	 */
302
	public function setUrls($urls) {
303
		if(\is_array($urls)){
304
			$this->_urls["refresh"]=JArray::getValue($urls, "refresh",0);
305
			$this->_urls["edit"]=JArray::getValue($urls, "edit",1);
306
			$this->_urls["delete"]=JArray::getValue($urls, "delete",2);
307
			$this->_urls["display"]=JArray::getValue($urls, "display",3);
308
		}else{
309
			$this->_urls=["refresh"=>$urls,"edit"=>$urls,"delete"=>$urls,"display"=>$urls];
310
		}
311
		return $this;
312
	}
313
314
	/**
315
	 * Paginates the DataTable element with a Semantic HtmlPaginationMenu component
316
	 * @param number $page the active page number
317
	 * @param number $total_rowcount the total number of items
318
	 * @param number $items_per_page The number of items per page
319
	 * @param number $pages_visibles The number of visible pages in the Pagination component
320
	 * @return DataTable
321
	 */
322
	public function paginate($page,$total_rowcount,$items_per_page=10,$pages_visibles=null){
323
		$this->_pagination=new Pagination($items_per_page,$pages_visibles,$page,$total_rowcount);
324
		return $this;
325
	}
326
327
	/**
328
	 * Auto Paginates the DataTable element with a Semantic HtmlPaginationMenu component
329
	 * @param number $page the active page number
330
	 * @param number $items_per_page The number of items per page
331
	 * @param number $pages_visibles The number of visible pages in the Pagination component
332
	 * @return DataTable
333
	 */
334
	public function autoPaginate($page=1,$items_per_page=10,$pages_visibles=4){
335
		$this->_pagination=new Pagination($items_per_page,$pages_visibles,$page);
336
		return $this;
337
	}
338
339
340
341
	/**
342
	 * @param array $compileParts
343
	 * @return DataTable
344
	 */
345
	public function refresh($compileParts=["tbody"]){
346
		$this->_compileParts=$compileParts;
347
		return $this;
348
	}
349
350
351
	/**
352
	 * Adds a search input in toolbar
353
	 * @param string $position
354
	 * @return \Ajax\common\html\HtmlDoubleElement
355
	 */
356
	public function addSearchInToolbar($position=Direction::RIGHT){
357
		return $this->addInToolbar($this->getSearchField())->setPosition($position);
358
	}
359
360
	public function getSearchField(){
361
		if(isset($this->_searchField)===false){
362
			$this->_searchField=new HtmlInput("search-".$this->identifier,"search","","Search...");
363
			$this->_searchField->addIcon("search",Direction::RIGHT);
364
		}
365
		return $this->_searchField;
366
	}
367
368
	/**
369
	 * The callback function called after the insertion of each row when fromDatabaseObjects is called
370
	 * callback function takes the parameters $row : the row inserted and $object: the instance of model used
371
	 * @param callable $callback
372
	 * @return DataTable
373
	 */
374
	public function onNewRow($callback) {
375
		$this->content["table"]->onNewRow($callback);
376
		return $this;
377
	}
378
379
	/**
380
	 * Returns a form corresponding to the Datatable
381
	 * @return \Ajax\semantic\html\collections\form\HtmlForm
382
	 */
383
	public function asForm(){
384
		return $this->getForm();
385
	}
386
387
388
389
	protected function getTargetSelector($op) {
390
		$result=$this->_targetSelector;
391
		if(!isset($result[$op]))
392
			$result="#".$this->identifier;
393
		return $result[$op];
394
	}
395
396
	/**
397
	 * Sets the response element selector for Edit and Delete request with ajax
398
	 * @param string|array $_targetSelector string or associative array ["edit"=>"edit_selector","delete"=>"delete_selector"]
399
	 * @return DataTable
400
	 */
401
	public function setTargetSelector($_targetSelector) {
402
		if(!\is_array($_targetSelector)){
403
			$_targetSelector=["edit"=>$_targetSelector,"delete"=>$_targetSelector];
404
		}
405
		$this->_targetSelector=$_targetSelector;
406
		return $this;
407
	}
408
409
	public function getRefreshSelector() {
410
		if(isset($this->_refreshSelector))
411
			return $this->_refreshSelector;
412
		return "#".$this->identifier." tbody";
413
	}
414
415
	/**
416
	 * @param string $_refreshSelector
417
	 * @return DataTable
418
	 */
419
	public function setRefreshSelector($_refreshSelector) {
420
		$this->_refreshSelector=$_refreshSelector;
421
		return $this;
422
	}
423
424
	/**
425
	 * {@inheritDoc}
426
	 * @see \Ajax\common\Widget::show()
427
	 */
428
	public function show($modelInstance){
429
		if(\is_array($modelInstance)){
430
			if(isset($modelInstance[0]) && \is_array(array_values($modelInstance)[0]))
431
				$modelInstance=\json_decode(\json_encode($modelInstance), FALSE);
432
		}
433
		$this->_modelInstance=$modelInstance;
434
	}
435
436
	public function getRowClass() {
437
		return $this->_rowClass;
438
	}
439
440
	/**
441
	 * Sets the default row class (tr class)
442
	 * @param string $_rowClass
443
	 * @return DataTable
444
	 */
445
	public function setRowClass($_rowClass) {
446
		$this->_rowClass=$_rowClass;
447
		return $this;
448
	}
449
450
	/**
451
	 * Sets the message displayed when there is no record
452
	 * @param mixed $_emptyMessage
453
	 * @return DataTable
454
	 */
455
	public function setEmptyMessage($_emptyMessage) {
456
		$this->_emptyMessage=$_emptyMessage;
457
		return $this;
458
	}
459
460
	public function setSortable($colIndex=NULL) {
461
		$this->_sortable=$colIndex;
462
		return $this;
463
	}
464
465
	public function setActiveRowSelector($class="active",$event="click",$multiple=false){
466
		$this->_self->setActiveRowSelector($class,$event,$multiple);
467
		return $this;
468
	}
469
470
	public function hideColumn($colIndex){
471
		if(!\is_array($this->_hiddenColumns))
472
			$this->_hiddenColumns=[];
473
		$this->_hiddenColumns[]=$colIndex;
474
		return $this;
475
	}
476
477
	public function setColWidth($colIndex,$width){
478
		$this->_colWidths[$colIndex]=$width;
479
		return $this;
480
	}
481
	public function setColWidths($_colWidths) {
482
		$this->_colWidths = $_colWidths;
483
		return $this;
484
	}
485
486
	public function setColAlignment($colIndex,$alignment){
487
		$this->content["table"]->setColAlignment($colIndex,$alignment);
488
		return $this;
489
	}
490
}
491