Completed
Push — master ( 872df9...4bf172 )
by Jean-Christophe
04:11
created

DataTable::getRowClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 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\collections\menus\HtmlPaginationMenu;
10
use Ajax\semantic\html\modules\checkbox\HtmlCheckbox;
11
use Ajax\semantic\html\base\constants\Direction;
12
use Ajax\service\JArray;
13
use Ajax\semantic\widgets\base\InstanceViewer;
14
use Ajax\semantic\html\collections\table\traits\TableTrait;
15
use Ajax\semantic\html\collections\HtmlMessage;
16
use Ajax\semantic\html\collections\menus\HtmlMenu;
17
18
/**
19
 * DataTable widget for displaying list of objects
20
 * @version 1.0
21
 * @author jc
22
 * @since 2.2
23
 *
24
 */
25
class DataTable extends Widget {
26
	use TableTrait,DataTableFieldAsTrait,HasCheckboxesTrait;
27
	protected $_searchField;
28
	protected $_urls;
29
	protected $_pagination;
30
	protected $_compileParts;
31
	protected $_deleteBehavior;
32
	protected $_editBehavior;
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
		if($this->_hasCheckboxes && isset($js)){
54
			$this->_runCheckboxes($js);
55
		}
56
		if($this->_visibleHover){
57
			$js->execOn("mouseover", "#".$this->identifier." tr", "$(event.target).closest('tr').find('.visibleover').css('visibility', 'visible');",["preventDefault"=>false,"stopPropagation"=>true]);
58
			$js->execOn("mouseout", "#".$this->identifier." tr", "$(event.target).closest('tr').find('.visibleover').css('visibility', 'hidden');",["preventDefault"=>false,"stopPropagation"=>true]);
59
		}
60
		if(\is_array($this->_deleteBehavior))
61
			$this->_generateBehavior("delete",$this->_deleteBehavior, $js);
62
		if(\is_array($this->_editBehavior))
63
			$this->_generateBehavior("edit",$this->_editBehavior,$js);
64
		return parent::run($js);
65
	}
66
67
68
69
	protected function _generateBehavior($op,$params,JsUtils $js){
70
		if(isset($this->_urls[$op])){
71
			$params=\array_merge($params,["attr"=>"data-ajax"]);
72
			$js->getOnClick("#".$this->identifier." ._".$op, $this->_urls[$op],$this->getTargetSelector(),$params);
73
		}
74
	}
75
76
	/**
77
	 * {@inheritDoc}
78
	 * @see \Ajax\semantic\html\collections\table\TableTrait::getTable()
79
	 */
80
	protected function getTable() {
81
		return $this->content["table"];
82
	}
83
84
85
	public function compile(JsUtils $js=NULL,&$view=NULL){
86
		if(!$this->_generated){
87
			$this->_instanceViewer->setInstance($this->_model);
88
			$captions=$this->_instanceViewer->getCaptions();
89
90
			$table=$this->content["table"];
91
92
			if($this->_hasCheckboxes){
93
				$this->_generateMainCheckbox($captions);
94
			}
95
96
			$table->setRowCount(0, \sizeof($captions));
97
			$this->_generateHeader($table,$captions);
98
99
			if(isset($this->_compileParts))
100
				$table->setCompileParts($this->_compileParts);
101
102
			if(isset($this->_searchField) && isset($js)){
103
				if(isset($this->_urls["refresh"]))
104
					$this->_searchField->postOn("change", $this->_urls["refresh"],"{'s':$(this).val()}","#".$this->identifier." tbody",["preventDefault"=>false,"jqueryDone"=>"replaceWith"]);
105
			}
106
107
			$this->_generateContent($table);
108
109
			if($this->_hasCheckboxes && $table->hasPart("thead")){
110
					$table->getHeader()->getCell(0, 0)->addClass("no-sort");
111
			}
112
113
			if(isset($this->_toolbar)){
114
				$this->_setToolbarPosition($table, $captions);
115
			}
116
			if(isset($this->_pagination) && $this->_pagination->getVisible()){
117
				$this->_generatePagination($table,$js);
118
			}
119
120
			$this->content=JArray::sortAssociative($this->content, [PositionInTable::BEFORETABLE,"table",PositionInTable::AFTERTABLE]);
121
			$this->_compileForm();
122
			if(isset($this->_hiddenColumns))
123
				$this->_hideColumns();
124
			if(isset($this->_colWidths)){
125
				foreach ($this->_colWidths as $colIndex=>$width){
126
					$table->setColWidth($colIndex,$width);
127
				}
128
			}
129
			
130
			$this->_generated=true;
131
		}
132
		return parent::compile($js,$view);
133
	}
134
135
	protected function _hideColumns(){
136
		$table=$this->getTable();
137
		foreach ($this->_hiddenColumns as $colIndex){
138
			$table->hideColumn($colIndex);
139
		}
140
		return $this;
141
	}
142
143
	protected function _generateHeader(HtmlTable $table,$captions){
144
		$table->setHeaderValues($captions);
145
		if(isset($this->_sortable)){
146
			$table->setSortable($this->_sortable);
147
		}
148
	}
149
150
151
152
	protected function _generateContent($table){
153
		$objects=$this->_modelInstance;
154
		if(isset($this->_pagination)){
155
			$objects=$this->_pagination->getObjects($this->_modelInstance);
156
		}
157
			InstanceViewer::setIndex(0);
158
			$table->fromDatabaseObjects($objects, function($instance) use($table){
159
				return $this->_generateRow($instance, $table);
160
			});
161
		if($table->getRowCount()==0){
162
			$result=$table->addRow();
163
			$result->mergeRow();
164
			$result->setValues([$this->_emptyMessage]);
165
		}
166
	}
167
168
	protected function _generateRow($instance,&$table,$checkedClass=null){
169
		$this->_instanceViewer->setInstance($instance);
170
		InstanceViewer::$index++;
171
		$values= $this->_instanceViewer->getValues();
172
		$id=$this->_instanceViewer->getIdentifier();
173
		if($this->_hasCheckboxes){
174
			$ck=new HtmlCheckbox("ck-".$this->identifier."-".$id,"");
175
			$ck->setOnChange("event.stopPropagation();");
176
			$field=$ck->getField();
177
			$field->setProperty("value",$id);
178
			$field->setProperty("name", "selection[]");
179
			if(isset($checkedClass))
180
				$field->setClass($checkedClass);
181
			\array_unshift($values, $ck);
182
		}
183
		$result=$table->newRow();
184
		$result->setIdentifier($this->identifier."-tr-".$id);
185
		$result->setProperty("data-ajax",$id);
186
		$result->setValues($values);
187
		$result->addToProperty("class",$this->_rowClass);
188
		return $result;
189
	}
190
191
	protected function _generatePagination($table,$js=NULL){
192
		if(isset($this->_toolbar)){
193
			if($this->_toolbarPosition==PositionInTable::FOOTER)
194
				$this->_toolbar->setFloated("left");
195
		}
196
		$footer=$table->getFooter();
197
		$footer->mergeCol();
198
		$menu=new HtmlPaginationMenu("pagination-".$this->identifier,$this->_pagination->getPagesNumbers());
199
		$menu->floatRight();
200
		$menu->setActiveItem($this->_pagination->getPage()-1);
201
		$footer->addValues($menu);
202
		$this->_associatePaginationBehavior($menu,$js);
203
	}
204
205
	protected function _associatePaginationBehavior(HtmlMenu $menu,JsUtils $js=NULL){
206
		if(isset($this->_urls["refresh"])){
207
			$menu->postOnClick($this->_urls["refresh"],"{'p':$(this).attr('data-page')}",$this->getRefreshSelector(),["preventDefault"=>false,"jqueryDone"=>"replaceWith"]);
208
		}
209
	}
210
211
	protected function _getFieldName($index){
212
		$fieldName=parent::_getFieldName($index);
213
		if(\is_object($fieldName))
214
			$fieldName="field-".$index;
215
		return $fieldName."[]";
216
	}
217
218
	protected function _getFieldCaption($index){
219
		return null;
220
	}
221
222
	protected function _setToolbarPosition($table,$captions=NULL){
223
		switch ($this->_toolbarPosition){
224
			case PositionInTable::BEFORETABLE:
225
			case PositionInTable::AFTERTABLE:
226
				if(isset($this->_compileParts)===false){
227
					$this->content[$this->_toolbarPosition]=$this->_toolbar;
228
				}
229
				break;
230
			case PositionInTable::HEADER:
231
			case PositionInTable::FOOTER:
232
			case PositionInTable::BODY:
233
				$this->addToolbarRow($this->_toolbarPosition,$table, $captions);
234
				break;
235
		}
236
	}
237
238
	/**
239
	 * Associates a $callback function after the compilation of the field at $index position
240
	 * The $callback function can take the following arguments : $field=>the compiled field, $instance : the active instance of the object, $index: the field position
241
	 * @param int $index postion of the compiled field
242
	 * @param callable $callback function called after the field compilation
243
	 * @return DataTable
244
	 */
245
	public function afterCompile($index,$callback){
246
		$this->_instanceViewer->afterCompile($index,$callback);
247
		return $this;
248
	}
249
250
	private function addToolbarRow($part,$table,$captions){
251
		$hasPart=$table->hasPart($part);
252
		if($hasPart){
253
			$row=$table->getPart($part)->addRow(\sizeof($captions));
254
		}else{
255
			$row=$table->getPart($part)->getRow(0);
256
		}
257
		$row->mergeCol();
258
		$row->setValues([$this->_toolbar]);
259
	}
260
261
	public function getHtmlComponent(){
262
		return $this->content["table"];
263
	}
264
265
	public function getUrls() {
266
		return $this->_urls;
267
	}
268
269
	/**
270
	 * Sets the associative array of urls for refreshing, updating or deleting
271
	 * @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
272
	 * @return DataTable
273
	 */
274
	public function setUrls($urls) {
275
		if(\is_array($urls)){
276
			$this->_urls["refresh"]=JArray::getValue($urls, "refresh",0);
277
			$this->_urls["edit"]=JArray::getValue($urls, "edit",1);
278
			$this->_urls["delete"]=JArray::getValue($urls, "delete",2);
279
		}else{
280
			$this->_urls=["refresh"=>$urls,"edit"=>$urls,"delete"=>$urls];
281
		}
282
		return $this;
283
	}
284
285
	/**
286
	 * Paginates the DataTable element with a Semantic HtmlPaginationMenu component
287
	 * @param number $page the active page number
288
	 * @param number $total_rowcount the total number of items
289
	 * @param number $items_per_page The number of items per page
290
	 * @param number $pages_visibles The number of visible pages in the Pagination component
291
	 * @return DataTable
292
	 */
293
	public function paginate($page,$total_rowcount,$items_per_page=10,$pages_visibles=null){
294
		$this->_pagination=new Pagination($items_per_page,$pages_visibles,$page,$total_rowcount);
295
		return $this;
296
	}
297
298
	/**
299
	 * Auto Paginates the DataTable element with a Semantic HtmlPaginationMenu component
300
	 * @param number $page the active page number
301
	 * @param number $items_per_page The number of items per page
302
	 * @param number $pages_visibles The number of visible pages in the Pagination component
303
	 * @return DataTable
304
	 */
305
	public function autoPaginate($page=1,$items_per_page=10,$pages_visibles=4){
306
		$this->_pagination=new Pagination($items_per_page,$pages_visibles,$page);
307
		return $this;
308
	}
309
310
311
312
	/**
313
	 * @param array $compileParts
314
	 * @return DataTable
315
	 */
316
	public function refresh($compileParts=["tbody"]){
317
		$this->_compileParts=$compileParts;
318
		return $this;
319
	}
320
321
322
	public function addSearchInToolbar($position=Direction::RIGHT){
323
		return $this->addInToolbar($this->getSearchField())->setPosition($position);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Ajax\common\html\HtmlDoubleElement as the method setPosition() does only exist in the following sub-classes of Ajax\common\html\HtmlDoubleElement: Ajax\bootstrap\html\content\HtmlGridCol, Ajax\semantic\html\colle...menus\HtmlAccordionMenu, Ajax\semantic\html\collections\menus\HtmlIconMenu, Ajax\semantic\html\colle...nus\HtmlLabeledIconMenu, Ajax\semantic\html\collections\menus\HtmlMenu, Ajax\semantic\html\colle...enus\HtmlPaginationMenu, Ajax\semantic\html\content\HtmlAccordionMenuItem, Ajax\semantic\html\content\HtmlDropdownItem, Ajax\semantic\html\content\HtmlMenuItem, Ajax\semantic\html\modules\HtmlPopup. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
324
	}
325
326
	public function getSearchField(){
327
		if(isset($this->_searchField)===false){
328
			$this->_searchField=new HtmlInput("search-".$this->identifier,"search","","Search...");
329
			$this->_searchField->addIcon("search",Direction::RIGHT);
330
		}
331
		return $this->_searchField;
332
	}
333
334
	/**
335
	 * The callback function called after the insertion of each row when fromDatabaseObjects is called
336
	 * callback function takes the parameters $row : the row inserted and $object: the instance of model used
337
	 * @param callable $callback
338
	 * @return DataTable
339
	 */
340
	public function onNewRow($callback) {
341
		$this->content["table"]->onNewRow($callback);
342
		return $this;
343
	}
344
345
	public function asForm(){
346
		return $this->getForm();
347
	}
348
349
350
351
	protected function getTargetSelector() {
352
		$result=$this->_targetSelector;
353
		if(!isset($result))
354
			$result="#".$this->identifier;
355
		return $result;
356
	}
357
358
	/**
359
	 * Sets the response element selector for Edit and Delete request with ajax
360
	 * @param string $_targetSelector
361
	 * @return \Ajax\semantic\widgets\datatable\DataTable
362
	 */
363
	public function setTargetSelector($_targetSelector) {
364
		$this->_targetSelector=$_targetSelector;
365
		return $this;
366
	}
367
368
	public function getRefreshSelector() {
369
		if(isset($this->_refreshSelector))
370
			return $this->_refreshSelector;
371
		return "#".$this->identifier." tbody";
372
	}
373
374
	/**
375
	 * @param string $_refreshSelector
376
	 * @return DataTable
377
	 */
378
	public function setRefreshSelector($_refreshSelector) {
379
		$this->_refreshSelector=$_refreshSelector;
380
		return $this;
381
	}
382
383
	/**
384
	 * {@inheritDoc}
385
	 * @see \Ajax\common\Widget::show()
386
	 */
387
	public function show($modelInstance){
388
		if(\is_array($modelInstance)){
389
			if(\is_array(array_values($modelInstance)[0]))
390
				$modelInstance=\json_decode(\json_encode($modelInstance), FALSE);
391
		}
392
		$this->_modelInstance=$modelInstance;
393
	}
394
395
	public function getRowClass() {
396
		return $this->_rowClass;
397
	}
398
399
	/**
400
	 * Sets the default row class (tr class)
401
	 * @param string $_rowClass
402
	 * @return DataTable
403
	 */
404
	public function setRowClass($_rowClass) {
405
		$this->_rowClass=$_rowClass;
406
		return $this;
407
	}
408
409
	/**
410
	 * Sets the message displayed when there is no record
411
	 * @param mixed $_emptyMessage
412
	 * @return DataTable
413
	 */
414
	public function setEmptyMessage($_emptyMessage) {
415
		$this->_emptyMessage=$_emptyMessage;
416
		return $this;
417
	}
418
419
	public function setSortable($colIndex=NULL) {
420
		$this->_sortable=$colIndex;
421
		return $this;
422
	}
423
424
	public function setActiveRowSelector($class="active",$event="click",$multiple=false){
425
		$this->getTable()->setActiveRowSelector($class,$event,$multiple);
426
		return $this;
427
	}
428
429
	public function hideColumn($colIndex){
430
		if(!\is_array($this->_hiddenColumns))
431
			$this->_hiddenColumns=[];
432
		$this->_hiddenColumns[]=$colIndex;
433
		return $this;
434
	}
435
	
436
	public function setColWidth($colIndex,$width){
437
		$this->_colWidths[$colIndex]=$width;
438
		return $this;
439
	}
440
	public function setColWidths($_colWidths) {
441
		$this->_colWidths = $_colWidths;
442
		return $this;
443
	}
444
	
445
446
}