Completed
Push — master ( 3ee6aa...f7422c )
by Jean-Christophe
02:00
created

CRUDController::index()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 0
1
<?php
2
3
namespace Ubiquity\controllers\crud;
4
5
use Ubiquity\orm\DAO;
6
use Ubiquity\controllers\ControllerBase;
7
use Ubiquity\controllers\admin\interfaces\HasModelViewerInterface;
8
use Ubiquity\controllers\admin\viewers\ModelViewer;
9
use Ubiquity\controllers\semantic\MessagesTrait;
10
use Ubiquity\utils\http\URequest;
11
use Ubiquity\utils\http\UResponse;
12
use Ubiquity\controllers\rest\ResponseFormatter;
13
use Ajax\semantic\widgets\datatable\Pagination;
14
use Ubiquity\orm\OrmUtils;
15
use Ubiquity\utils\base\UString;
16
use Ajax\semantic\html\collections\HtmlMessage;
17
18
abstract class CRUDController extends ControllerBase implements HasModelViewerInterface{
19
	use MessagesTrait;
20
	protected $model;
21
	protected $modelViewer;
22
	protected $events;
23
	protected $crudFiles;
24
	protected $adminDatas;
25
	protected $activePage;
26
	
27
	/**
28
	 * Default page : list all objects
29
	 */
30
	public function index() {
31
		$objects=$this->getInstances();
32
		$modal=($this->_getModelViewer()->isModal($objects,$this->model))?"modal":"no";
33
		$this->_getModelViewer()->getModelDataTable($objects, $this->model);
34
		$this->jquery->getOnClick ( "#btAddNew", $this->_getBaseRoute() . "/newModel/" . $modal, "#frm-add-update",["hasLoader"=>"internal"] );
35
		$this->_getEvents()->onDisplayElements();
36
		$this->crudLoadView($this->_getFiles()->getViewIndex(), [ "classname" => $this->model ,"messages"=>$this->jquery->semantic()->matchHtmlComponents(function($compo){return $compo instanceof HtmlMessage;})]);		
1 ignored issue
show
Bug introduced by
The class Ajax\semantic\html\collections\HtmlMessage does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
37
	}
38
	
39
	protected function getInstances($page=1,$id=null){
40
		$this->activePage=$page;
41
		$model=$this->model;
42
		$condition=$this->_getInstancesFilter($model);
43
		$recordsPerPage=$this->_getModelViewer()->recordsPerPage($model,DAO::count($model,$condition));
44
		if(is_numeric($recordsPerPage)){
45
			if(isset($id)){
46
				$rownum=DAO::getRownum($model, $id);
47
				$this->activePage=Pagination::getPageOfRow($rownum,$recordsPerPage);
48
			}
49
			return DAO::paginate($model,$this->activePage,$recordsPerPage,$condition);
50
		}
51
		return DAO::getAll($model,$condition);
52
	}
53
	
54
	public function _getInstancesFilter($model){
55
		return "1=1";
56
	}
57
	
58
	protected function search($model,$search){
59
		$fields=$this->_getAdminData()->getSearchFieldNames($model);
60
		$condition=$this->_getInstancesFilter($model);
61
		return CRUDHelper::search($model, $search, $fields,$condition);
62
	}
63
	
64
	public function refresh_(){
65
		$model=$this->model;
66
		if(isset($_POST["s"])){
67
			$instances=$this->search($model, $_POST["s"]);
68
		}else{
69
			$page=URequest::post("p",1);
70
			$instances=$this->getInstances($page);
71
		}
72
		$recordsPerPage=$this->_getModelViewer()->recordsPerPage($model,DAO::count($model,$this->_getInstancesFilter($model)));
73
		$grpByFields=$this->_getModelViewer()->getGroupByFields();
74
		if(isset($recordsPerPage)){
75
			if(!is_array($grpByFields)){
76
				UResponse::asJSON();
77
				$responseFormatter=new ResponseFormatter();
78
				print_r($responseFormatter->getJSONDatas($instances));
79
			}else{
80
				$this->_renderDataTableForRefresh($instances, $model);
81
			}
82
		}else{
83
			$this->jquery->execAtLast('$("#search-query-content").html("'.$_POST["s"].'");$("#search-query").show();$("#table-details").html("");');
84
			$this->_renderDataTableForRefresh($instances, $model);
85
		}
86
	}
87
	
88
	private function _renderDataTableForRefresh($instances,$model){
89
		$this->formModal=($this->_getModelViewer()->isModal($instances,$model))? "modal" : "no";
90
		$compo= $this->_getModelViewer()->getModelDataTable($instances, $model)->refresh(["tbody"]);
91
		$this->_getEvents()->onDisplayElements();
92
		$this->jquery->renderView("@framework/main/component.html",["compo"=>$compo]);
93
	}
94
	
95
	/**
96
	 * Edits an instance
97
	 * @param string $modal Accept "no" or "modal" for a modal dialog
98
	 * @param string $ids the primary value(s)
99
	 */
100 View Code Duplication
	public function edit($modal="no", $ids="") {
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
101
		if(URequest::isAjax()){
102
			$instance=$this->getModelInstance($ids);
103
			$instance->_new=false;
104
			$this->_edit($instance, $modal);
105
		}else{
106
			$this->jquery->execAtLast("$('._edit[data-ajax={$ids}]').trigger('click');");
107
			$this->index();
108
		}
109
	}
110
	/**
111
	 * Adds a new instance and edits it
112
	 * @param string $modal Accept "no" or "modal" for a modal dialog
113
	 */
114 View Code Duplication
	public function newModel($modal="no") {
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
115
		if(URequest::isAjax()){
116
			$model=$this->model;
117
			$instance=new $model();
118
			$instance->_new=true;
119
			$this->_edit($instance, $modal);
120
		}else{
121
			$this->jquery->execAtLast("$('.ui.button._new').trigger('click');");
122
			$this->index();
123
		}
124
	}
125
	
126
	/**
127
	 * Displays an instance
128
	 * @param string $modal
129
	 * @param string $ids
130
	 */
131
	public function display($modal="no",$ids=""){
132
		if(URequest::isAjax()){
133
			$instance=$this->getModelInstance($ids);
134
			$key=OrmUtils::getFirstKeyValue($instance);
135
			$this->jquery->execOn("click","._close",'$("#table-details").html("");$("#dataTable").show();');
136
			$this->jquery->getOnClick("._edit", $this->_getBaseRoute()."/edit/".$modal."/".$key,"#frm-add-update");
137
			$this->jquery->getOnClick("._delete", $this->_getBaseRoute()."/delete/".$key,"#table-messages");
138
			
139
			$this->_getModelViewer()->getModelDataElement($instance, $this->model,$modal);
140
			$this->jquery->renderView($this->_getFiles()->getViewDisplay(), [ "classname" => $this->model,"instance"=>$instance,"pk"=>$key ]);
141
		}else{
142
			$this->jquery->execAtLast("$('._display[data-ajax={$ids}]').trigger('click');");
143
			$this->index();
144
		}
145
	}
146
	
147
	protected function _edit($instance, $modal="no") {
148
		$_SESSION["instance"]=$instance;
149
		$modal=($modal == "modal");
150
		$form=$this->_getModelViewer()->getForm("frmEdit", $instance);
151
		$this->jquery->click("#action-modal-frmEdit-0", "$('#frmEdit').form('submit');", false);
152
		if (!$modal) {
153
			$this->jquery->click("#bt-cancel", "$('#form-container').transition('drop');");
154
			$this->jquery->compile($this->view);
155
			$this->loadView($this->_getFiles()->getViewForm(), [ "modal" => $modal ]);
156
		} else {
157
			$this->jquery->exec("$('#modal-frmEdit').modal('show');", true);
158
			$form=$form->asModal(\get_class($instance));
159
			$form->setActions([ "Okay","Cancel" ]);
160
			$btOkay=$form->getAction(0);
161
			$btOkay->addClass("green")->setValue("Validate modifications");
162
			$form->onHidden("$('#modal-frmEdit').remove();");
163
			echo $form->compile($this->jquery, $this->view);
164
			echo $this->jquery->compile($this->view);
165
		}
166
	}
167
	
168
	protected function _showModel($id=null) {
169
		$model=$this->model;
170
		$datas=$this->getInstances(1,$id);
171
		$this->formModal=($this->_getModelViewer()->isModal($datas,$model))? "modal" : "no";
172
		return $this->_getModelViewer()->getModelDataTable($datas, $model,$this->activePage);
173
	}
174
	
175
	/**
176
	 * Deletes an instance
177
	 * @param mixed $ids
178
	 */
179
	public function delete($ids) {
180
		if(URequest::isAjax()){
181
			$instance=$this->getModelInstance($ids);
182 View Code Duplication
			if (method_exists($instance, "__toString"))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
183
				$instanceString=$instance . "";
184
			else
185
				$instanceString=get_class($instance);
186
			if (sizeof($_POST) > 0) {
187
				try{
188
					if (DAO::remove($instance)) {
0 ignored issues
show
Bug introduced by
It seems like $instance defined by $this->getModelInstance($ids) on line 181 can also be of type null; however, Ubiquity\orm\traits\DAOUpdatesTrait::remove() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
189
						$message=new CRUDMessage("Deletion of `<b>" . $instanceString . "</b>`","Deletion","info","info circle",4000);
190
						$message=$this->_getEvents()->onSuccessDeleteMessage($message);
191
						$this->jquery->exec("$('._element[data-ajax={$ids}]').remove();", true);
192
					} else {
193
						$message=new CRUDMessage("Can not delete `" . $instanceString . "`","Deletion","warning","warning circle");
194
						$message=$this->_getEvents()->onErrorDeleteMessage($message);
195
					}
196
				}catch (\Exception $e){
197
					$message=new CRUDMessage("Exception : can not delete `" . $instanceString . "`","Exception", "warning", "warning");
198
					$message=$this->_getEvents()->onErrorDeleteMessage($message);
199
				}
200
				$message=$this->_showSimpleMessage($message);
201
			} else {
202
				$message=new CRUDMessage("Do you confirm the deletion of `<b>" . $instanceString . "</b>`?", "Remove confirmation","error");
203
				$this->_getEvents()->onConfDeleteMessage($message);
0 ignored issues
show
Unused Code introduced by
The call to the method Ubiquity\controllers\cru...::onConfDeleteMessage() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
204
				$message=$this->_showConfMessage($message, $this->_getBaseRoute() . "/delete/{$ids}", "#table-messages", $ids);
205
			}
206
			echo $message;
207
			echo $this->jquery->compile($this->view);
208
		}else{
209
			$this->jquery->execAtLast("$('._delete[data-ajax={$ids}]').trigger('click');");
210
			$this->index();
211
		}
212
	}
213
	
214
	/**
215
	 * Helper to delete multiple objects
216
	 * @param mixed $data
217
	 * @param string $action
218
	 * @param string $target the css selector for refreshing
219
	 * @param callable|string $condition the callback for generating the SQL where (for deletion) with the parameter data, or a simple string
220
	 */
221
	protected function _deleteMultiple($data,$action,$target,$condition){
222
		if(URequest::isPost()){
223
			if(is_callable($condition)){
224
				$condition=$condition($data);
225
			}
226
			$rep=DAO::deleteAll($this->model, $condition);
227
			if($rep){
228
				$message=new CRUDMessage("Deleting {count} objects","Deletion","info","info circle",4000);
229
				$message=$this->_getEvents()->onSuccessDeleteMultipleMessage($message);
230
				$message->parseContent(["count"=>$rep]);
231
			}
232
			$this->_showSimpleMessage($message,"delete-all");
0 ignored issues
show
Bug introduced by
The variable $message does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
233
			$this->index();
234
		}else{
235
			$message=new CRUDMessage("Do you confirm the deletion of this objects?", "Remove confirmation","error");
236
			$this->_getEvents()->onConfDeleteMultipleMessage($message,$data);
1 ignored issue
show
Unused Code introduced by
The call to the method Ubiquity\controllers\cru...DeleteMultipleMessage() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
237
			$message=$this->_showConfMessage($message, $this->_getBaseRoute() . "/{$action}/{$data}",$target, $data,["jqueryDone"=>"replaceWith"]);
238
			echo $message;
239
			echo $this->jquery->compile($this->view);
240
		}
241
	}
242
	
243
244
	
245
	public function refreshTable($id=null) {
246
		$compo= $this->_showModel($id);
247
		$this->jquery->execAtLast('$("#table-details").html("");');
248
		$this->jquery->renderView("@framework/main/component.html",["compo"=>$compo]);	
249
	}
250
	
251
	/**
252
	 * Updates an instance from the data posted in a form
253
	 */
254
	public function update() {
255
		$message=new CRUDMessage("Modifications were successfully saved", "Updating");
256
		$instance=@$_SESSION["instance"];
257
		$isNew=$instance->_new;
258
		try{
259
			$updated=CRUDHelper::update($instance, $_POST,$this->_getAdminData()->getUpdateManyToOneInForm(),$this->_getAdminData()->getUpdateManyToManyInForm());
260
			if($updated){
261
				$message->setType("success")->setIcon("check circle outline");
262
				$message=$this->_getEvents()->onSuccessUpdateMessage($message);
263
				$this->refreshInstance($instance,$isNew);
264
			} else {
265
				$message->setMessage("An error has occurred. Can not save changes.")->setType("error")->setIcon("warning circle");
0 ignored issues
show
Bug introduced by
The method setType cannot be called on $message->setMessage('An...Can not save changes.') (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
266
				$message=$this->_getEvents()->onErrorUpdateMessage($message);
267
			}
268
		}catch(\Exception $e){
269 View Code Duplication
			if (method_exists($instance, "__toString"))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
270
				$instanceString=$instance . "";
271
			else
272
				$instanceString=get_class($instance);
273
			$message=new CRUDMessage("Exception : can not update `" . $instanceString . "`","Exception", "warning", "warning");
274
			$message=$this->_getEvents()->onErrorUpdateMessage($message);
275
		}
276
		echo $this->_showSimpleMessage($message,"updateMsg");
277
		echo $this->jquery->compile($this->view);
278
	}
279
	
280
	protected function refreshInstance($instance,$isNew){
281
		if($this->_getAdminData()->refreshPartialInstance() && !$isNew){
282
			$this->jquery->setJsonToElement(OrmUtils::objectAsJSON($instance));
283
		}else{
284
			$pk=OrmUtils::getFirstKeyValue($instance);
285
			$this->jquery->get($this->_getBaseRoute() . "/refreshTable/".$pk, "#lv", [ "jqueryDone" => "replaceWith" ]);
286
		}
287
	}
288
	
289
	/**
290
	 * Shows associated members with foreign keys
291
	 * @param mixed $ids
292
	 */
293
	public function showDetail($ids) {
294
		if(URequest::isAjax()){
295
			$instance=$this->getModelInstance($ids);
296
			$viewer=$this->_getModelViewer();
297
			$hasElements=false;
298
			$model=$this->model;
299
			$fkInstances=CRUDHelper::getFKIntances($instance, $model);
300
			$semantic=$this->jquery->semantic();
301
			$grid=$semantic->htmlGrid("detail");
302
			if (sizeof($fkInstances) > 0) {
303
				$wide=intval(16 / sizeof($fkInstances));
304
				if ($wide < 4)
305
					$wide=4;
306
					foreach ( $fkInstances as $member=>$fkInstanceArray ) {
307
						$element=$viewer->getFkMemberElementDetails($member,$fkInstanceArray["objectFK"],$fkInstanceArray["fkClass"],$fkInstanceArray["fkTable"]);
308
						if (isset($element)) {
309
							$grid->addCol($wide)->setContent($element);
310
							$hasElements=true;
311
						}
312
					}
313
					if ($hasElements)
314
						echo $grid;
315
						$url=$this->_getEvents()->onDetailClickURL($this->model);
316
					if(UString::isNotNull($url)){
317
						$this->detailClick($url);
318
					}
319
					echo $this->jquery->compile($this->view);
320
			}
321
		}else{
322
			$this->jquery->execAtLast("$('tr[data-ajax={$ids}]').trigger('click');");
323
			$this->index();
324
		}
325
326
	}
327
	
328
	public function detailClick($url) {
329
		$this->jquery->postOnClick(".showTable", $this->_getBaseRoute() . "/".$url,"{}", "#divTable", [ "attr" => "data-ajax","ajaxTransition" => "random" ]);
330
	}
331
	
332
	private function getModelInstance($ids) {
333
		$ids=\explode("_", $ids);
334
		$instance=DAO::getOne($this->model, $ids);
335
		if(isset($instance)){
336
			return $instance;
337
		}
338
		$message=new CRUDMessage("This object does not exist!","Get object","warning","warning circle");
339
		$message=$this->_getEvents()->onNotFoundMessage($message);
340
		echo $this->_showSimpleMessage($message);
341
		echo $this->jquery->compile($this->view);
342
		exit(1);
343
	}
344
	
345
	/**
346
	 * To override for defining a new adminData
347
	 * @return CRUDDatas
348
	 */
349
	protected function getAdminData ():CRUDDatas{
350
		return new CRUDDatas();
351
	}
352
	
353
	public function _getAdminData ():CRUDDatas{
354
		return $this->getSingleton($this->modelViewer,"getAdminData");
355
	}
356
	
357
	/**
358
	 * To override for defining a new ModelViewer
359
	 * @return ModelViewer
360
	 */
361
	protected function getModelViewer ():ModelViewer{
362
		return new ModelViewer($this);
363
	}
364
	
365
	private function _getModelViewer():ModelViewer{
366
		return $this->getSingleton($this->modelViewer,"getModelViewer");
367
	}
368
	
369
	/**
370
	 * To override for changing view files
371
	 * @return CRUDFiles
372
	 */
373
	protected function getFiles ():CRUDFiles{
374
		return new CRUDFiles();
375
	}
376
	
377
	private function _getFiles():CRUDFiles{
378
		return $this->getSingleton($this->crudFiles,"getFiles");
379
	}
380
	
381
	/**
382
	 * To override for changing events
383
	 * @return CRUDEvents
384
	 */
385
	protected function getEvents ():CRUDEvents{
386
		return new CRUDEvents($this);
387
	}
388
	
389
	private function _getEvents():CRUDEvents{
390
		return $this->getSingleton($this->events,"getEvents");
391
	}
392
	
393
	private function getSingleton($value, $method) {
394
		if (! isset ( $value )) {
395
			$value = $this->$method ();
396
		}
397
		return $value;
398
	}
399
	
400
	private function crudLoadView($viewName,$vars=[]){
401
		$this->_getEvents()->beforeLoadView($viewName,$vars);
402
		if(!URequest::isAjax()){
403
			$files=$this->_getFiles();
404
			$mainTemplate=$files->getBaseTemplate();
405
			if(isset($mainTemplate)){
406
				$vars["_viewname"]=$viewName;
407
				$vars["_base"]=$mainTemplate;
408
				$this->jquery->renderView($files->getViewBaseTemplate(),$vars);
409
			}else{
410
				$vars["hasScript"]=true;
411
				$this->jquery->renderView($viewName,$vars);
412
			}
413
		}else{
414
			$vars["hasScript"]=true;
415
			$this->jquery->renderView($viewName,$vars);
416
		}
417
	}
418
419
}
420