Completed
Push — master ( 5c7ff3...abc246 )
by Jean-Christophe
03:41
created

DataTable::setCheckedClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
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
16
/**
17
 * DataTable widget for displaying list of objects
18
 * @version 1.0
19
 * @author jc
20
 * @since 2.2
21
 *
22
 */
23
class DataTable extends Widget {
24
	use TableTrait,DataTableFieldAsTrait,HasCheckboxesTrait;
25
	protected $_searchField;
26
	protected $_urls;
27
	protected $_pagination;
28
	protected $_compileParts;
29
	protected $_deleteBehavior;
30
	protected $_editBehavior;
31
	protected $_visibleHover=false;
32
	protected $_targetSelector;
33
34
35
	public function __construct($identifier,$model,$modelInstance=NULL) {
36
		parent::__construct($identifier, $model,$modelInstance);
37
		$this->_init(new InstanceViewer($identifier), "table", new HtmlTable($identifier, 0,0), false);
38
		$this->_urls=[];
39
	}
40
41
	public function run(JsUtils $js){
42
		if($this->_hasCheckboxes && isset($js)){
43
			$this->_runCheckboxes($js);
44
		}
45
		if($this->_visibleHover){
46
			$js->execOn("mouseover", "#".$this->identifier." tr", "$(event.target).closest('tr').find('.visibleover').css('visibility', 'visible');",["preventDefault"=>false,"stopPropagation"=>true]);
47
			$js->execOn("mouseout", "#".$this->identifier." tr", "$(event.target).closest('tr').find('.visibleover').css('visibility', 'hidden');",["preventDefault"=>false,"stopPropagation"=>true]);
48
		}
49
		if(\is_array($this->_deleteBehavior))
50
			$this->_generateBehavior("delete",$this->_deleteBehavior, $js);
51
		if(\is_array($this->_editBehavior))
52
			$this->_generateBehavior("edit",$this->_editBehavior,$js);
53
		return parent::run($js);
54
	}
55
56
57
58
	protected function _generateBehavior($op,$params,JsUtils $js){
59
		if(isset($this->_urls[$op])){
60
			$params=\array_merge($params,["attr"=>"data-ajax"]);
61
			$js->getOnClick("#".$this->identifier." ._".$op, $this->_urls[$op],$this->getTargetSelector(),$params);
62
		}
63
	}
64
65
	/**
66
	 * {@inheritDoc}
67
	 * @see \Ajax\semantic\html\collections\table\TableTrait::getTable()
68
	 */
69
	protected function getTable() {
70
		return $this->content["table"];
71
	}
72
73
74
	public function compile(JsUtils $js=NULL,&$view=NULL){
75
		if(!$this->_generated){
76
			$this->_instanceViewer->setInstance($this->_model);
77
			$captions=$this->_instanceViewer->getCaptions();
78
79
			$table=$this->content["table"];
80
81
			if($this->_hasCheckboxes){
82
				$this->_generateMainCheckbox($captions);
83
			}
84
85
			$table->setRowCount(0, \sizeof($captions));
86
			$table->setHeaderValues($captions);
87
			if(isset($this->_compileParts))
88
				$table->setCompileParts($this->_compileParts);
89
90
			if(isset($this->_searchField) && isset($js)){
91
				if(isset($this->_urls["refresh"]))
92
					$this->_searchField->postOn("change", $this->_urls["refresh"],"{'s':$(this).val()}","#".$this->identifier." tbody",["preventDefault"=>false,"jqueryDone"=>"replaceWith"]);
93
			}
94
95
			$this->_generateContent($table);
96
97
			if($this->_hasCheckboxes && $table->hasPart("thead")){
98
					$table->getHeader()->getCell(0, 0)->addClass("no-sort");
99
			}
100
101
			if(isset($this->_pagination) && $this->_pagination->getVisible()){
102
				$this->_generatePagination($table);
103
			}
104
			if(isset($this->_toolbar)){
105
				$this->_setToolbarPosition($table, $captions);
106
			}
107
			$this->content=JArray::sortAssociative($this->content, [PositionInTable::BEFORETABLE,"table",PositionInTable::AFTERTABLE]);
108
			$this->_compileForm();
109
			$this->_generated=true;
110
		}
111
		return parent::compile($js,$view);
112
	}
113
114
115
116
	protected function _generateContent($table){
117
		$objects=$this->_modelInstance;
118
		if(isset($this->_pagination)){
119
			$objects=$this->_pagination->getObjects($this->_modelInstance);
120
		}
121
		InstanceViewer::setIndex(0);
122
		$table->fromDatabaseObjects($objects, function($instance) use($table){
123
			$this->_instanceViewer->setInstance($instance);
124
			InstanceViewer::$index++;
125
			$values= $this->_instanceViewer->getValues();
126
			if($this->_hasCheckboxes){
127
				$ck=new HtmlCheckbox("ck-".$this->identifier,"");
128
				$field=$ck->getField();
129
				$field->setProperty("value",$this->_instanceViewer->getIdentifier());
130
				$field->setProperty("name", "selection[]");
131
				\array_unshift($values, $ck);
132
			}
133
			$result=$table->newRow();
134
			$result->setIdentifier($this->identifier."-tr-".$this->_instanceViewer->getIdentifier());
135
			$result->setValues($values);
136
			return $result;
137
		});
138
	}
139
140
	private function _generatePagination($table){
141
		$footer=$table->getFooter();
142
		$footer->mergeCol();
143
		$menu=new HtmlPaginationMenu("pagination-".$this->identifier,$this->_pagination->getPagesNumbers());
144
		$menu->floatRight();
145
		$menu->setActiveItem($this->_pagination->getPage()-1);
146
		$footer->setValues($menu);
147
		if(isset($this->_urls["refresh"]))
148
			$menu->postOnClick($this->_urls["refresh"],"{'p':$(this).attr('data-page')}","#".$this->identifier." tbody",["preventDefault"=>false,"jqueryDone"=>"replaceWith"]);
149
	}
150
151
	protected function _getFieldName($index){
152
		return parent::_getFieldName($index)."[]";
153
	}
154
155
	protected function _getFieldCaption($index){
156
		return null;
157
	}
158
159
	protected function _setToolbarPosition($table,$captions=NULL){
160
		switch ($this->_toolbarPosition){
161
			case PositionInTable::BEFORETABLE:
162
			case PositionInTable::AFTERTABLE:
163
				if(isset($this->_compileParts)===false){
164
					$this->content[$this->_toolbarPosition]=$this->_toolbar;
165
				}
166
				break;
167
			case PositionInTable::HEADER:
168
			case PositionInTable::FOOTER:
169
			case PositionInTable::BODY:
170
				$this->addToolbarRow($this->_toolbarPosition,$table, $captions);
171
				break;
172
		}
173
	}
174
175
	/**
176
	 * Associates a $callback function after the compilation of the field at $index position
177
	 * The $callback function can take the following arguments : $field=>the compiled field, $instance : the active instance of the object, $index: the field position
178
	 * @param int $index postion of the compiled field
179
	 * @param callable $callback function called after the field compilation
180
	 * @return \Ajax\semantic\widgets\datatable\DataTable
181
	 */
182
	public function afterCompile($index,$callback){
183
		$this->_instanceViewer->afterCompile($index,$callback);
184
		return $this;
185
	}
186
187
	private function addToolbarRow($part,$table,$captions){
188
		$hasPart=$table->hasPart($part);
189
		if($hasPart){
190
			$row=$table->getPart($part)->addRow(\sizeof($captions));
191
		}else{
192
			$row=$table->getPart($part)->getRow(0);
193
		}
194
		$row->mergeCol();
195
		$row->setValues([$this->_toolbar]);
196
	}
197
198
	public function getHtmlComponent(){
199
		return $this->content["table"];
200
	}
201
202
	public function getUrls() {
203
		return $this->_urls;
204
	}
205
206
	/**
207
	 * Sets the associative array of urls for refreshing, updating or deleting
208
	 * @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
209
	 * @return \Ajax\semantic\widgets\datatable\DataTable
210
	 */
211
	public function setUrls($urls) {
212
		if(\is_array($urls)){
213
			$this->_urls["refresh"]=JArray::getValue($urls, "refresh",0);
214
			$this->_urls["edit"]=JArray::getValue($urls, "edit",1);
215
			$this->_urls["delete"]=JArray::getValue($urls, "delete",2);
216
		}else{
217
			$this->_urls=["refresh"=>$urls,"edit"=>$urls,"delete"=>$urls];
218
		}
219
		return $this;
220
	}
221
222
	public function paginate($items_per_page=10,$page=1){
223
		$this->_pagination=new Pagination($items_per_page,4,$page);
224
	}
225
226
227
228
	public function refresh($compileParts=["tbody"]){
229
		$this->_compileParts=$compileParts;
230
		return $this;
231
	}
232
233
234
	public function addSearchInToolbar($position=Direction::RIGHT){
235
		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...
236
	}
237
238
	public function getSearchField(){
239
		if(isset($this->_searchField)===false){
240
			$this->_searchField=new HtmlInput("search-".$this->identifier,"search","","Search...");
241
			$this->_searchField->addIcon("search",Direction::RIGHT);
242
		}
243
		return $this->_searchField;
244
	}
245
246
	/**
247
	 * The callback function called after the insertion of each row when fromDatabaseObjects is called
248
	 * callback function takes the parameters $row : the row inserted and $object: the instance of model used
249
	 * @param callable $callback
250
	 * @return DataTable
251
	 */
252
	public function onNewRow($callback) {
253
		$this->content["table"]->onNewRow($callback);
254
		return $this;
255
	}
256
257
	public function asForm(){
258
		return $this->getForm();
259
	}
260
261
262
263
	protected function getTargetSelector() {
264
		$result=$this->_targetSelector;
265
		if(!isset($result))
266
			$result="#".$this->identifier;
267
		return $result;
268
	}
269
270
	/**
271
	 * Sets the response element selector for Edit and Delete request with ajax
272
	 * @param string $_targetSelector
273
	 * @return \Ajax\semantic\widgets\datatable\DataTable
274
	 */
275
	public function setTargetSelector($_targetSelector) {
276
		$this->_targetSelector=$_targetSelector;
277
		return $this;
278
	}
279
}