Test Failed
Push — master ( f3b9bd...3b5313 )
by Jean-Christophe
23:33
created

ModelViewer::oneToManyFormField()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
c 0
b 0
f 0
dl 0
loc 12
rs 9.9
cc 1
nc 1
nop 4
1
<?php
2
3
namespace Ubiquity\controllers\crud\viewers;
4
5
use Ajax\semantic\html\collections\form\HtmlFormField;
6
use Ajax\semantic\html\collections\form\HtmlFormInput;
7
use Ajax\semantic\html\elements\HtmlButton;
8
use Ajax\semantic\html\elements\HtmlHeader;
9
use Ajax\semantic\html\elements\HtmlIconGroups;
10
use Ajax\semantic\html\elements\HtmlLabel;
11
use Ajax\semantic\widgets\dataform\DataForm;
12
use Ajax\semantic\widgets\datatable\DataTable;
13
use Ajax\semantic\widgets\datatable\PositionInTable;
14
use Ajax\service\JArray;
15
use Ubiquity\controllers\crud\CRUDHelper;
16
use Ubiquity\controllers\crud\EditMemberParams;
17
use Ubiquity\orm\DAO;
18
use Ubiquity\orm\OrmUtils;
19
use Ubiquity\orm\parser\Reflexion;
20
use Ubiquity\utils\base\UString;
21
use Ajax\common\html\HtmlCollection;
22
use Ajax\common\html\HtmlDoubleElement;
23
use Ubiquity\controllers\crud\interfaces\HasModelViewerInterface;
24
25
/**
26
 * Associated with a CRUDController class
27
 * Responsible of the display
28
 *
29
 * @author jc
30
 *
31
 */
32
class ModelViewer {
33
34
	/**
35
	 *
36
	 * @var \Ajax\JsUtils
37
	 */
38
	private $jquery;
39
40
	/**
41
	 *
42
	 * @var HasModelViewerInterface
43
	 */
44
	protected $controller;
45
46
	public function __construct(HasModelViewerInterface $controller) {
47
		$this->jquery = $controller->jquery;
1 ignored issue
show
Bug introduced by
Accessing jquery on the interface Ubiquity\controllers\cru...HasModelViewerInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
48
		$this->controller = $controller;
49
	}
50
51
	/**
52
	 * Returns the form for adding or modifying an object
53
	 *
54
	 * @param string $identifier
55
	 * @param object $instance the object to add or modify
56
	 * @return \Ajax\semantic\widgets\dataform\DataForm
57
	 */
58
	public function getForm($identifier, $instance) {
59
		$form = $this->jquery->semantic ()->dataForm ( $identifier, $instance );
60
		$form->setLibraryId ( "frmEdit" );
61
		$className = \get_class ( $instance );
62
		$fields = $this->controller->_getAdminData ()->getFormFieldNames ( $className, $instance );
63
		$relFields = OrmUtils::getFieldsInRelations_ ( $className );
64
65
		$this->setFormFields_ ( $fields, $relFields );
66
		array_unshift ( $fields, "_message" );
67
		$form->setFields ( $fields );
68
69
		$fieldTypes = OrmUtils::getFieldTypes ( $className );
70
		$this->setFormFieldsComponent ( $form, $fieldTypes );
71
		$this->relationMembersInForm ( $form, $instance, $className, $fields, $relFields );
72
		OrmUtils::setFieldToMemberNames ( $fields, $relFields );
73
		$form->setCaptions ( $this->getFormCaptions ( $fields, $className, $instance ) );
74
		$message = $this->getFormTitle ( $form, $instance );
75
		$form->setCaption ( "_message", $message ["subMessage"] );
76
		$form->fieldAsMessage ( "_message", [ "icon" => $message ["icon"] ] );
77
		$instance->_message = $message ["message"];
78
		$form->setSubmitParams ( $this->controller->_getBaseRoute () . "/update", "#frm-add-update" );
79
		$form->onGenerateField ( [ $this,'onGenerateFormField' ] );
80
		return $form;
81
	}
82
83
	/**
84
	 * Returns a form for member editing
85
	 *
86
	 * @param string $identifier
87
	 * @param object $instance
88
	 * @param string $member
89
	 * @param string $td
90
	 * @param string $part
91
	 * @return \Ajax\semantic\widgets\dataform\DataForm
92
	 */
93
	public function getMemberForm($identifier, $instance, $member, $td, $part) {
94
		$editMemberParams = $this->getEditMemberParams_ ( $part );
95
96
		$form = $this->jquery->semantic ()->dataForm ( $identifier, $instance );
97
		$form->on ( "dblclick", "", true, true );
98
		$form->setProperty ( "onsubmit", "return false;" );
99
		$form->addClass ( "_memberForm" );
100
		$className = \get_class ( $instance );
101
		$fields = [ "id",$member ];
102
		$relFields = OrmUtils::getFieldsInRelations_ ( $className );
103
		$hasRelations = $this->setFormFields_ ( $fields, $relFields );
104
		$form->setFields ( $fields );
105
		$fieldTypes = OrmUtils::getFieldTypes ( $className );
106
		$form->fieldAsHidden ( 0 );
107
		$this->setMemberFormFieldsComponent ( $form, $fieldTypes );
108
		if ($hasRelations) {
109
			$this->relationMembersInForm ( $form, $instance, $className, $fields, $relFields );
110
		}
111
		$form->setCaptions ( [ "","" ] );
112
		$form->onGenerateField ( function (HtmlFormField $f, $nb) use ($identifier, $editMemberParams) {
113
			if ($nb == 1) {
114
				$f->setSize ( "mini" );
115
				if ($editMemberParams->getHasButtons ()) {
116
					$btO = HtmlButton::icon ( "btO", "check" )->addClass ( "green mini compact" )->onClick ( "\$('#" . $identifier . "').trigger('validate');", true, true );
117
					$btC = HtmlButton::icon ( "btC", "close" )->addClass ( "mini compact" )->onClick ( "\$('#" . $identifier . "').trigger('endEdit');" );
118
					$f->wrap ( "<div class='fields' style='margin:0;'>", [ $btO,$btC,"</div>" ] );
119
					if (! $editMemberParams->getHasPopup ()) {
120
						$f->setWidth ( 16 )->setProperty ( "style", "padding-left:0;" );
121
					}
122
				}
123
				$f->on ( "keydown", "if(event.which == 13) {\$('#" . $identifier . "').trigger('validate');}if(event.keyCode===27) {\$('#" . $identifier . "').trigger('endEdit');}" );
124
				$f->onClick ( "return false;", true, true );
125
			} else {
126
				$f->setProperty ( "style", "display: none;" );
127
			}
128
		} );
129
		$form->setSubmitParams ( $this->controller->_getBaseRoute () . "/updateMember/" . $member . "/" . $editMemberParams->getUpdateCallback (), "#" . $td, [ "attr" => "","hasLoader" => false,"jsCallback" => "$(self).remove();","jqueryDone" => "html" ] );
130
		if ($editMemberParams->getHasPopup ()) {
131
			$endEdit = "\$('#" . $identifier . "').html();\$('.popup').hide();\$('#" . $td . "').popup('destroy');";
132
			$validate = $endEdit;
133
		} else {
134
			$endEdit = "let td=\$('#" . $td . "');td.html(td.data('originalText'));";
135
			$validate = "";
136
		}
137
		$form->on ( "endEdit", $endEdit );
138
		$form->on ( "validate", "\$('#" . $identifier . "').form('submit');" . $validate );
139
		$this->jquery->execAtLast ( "$('form').find('input[type=text],textarea,select').filter(':visible:first').focus();" );
140
		return $form;
141
	}
142
143
	private function setFormFields_(&$fields, $relFields) {
144
		$hasRelations = false;
145
		$relFields = array_flip ( $relFields );
146
		foreach ( $fields as $index => $field ) {
147
			if (isset ( $relFields [$field] )) {
148
				$fields [$index] = $relFields [$field];
149
				$hasRelations = true;
150
			}
151
		}
152
		return $hasRelations;
153
	}
154
155
	/**
156
	 * Returns an associative array defining form message title with keys "icon","message","subMessage"
157
	 *
158
	 * @param DataForm $form
159
	 * @param object $instance
160
	 * @return array the message title
161
	 */
162
	protected function getFormTitle($form, $instance) {
163
		$type = ($instance->_new) ? "new" : "edit";
164
		$messageInfos = [ "new" => [ "icon" => HtmlIconGroups::corner ( "table", "plus", "big" ),"subMessage" => "New object creation" ],"edit" => [ "icon" => HtmlIconGroups::corner ( "table", "edit", "big" ),"subMessage" => "Editing an existing object" ] ];
165
		$message = $messageInfos [$type];
166
		$message ["message"] = \get_class ( $instance );
167
		return $message;
168
	}
169
170
	/**
171
	 * Sets the components for each field
172
	 *
173
	 * @param DataForm $form
174
	 * @param array $fieldTypes associative array of field names (keys) and types (values)
175
	 */
176
	public function setFormFieldsComponent(DataForm $form, $fieldTypes) {
177
		$this->setFormFieldsComponent_ ( $form, $fieldTypes );
178
	}
179
180
	/**
181
	 * Sets the components for each field
182
	 *
183
	 * @param DataForm $form
184
	 * @param array $fieldTypes associative array of field names (keys) and types (values)
185
	 */
186
	public function setMemberFormFieldsComponent(DataForm $form, $fieldTypes) {
187
		$this->setFormFieldsComponent_ ( $form, $fieldTypes );
188
	}
189
190
	private function setFormFieldsComponent_(DataForm $form, $fieldTypes) {
191
		foreach ( $fieldTypes as $property => $type ) {
192
			switch ($property) {
193
				case "password" :
194
					$form->fieldAsInput ( $property, [ "inputType" => "password" ] );
195
					$form->setValidationParams ( [ "inline" => true ] );
196
					break;
197
				case "email" :
198
				case "mail" :
199
					$form->fieldAsInput ( $property, [ "inputType" => "email","rules" => [ [ "email" ] ] ] );
200
					break;
201
			}
202
203
			switch ($type) {
204
				case "tinyint(1)" :
205
					$form->fieldAsCheckbox ( $property );
206
					break;
207
				case "int" :
208
				case "integer" :
209
					$form->fieldAsInput ( $property, [ "inputType" => "number" ] );
210
					break;
211
				case "date" :
212
					$form->fieldAsInput ( $property, [ "inputType" => "date" ] );
213
					break;
214
				case "datetime" :
215
					$form->fieldAsInput ( $property, [ "inputType" => "datetime-local" ] );
216
					break;
217
			}
218
		}
219
	}
220
221
	/**
222
	 * Returns a DataElement object for displaying the instance
223
	 * Used in the display method of the CrudController
224
	 * in display route
225
	 *
226
	 * @param object $instance
227
	 * @param string $model The model class name (long name)
228
	 * @param boolean $modal
229
	 * @return \Ajax\semantic\widgets\dataelement\DataElement
230
	 */
231
	public function getModelDataElement($instance, $model, $modal) {
232
		$semantic = $this->jquery->semantic ();
233
		$fields = $this->controller->_getAdminData ()->getElementFieldNames ( $model );
234
235
		$dataElement = $semantic->dataElement ( "de", $instance );
236
		$pk = OrmUtils::getFirstKeyValue ( $instance );
237
		$dataElement->getInstanceViewer ()->setIdentifierFunction ( function () use ($pk) {
238
			return $pk;
239
		} );
240
		$dataElement->setFields ( $fields );
241
		$dataElement->setCaptions ( $this->getElementCaptions ( $fields, $model, $instance ) );
242
243
		$fkInstances = CRUDHelper::getFKIntances ( $instance, $model );
244
		foreach ( $fkInstances as $member => $fkInstanceArray ) {
245
			if (array_search ( $member, $fields ) !== false) {
246
				$dataElement->setValueFunction ( $member, function () use ($fkInstanceArray, $member) {
247
					return $this->getFkMemberElement ( $member, $fkInstanceArray ["objectFK"], $fkInstanceArray ["fkClass"], $fkInstanceArray ["fkTable"] );
248
				} );
249
			}
250
		}
251
		$this->addEditMemberFonctionality ( "dataElement" );
252
		return $dataElement;
253
	}
254
255
	/**
256
	 * Returns the captions for DataElement fields
257
	 * in display route
258
	 *
259
	 * @param array $captions
260
	 * @param string $className
261
	 */
262
	public function getElementCaptions($captions, $className, $instance) {
263
		return array_map ( "ucfirst", $captions );
264
	}
265
266
	/**
267
	 * Returns the dataTable responsible for displaying instances of the model
268
	 *
269
	 * @param array $instances objects to display
270
	 * @param string $model model class name (long name)
271
	 * @return DataTable
272
	 */
273
	public function getModelDataTable($instances, $model, $totalCount, $page = 1) {
274
		$adminRoute = $this->controller->_getBaseRoute ();
275
		$files = $this->controller->_getFiles ();
276
		$dataTable = $this->getDataTableInstance ( $instances, $model, $totalCount, $page );
277
		$attributes = $this->controller->_getAdminData ()->getFieldNames ( $model );
278
		$this->setDataTableAttributes ( $dataTable, $attributes, $model, $instances );
279
		$dataTable->setCaptions ( $this->getCaptions ( $attributes, $model ) );
280
281
		$dataTable->addClass ( "small very compact" );
282
		$lbl = new HtmlLabel ( "search-query", "<span id='search-query-content'></span>" );
283
		$icon = $lbl->addIcon ( "delete", false );
284
		$lbl->wrap ( "<span>", "</span>" );
285
		$lbl->setProperty ( "style", "display: none;" );
286
		$icon->getOnClick ( $adminRoute . $files->getRouteRefreshTable (), "#lv", [ "jqueryDone" => "replaceWith","hasLoader" => "internal" ] );
287
288
		$dataTable->addItemInToolbar ( $lbl );
289
		$dataTable->addSearchInToolbar ();
290
		$dataTable->setToolbarPosition ( PositionInTable::FOOTER );
291
		$dataTable->getToolbar ()->setSecondary ();
292
		return $dataTable;
293
	}
294
295
	public function setDataTableAttributes(DataTable $dataTable, $attributes, $model, $instances, $selector = null) {
296
		$modal = ($this->isModal ( $instances, $model ) ? "modal" : "no");
297
298
		$adminRoute = $this->controller->_getBaseRoute ();
299
		$files = $this->controller->_getFiles ();
300
		$dataTable->setButtons ( $this->getDataTableRowButtons () );
301
		$dataTable->setFields ( $attributes );
302
		if (array_search ( "password", $attributes ) !== false) {
303
			$dataTable->setValueFunction ( "password", function ($v) {
304
				return UString::mask ( $v );
305
			} );
306
		}
307
		$dataTable->setIdentifierFunction ( CRUDHelper::getIdentifierFunction ( $model ) );
308
309
		if (! isset ( $selector )) {
310
			if ($this->showDetailsOnDataTableClick ()) {
311
				$dataTable->getOnRow ( "click", $adminRoute . $files->getRouteDetails (), "#table-details", [ "selector" => $selector,"attr" => "data-ajax","hasLoader" => false,"jsCondition" => "!event.detail || event.detail==1","jsCallback" => "return false;" ] );
312
				$dataTable->setActiveRowSelector ( "active" );
313
			}
314
315
			$dataTable->setUrls ( [ "refresh" => $adminRoute . $files->getRouteRefresh (),"delete" => $adminRoute . $files->getRouteDelete (),"edit" => $adminRoute . $files->getRouteEdit () . "/" . $modal,"display" => $adminRoute . $files->getRouteDisplay () . "/" . $modal ] );
316
			$dataTable->setTargetSelector ( [ "delete" => "#table-messages","edit" => "#frm-add-update","display" => "#table-details" ] );
317
			$this->addEditMemberFonctionality ( "dataTable" );
318
		}
319
		$this->addAllButtons ( $dataTable, $attributes );
320
	}
321
322
	public function addEditMemberFonctionality($part) {
323
		if (($editMemberParams = $this->getEditMemberParams ()) !== false) {
1 ignored issue
show
introduced by
The condition $editMemberParams = $thi...emberParams() !== false is always true.
Loading history...
324
			if (isset ( $editMemberParams [$part] )) {
325
				$params = $editMemberParams [$part];
326
				$params->compile ( $this->controller->_getBaseRoute (), $this->jquery, $part );
327
			}
328
		}
329
	}
330
331
	/**
332
	 *
333
	 * @param string $model The model class name (long name)
334
	 * @param number $totalCount The total count of objects
335
	 * @return void|number default : 6
336
	 */
337
	public function recordsPerPage($model, $totalCount = 0) {
338
		if ($totalCount > 6)
339
			return 6;
340
		return;
341
	}
342
343
	/**
344
	 * Returns the fields on which a grouping is performed
345
	 */
346
	public function getGroupByFields() {
347
		return;
348
	}
349
350
	/**
351
	 * For doing something when $field is generated in form
352
	 *
353
	 * @param mixed $field
354
	 */
355
	public function onGenerateFormField($field, $nb) {
356
		if ($field instanceof HtmlFormInput) {
357
			if ($field->getDataField ()->getProperty ( 'type' ) == "datetime-local") {
358
				$v = $field->getDataField ()->getProperty ( 'value' );
359
				$field->getDataField ()->setValue ( date ( "Y-m-d\TH:i:s", strtotime ( $v ) ) );
360
			}
361
		}
362
		return;
363
	}
364
365
	/**
366
	 * Returns the dataTable instance for dispaying a list of object
367
	 *
368
	 * @param array $instances
369
	 * @param string $model
370
	 * @param number $totalCount
371
	 * @param number $page
372
	 * @return DataTable
373
	 */
374
	protected function getDataTableInstance($instances, $model, $totalCount, $page = 1): DataTable {
375
		$semantic = $this->jquery->semantic ();
376
		$recordsPerPage = $this->recordsPerPage ( $model, $totalCount );
377
		if (is_numeric ( $recordsPerPage )) {
1 ignored issue
show
introduced by
The condition is_numeric($recordsPerPage) is always true.
Loading history...
378
			$grpByFields = $this->getGroupByFields ();
1 ignored issue
show
Bug introduced by
Are you sure the assignment to $grpByFields is correct as $this->getGroupByFields() targeting Ubiquity\controllers\cru...wer::getGroupByFields() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
379
			if (is_array ( $grpByFields )) {
1 ignored issue
show
introduced by
The condition is_array($grpByFields) is always false.
Loading history...
380
				$dataTable = $semantic->dataTable ( "lv", $model, $instances );
381
				$dataTable->setGroupByFields ( $grpByFields );
382
			} else {
383
				$dataTable = $semantic->jsonDataTable ( "lv", $model, $instances );
384
			}
385
			$dataTable->paginate ( $page, $totalCount, $recordsPerPage, 5 );
386
			$dataTable->onActiveRowChange ( '$("#table-details").html("");' );
387
			$dataTable->onSearchTerminate ( '$("#search-query-content").html(data);$("#search-query").show();$("#table-details").html("");' );
388
		} else {
389
			$dataTable = $semantic->dataTable ( "lv", $model, $instances );
390
		}
391
		return $dataTable;
392
	}
393
394
	/**
395
	 * Returns an array of buttons ["display","edit","delete"] to display for each row in dataTable
396
	 *
397
	 * @return string[]
398
	 */
399
	protected function getDataTableRowButtons() {
400
		return [ "edit","delete" ];
401
	}
402
403
	public function addAllButtons(DataTable $dataTable, $attributes) {
404
		$dataTable->onPreCompile ( function () use (&$dataTable) {
405
			$dataTable->getHtmlComponent ()->colRightFromRight ( 0 );
406
		} );
407
		$dataTable->addAllButtons ( false, [ "ajaxTransition" => "random" ], function ($bt) {
408
			$bt->addClass ( "circular" );
409
			$this->onDataTableRowButton ( $bt );
410
		}, function ($bt) {
411
			$bt->addClass ( "circular" );
412
			$this->onDataTableRowButton ( $bt );
413
		}, function ($bt) {
414
			$bt->addClass ( "circular" );
415
			$this->onDataTableRowButton ( $bt );
416
		} );
417
		$dataTable->setDisplayBehavior ( [ "jsCallback" => '$("#dataTable").hide();',"ajaxTransition" => "random" ] );
418
	}
419
420
	/**
421
	 * To override for modifying the dataTable row buttons
422
	 *
423
	 * @param HtmlButton $bt
424
	 */
425
	public function onDataTableRowButton(HtmlButton $bt) {
426
	}
427
428
	/**
429
	 * To override for modifying the showConfMessage dialog buttons
430
	 *
431
	 * @param HtmlButton $confirmBtn The confirmation button
432
	 * @param HtmlButton $cancelBtn The cancellation button
433
	 */
434
	public function onConfirmButtons(HtmlButton $confirmBtn, HtmlButton $cancelBtn) {
435
	}
436
437
	/**
438
	 * Condition to determine if the edit or add form is modal for $model objects
439
	 *
440
	 * @param array $objects
441
	 * @param string $model
442
	 * @return boolean
443
	 */
444
	public function isModal($objects, $model) {
445
		return \count ( $objects ) > 5;
446
	}
447
448
	/**
449
	 * Returns the captions for list fields in showTable action
450
	 *
451
	 * @param array $captions
452
	 * @param string $className
453
	 */
454
	public function getCaptions($captions, $className) {
455
		return array_map ( "ucfirst", $captions );
456
	}
457
458
	/**
459
	 * Returns the captions for form fields
460
	 *
461
	 * @param array $captions
462
	 * @param string $className
463
	 */
464
	public function getFormCaptions($captions, $className, $instance) {
465
		return array_map ( "ucfirst", $captions );
466
	}
467
468
	/**
469
	 * Returns the header for a single foreign object (element is an instance, issue from ManyToOne), (from DataTable)
470
	 *
471
	 * @param string $member
472
	 * @param string $className
473
	 * @param object $object
474
	 * @return HtmlHeader
475
	 */
476
	public function getFkHeaderElementDetails($member, $className, $object) {
477
		return new HtmlHeader ( "", 4, $member, "content" );
478
	}
479
480
	/**
481
	 * Returns the header for a list of foreign objects (issue from oneToMany or ManyToMany), (from DataTable)
482
	 *
483
	 * @param string $member
484
	 * @param string $className
485
	 * @param array $list
486
	 * @return HtmlHeader
487
	 */
488
	public function getFkHeaderListDetails($member, $className, $list) {
489
		return new HtmlHeader ( "", 4, $member . " (" . \count ( $list ) . ")", "content" );
490
	}
491
492
	/**
493
	 * Returns a component for displaying a single foreign object (manyToOne relation), (from DataTable)
494
	 *
495
	 * @param string $member
496
	 * @param string $className
497
	 * @param object $object
498
	 * @return \Ajax\common\html\BaseHtml
499
	 */
500
	public function getFkElementDetails($member, $className, $object) {
501
		return $this->jquery->semantic ()->htmlLabel ( "element-" . $className . "." . $member, $object . "" );
1 ignored issue
show
Bug introduced by
Are you sure $object of type object can be used in concatenation? ( Ignorable by Annotation )

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

501
		return $this->jquery->semantic ()->htmlLabel ( "element-" . $className . "." . $member, /** @scrutinizer ignore-type */ $object . "" );
Loading history...
502
	}
503
504
	/**
505
	 * Returns a list component for displaying a collection of foreign objects (*ToMany relations), (from DataTable)
506
	 *
507
	 * @param string $member
508
	 * @param string $className
509
	 * @param array|\Traversable $list
510
	 * @return HtmlCollection
511
	 */
512
	public function getFkListDetails($member, $className, $list) {
513
		$element = $this->jquery->semantic ()->htmlList ( "list-" . $className . "." . $member );
514
		$element->setMaxVisible ( 15 );
515
516
		return $element->addClass ( "animated divided celled" );
517
	}
518
519
	/**
520
	 * Returns a component for displaying a foreign object (from DataTable)
521
	 *
522
	 * @param string $memberFK
523
	 * @param mixed $objectFK
524
	 * @param string $fkClass
525
	 * @param string $fkTable
526
	 * @return string|NULL
527
	 */
528
	public function getFkMemberElementDetails($memberFK, $objectFK, $fkClass, $fkTable) {
529
		$_fkClass = str_replace ( "\\", ".", $fkClass );
530
		$header = new HtmlHeader ( "", 4, $memberFK, "content" );
531
		if (is_array ( $objectFK ) || $objectFK instanceof \Traversable) {
532
			$header = $this->getFkHeaderListDetails ( $memberFK, $fkClass, $objectFK );
1 ignored issue
show
Bug introduced by
It seems like $objectFK can also be of type Traversable; however, parameter $list of Ubiquity\controllers\cru...etFkHeaderListDetails() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

532
			$header = $this->getFkHeaderListDetails ( $memberFK, $fkClass, /** @scrutinizer ignore-type */ $objectFK );
Loading history...
533
			$element = $this->getFkListDetails ( $memberFK, $fkClass, $objectFK );
534
			foreach ( $objectFK as $oItem ) {
535
				if (method_exists ( $oItem, "__toString" )) {
536
					$id = (CRUDHelper::getIdentifierFunction ( $fkClass )) ( 0, $oItem );
537
					$item = $element->addItem ( $oItem . "" );
1 ignored issue
show
Bug introduced by
Are you sure $oItem of type object can be used in concatenation? ( Ignorable by Annotation )

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

537
					$item = $element->addItem ( /** @scrutinizer ignore-type */ $oItem . "" );
Loading history...
538
					$item->setProperty ( "data-ajax", $_fkClass . "||" . $id );
539
					$item->addClass ( "showTable" );
540
					$this->onDisplayFkElementListDetails ( $item, $memberFK, $fkClass, $oItem );
541
				}
542
			}
543
		} else {
544
			if (method_exists ( $objectFK, "__toString" )) {
545
				$header = $this->getFkHeaderElementDetails ( $memberFK, $fkClass, $objectFK );
546
				$id = (CRUDHelper::getIdentifierFunction ( $fkClass )) ( 0, $objectFK );
547
				$element = $this->getFkElementDetails ( $memberFK, $fkClass, $objectFK );
548
				$element->setProperty ( "data-ajax", $_fkClass . "||" . $id )->addClass ( "showTable" );
549
			}
550
		}
551
		if (isset ( $element )) {
552
			return [ $header,$element ];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array($header, $element) returns the type array<integer,Ajax\seman...ments\HtmlHeader|mixed> which is incompatible with the documented return type null|string.
Loading history...
553
		}
554
		return null;
555
	}
556
557
	/**
558
	 * To modify for displaying an element in a list component of foreign objects (from DataTable)
559
	 *
560
	 * @param HtmlDoubleElement $element
561
	 * @param string $member
562
	 * @param string $className
563
	 * @param object $object
564
	 */
565
	public function onDisplayFkElementListDetails($element, $member, $className, $object) {
566
	}
567
568
	/**
569
	 * Returns a component for displaying a foreign object (from DataElement)
570
	 *
571
	 * @param string $memberFK
572
	 * @param mixed $objectFK
573
	 * @param string $fkClass
574
	 * @param string $fkTable
575
	 * @return string|NULL
576
	 */
577
	public function getFkMemberElement($memberFK, $objectFK, $fkClass, $fkTable) {
578
		$element = "";
579
		$_fkClass = str_replace ( "\\", ".", $fkClass );
580
		if (is_array ( $objectFK ) || $objectFK instanceof \Traversable) {
581
			$element = $this->getFkList ( $memberFK, $objectFK );
582
			foreach ( $objectFK as $oItem ) {
583
				if (method_exists ( $oItem, "__toString" )) {
584
					$id = (CRUDHelper::getIdentifierFunction ( $fkClass )) ( 0, $oItem );
585
					$item = $element->addItem ( $oItem . "" );
1 ignored issue
show
Bug introduced by
Are you sure $oItem of type object can be used in concatenation? ( Ignorable by Annotation )

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

585
					$item = $element->addItem ( /** @scrutinizer ignore-type */ $oItem . "" );
Loading history...
586
					$item->setProperty ( "data-ajax", $_fkClass . "||" . $id );
587
					$item->addClass ( "showTable" );
588
					$this->displayFkElementList ( $item, $memberFK, $fkClass, $oItem );
589
				}
590
			}
591
		} else {
592
			if (method_exists ( $objectFK, "__toString" )) {
593
				$id = (CRUDHelper::getIdentifierFunction ( $fkClass )) ( 0, $objectFK );
594
				$element = $this->getFkElement ( $memberFK, $fkClass, $objectFK );
595
				$element->setProperty ( "data-ajax", $_fkClass . "||" . $id )->addClass ( "showTable" );
596
			}
597
		}
598
		return $element;
599
	}
600
601
	/**
602
	 * Returns a list component for displaying a collection of foreign objects (*ToMany relations), (from DataElement)
603
	 *
604
	 * @param string $member
605
	 * @param string $className
606
	 * @param array|\Traversable $list
607
	 * @return HtmlCollection
608
	 */
609
	public function getFkList($member, $list) {
610
		$element = $this->jquery->semantic ()->htmlList ( "list-" . $member );
611
		$element->setMaxVisible ( 10 );
612
		return $element->addClass ( "animated" );
613
	}
614
615
	/**
616
	 * To modify for displaying an element in a list component of foreign objects, (from DataElement)
617
	 *
618
	 * @param HtmlDoubleElement $element
619
	 * @param string $member
620
	 * @param string $className
621
	 * @param object $object
622
	 */
623
	public function displayFkElementList($element, $member, $className, $object) {
624
	}
625
626
	/**
627
	 * Returns a component for displaying a single foreign object (manyToOne relation), (from DataElement)
628
	 *
629
	 * @param string $member
630
	 * @param string $className
631
	 * @param object $object
632
	 * @return \Ajax\common\html\BaseHtml
633
	 */
634
	public function getFkElement($member, $className, $object) {
635
		return $this->jquery->semantic ()->htmlLabel ( "element-" . $className . "." . $member, $object . "" );
1 ignored issue
show
Bug introduced by
Are you sure $object of type object can be used in concatenation? ( Ignorable by Annotation )

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

635
		return $this->jquery->semantic ()->htmlLabel ( "element-" . $className . "." . $member, /** @scrutinizer ignore-type */ $object . "" );
Loading history...
636
	}
637
638
	/**
639
	 * To override to make sure that the detail of a clicked object is displayed or not
640
	 *
641
	 * @return boolean Return true if you want to see details
642
	 */
643
	public function showDetailsOnDataTableClick() {
644
		return true;
645
	}
646
647
	protected function relationMembersInForm($form, $instance, $className, $fields, $relations) {
648
		foreach ( $relations as $field => $member ) {
649
			if (array_search ( $field, $fields ) !== false) {
650
				if (OrmUtils::getAnnotationInfoMember ( $className, "#manyToOne", $member ) !== false) {
651
					$this->manyToOneFormField ( $form, $member, $className, $instance );
652
				} elseif (($annot = OrmUtils::getAnnotationInfoMember ( $className, "#oneToMany", $member )) !== false) {
653
					$this->oneToManyFormField ( $form, $member, $instance, $annot );
654
				} elseif (($annot = OrmUtils::getAnnotationInfoMember ( $className, "#manyToMany", $member )) !== false) {
655
					$this->manyToManyFormField ( $form, $member, $instance, $annot );
656
				}
657
			}
658
		}
659
	}
660
661
	protected function manyToOneFormField(DataForm $form, $member, $className, $instance) {
662
		$joinColumn = OrmUtils::getAnnotationInfoMember ( $className, "#joinColumn", $member );
663
		if ($joinColumn) {
664
			$fkObject = Reflexion::getMemberValue ( $instance, $member );
665
			$fkClass = $joinColumn ["className"];
666
			if ($fkObject === null) {
667
				$fkObject = new $fkClass ();
668
			}
669
			$fkId = OrmUtils::getFirstKey ( $fkClass );
670
			$fkIdGetter = "get" . \ucfirst ( $fkId );
671
			if (\method_exists ( $fkObject, "__toString" ) && \method_exists ( $fkObject, $fkIdGetter )) {
672
				$fkField = $joinColumn ["name"];
673
				$fkValue = OrmUtils::getFirstKeyValue ( $fkObject );
674
				if (! Reflexion::setMemberValue ( $instance, $fkField, $fkValue )) {
675
					$instance->{$fkField} = OrmUtils::getFirstKeyValue ( $fkObject );
676
				}
677
				$form->fieldAsDropDown ( $fkField, JArray::modelArray ( $this->controller->_getAdminData ()->getManyToOneDatas ( $fkClass, $instance, $member ), $fkIdGetter, "__toString" ) );
678
				$form->setCaption ( $fkField, \ucfirst ( $member ) );
679
			}
680
		}
681
	}
682
683
	protected function oneToManyFormField(DataForm $form, $member, $instance, $annot) {
684
		$newField = $member . "Ids";
685
		$fkClass = $annot ["className"];
686
		$fkId = OrmUtils::getFirstKey ( $fkClass );
687
		$fkIdGetter = "get" . \ucfirst ( $fkId );
688
		$fkInstances = DAO::getOneToMany ( $instance, $member );
689
		$ids = \array_map ( function ($elm) use ($fkIdGetter) {
690
			return $elm->{$fkIdGetter} ();
691
		}, $fkInstances );
692
		$instance->{$newField} = \implode ( ",", $ids );
693
		$form->fieldAsDropDown ( $newField, JArray::modelArray ( $this->controller->_getAdminData ()->getOneToManyDatas ( $fkClass, $instance, $member ), $fkIdGetter, "__toString" ), true );
694
		$form->setCaption ( $newField, \ucfirst ( $member ) );
695
	}
696
697
	protected function manyToManyFormField(DataForm $form, $member, $instance, $annot) {
698
		$newField = $member . "Ids";
699
		$fkClass = $annot ["targetEntity"];
700
		$fkId = OrmUtils::getFirstKey ( $fkClass );
701
		$fkIdGetter = "get" . \ucfirst ( $fkId );
702
		$fkInstances = DAO::getManyToMany ( $instance, $member );
703
		$ids = \array_map ( function ($elm) use ($fkIdGetter) {
704
			return $elm->{$fkIdGetter} ();
705
		}, $fkInstances );
706
		$instance->{$newField} = \implode ( ",", $ids );
707
		$form->fieldAsDropDown ( $newField, JArray::modelArray ( $this->controller->_getAdminData ()->getManyToManyDatas ( $fkClass, $instance, $member ), $fkIdGetter, "__toString" ), true, [ "jsCallback" => function ($elm) {
708
			$elm->getField ()->asSearch ();
709
		} ] );
710
		$form->setCaption ( $newField, \ucfirst ( $member ) );
711
	}
712
713
	/**
714
	 *
715
	 * @return \Ubiquity\controllers\crud\EditMemberParams[]
716
	 */
717
	public function getEditMemberParams() {
718
		return $this->defaultEditMemberParams ();
719
	}
720
721
	/**
722
	 *
723
	 * @param string $part
724
	 * @return \Ubiquity\controllers\crud\EditMemberParams
725
	 */
726
	protected function getEditMemberParams_($part) {
727
		$params = $this->getEditMemberParams ();
728
		if ($params && isset ( $params [$part] )) {
1 ignored issue
show
Bug Best Practice introduced by
The expression $params of type array<string,Ubiquity\co...\crud\EditMemberParams> is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
729
			return $params [$part];
730
		}
731
		return new EditMemberParams ();
732
	}
733
734
	/**
735
	 *
736
	 * @return \Ubiquity\controllers\crud\EditMemberParams[]
737
	 */
738
	protected function defaultEditMemberParams() {
739
		return [ "dataTable" => EditMemberParams::dataTable (),"dataElement" => EditMemberParams::dataElement () ];
740
	}
741
}
742
743