RTableNested   F
last analyzed

Complexity

Total Complexity 98

Size/Duplication

Total Lines 878
Duplicated Lines 0 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
wmc 98
eloc 232
c 4
b 1
f 0
dl 0
loc 878
rs 2

23 Methods

Rating   Name   Duplication   Size   Complexity  
A hasPrimaryKey() 0 15 5
A afterStore() 0 18 4
B __construct() 0 29 9
A afterCheck() 0 19 4
A beforeLoad() 0 18 4
B publish() 0 81 10
A beforeDelete() 0 18 4
A beforeCheck() 0 18 4
A beforeStore() 0 24 5
A checkIn() 0 33 5
A importPluginTypes() 0 5 2
A load() 0 21 4
A afterDelete() 0 19 4
A getOption() 0 8 2
A setOption() 0 5 1
A afterLoad() 0 18 4
A delete() 0 21 4
A check() 0 21 4
B bind() 0 23 7
A store() 0 21 4
A createSchema() 0 17 1
B getFields() 0 46 6
A updateSchema() 0 16 1

How to fix   Complexity   

Complex Class

Complex classes like RTableNested often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use RTableNested, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @package     Redcore
4
 * @subpackage  Base
5
 *
6
 * @copyright   Copyright (C) 2008 - 2021 redWEB.dk. All rights reserved.
7
 * @license     GNU General Public License version 2 or later, see LICENSE.
8
 */
9
10
defined('JPATH_REDCORE') or die;
11
12
JLoader::import('joomla.database.tablenested');
13
14
use Joomla\Utilities\ArrayHelper;
15
16
/**
17
 * redCORE Base Table
18
 *
19
 * @package     Redcore
20
 * @subpackage  Base
21
 * @since       1.0
22
 */
23
class RTableNested extends JTableNested
24
{
25
	/**
26
	 * The options.
27
	 *
28
	 * @var  array
29
	 */
30
	protected $_options = array();
31
32
	/**
33
	 * Prefix to add to log files
34
	 *
35
	 * @var  string
36
	 */
37
	protected $_logPrefix = 'redcore';
38
39
	/**
40
	 * The table name without the prefix. Ex: cursos_courses
41
	 *
42
	 * @var  string
43
	 */
44
	protected $_tableName = null;
45
46
	/**
47
	 * The table key column. Usually: id
48
	 *
49
	 * @var  string
50
	 */
51
	protected $_tableKey = 'id';
52
53
	/**
54
	 * Field name to publish/unpublish/trash table registers. Ex: state
55
	 *
56
	 * @var  string
57
	 */
58
	protected $_tableFieldState = 'state';
59
60
	/**
61
	 * Field name to keep creator user (created_by)
62
	 *
63
	 * @var  string
64
	 */
65
	protected $_tableFieldCreatedBy = 'created_by';
66
67
	/**
68
	 * Field name to keep latest modifier user (modified_by)
69
	 *
70
	 * @var  string
71
	 */
72
	protected $_tableFieldModifiedBy = 'modified_by';
73
74
	/**
75
	 * Field name to keep created date (created_date)
76
	 *
77
	 * @var  string
78
	 */
79
	protected $_tableFieldCreatedDate = 'created_date';
80
81
	/**
82
	 * Field name to keep latest modified user (modified_date)
83
	 *
84
	 * @var  string
85
	 */
86
	protected $_tableFieldModifiedDate = 'modified_date';
87
88
	/**
89
	 * Format for audit date fields (created_date, modified_date)
90
	 *
91
	 * @var  string
92
	 */
93
	protected $_auditDateFormat = 'Y-m-d H:i:s';
94
95
	/**
96
	 * An array of plugin types to import.
97
	 *
98
	 * @var  array
99
	 */
100
	protected $_pluginTypesToImport = array();
101
102
	/**
103
	 * Event name to trigger before load().
104
	 *
105
	 * @var  string
106
	 */
107
	protected $_eventBeforeLoad;
108
109
	/**
110
	 * Event name to trigger after load().
111
	 *
112
	 * @var  string
113
	 */
114
	protected $_eventAfterLoad;
115
116
	/**
117
	 * Event name to trigger before delete().
118
	 *
119
	 * @var  string
120
	 */
121
	protected $_eventBeforeDelete;
122
123
	/**
124
	 * Event name to trigger after delete().
125
	 *
126
	 * @var  string
127
	 */
128
	protected $_eventAfterDelete;
129
130
	/**
131
	 * Event name to trigger before check().
132
	 *
133
	 * @var  string
134
	 */
135
	protected $_eventBeforeCheck;
136
137
	/**
138
	 * Event name to trigger after check().
139
	 *
140
	 * @var  string
141
	 */
142
	protected $_eventAfterCheck;
143
144
	/**
145
	 * Event name to trigger before store().
146
	 *
147
	 * @var  string
148
	 */
149
	protected $_eventBeforeStore;
150
151
	/**
152
	 * Event name to trigger after store().
153
	 *
154
	 * @var  string
155
	 */
156
	protected $_eventAfterStore;
157
158
	/**
159
	 * Constructor
160
	 *
161
	 * @param   JDatabase  &$db  A database connector object
162
	 *
163
	 * @throws  UnexpectedValueException
164
	 */
165
	public function __construct(&$db)
166
	{
167
		// Keep checking _tbl value for standard defined tables
168
		if (empty($this->_tbl) && !empty($this->_tableName))
169
		{
170
			// Add the table prefix
171
			$this->_tbl = '#__' . $this->_tableName;
172
		}
173
174
		$key = $this->_tbl_key;
175
176
		if (empty($key) && !empty($this->_tbl_keys))
177
		{
178
			$key = $this->_tbl_keys;
179
		}
180
181
		// Keep checking _tbl_key for standard defined tables
182
		if (empty($key) && !empty($this->_tableKey))
183
		{
184
			$this->_tbl_key = $this->_tableKey;
185
			$key            = $this->_tbl_key;
186
		}
187
188
		if (empty($this->_tbl) || empty($key))
189
		{
190
			throw new UnexpectedValueException(sprintf('Missing data to initialize %s table | id: %s', $this->_tbl, $key));
191
		}
192
193
		parent::__construct($this->_tbl, $key, $db);
194
	}
195
196
	/**
197
	 * Method to bind an associative array or object to the JTable instance.This
198
	 * method only binds properties that are publicly accessible and optionally
199
	 * takes an array of properties to ignore when binding.
200
	 *
201
	 * @param   mixed  $src     An associative array or object to bind to the JTable instance.
202
	 * @param   mixed  $ignore  An optional array or space separated list of properties to ignore while binding.
203
	 *
204
	 * @return  boolean  True on success.
205
	 *
206
	 * @throws  InvalidArgumentException
207
	 */
208
	public function bind($src, $ignore = array())
209
	{
210
		if (isset($src['params']) && is_array($src['params']))
211
		{
212
			$registry = new JRegistry;
0 ignored issues
show
Bug introduced by
The type JRegistry was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
213
			$registry->loadArray($src['params']);
214
			$src['params'] = (string) $registry;
215
		}
216
217
		if (isset($src['metadata']) && is_array($src['metadata']))
218
		{
219
			$registry = new JRegistry;
220
			$registry->loadArray($src['metadata']);
221
			$src['metadata'] = (string) $registry;
222
		}
223
224
		if (isset($src['rules']) && is_array($src['rules']))
225
		{
226
			$rules = new JAccessRules($src['rules']);
227
			$this->setRules($rules);
228
		}
229
230
		return parent::bind($src, $ignore);
231
	}
232
233
	/**
234
	 * Import the plugin types.
235
	 *
236
	 * @return  void
237
	 */
238
	private function importPluginTypes()
239
	{
240
		foreach ($this->_pluginTypesToImport as $type)
241
		{
242
			JPluginHelper::importPlugin($type);
243
		}
244
	}
245
246
	/**
247
	 * Called before load().
248
	 *
249
	 * @param   mixed    $keys   An optional primary key value to load the row by, or an array of fields to match.  If not
250
	 *                           set the instance property value is used.
251
	 * @param   boolean  $reset  True to reset the default values before loading the new row.
252
	 *
253
	 * @return  boolean  True if successful. False if row not found.
254
	 */
255
	protected function beforeLoad($keys = null, $reset = true)
256
	{
257
		if ($this->_eventBeforeLoad)
258
		{
259
			// Import the plugin types
260
			$this->importPluginTypes();
261
262
			// Trigger the event
263
			$results = RFactory::getDispatcher()
264
				->trigger($this->_eventBeforeLoad, array($this, $keys, $reset));
265
266
			if (count($results) && in_array(false, $results, true))
267
			{
268
				return false;
269
			}
270
		}
271
272
		return true;
273
	}
274
275
	/**
276
	 * Called after load().
277
	 *
278
	 * @param   mixed    $keys   An optional primary key value to load the row by, or an array of fields to match.  If not
279
	 *                           set the instance property value is used.
280
	 * @param   boolean  $reset  True to reset the default values before loading the new row.
281
	 *
282
	 * @return  boolean  True if successful. False if row not found.
283
	 */
284
	protected function afterLoad($keys = null, $reset = true)
285
	{
286
		if ($this->_eventAfterLoad)
287
		{
288
			// Import the plugin types
289
			$this->importPluginTypes();
290
291
			// Trigger the event
292
			$results = RFactory::getDispatcher()
293
				->trigger($this->_eventAfterLoad, array($this, $keys, $reset));
294
295
			if (count($results) && in_array(false, $results, true))
296
			{
297
				return false;
298
			}
299
		}
300
301
		return true;
302
	}
303
304
	/**
305
	 * Method to load a row from the database by primary key and bind the fields
306
	 * to the JTable instance properties.
307
	 *
308
	 * @param   mixed    $keys   An optional primary key value to load the row by, or an array of fields to match.  If not
309
	 *                           set the instance property value is used.
310
	 * @param   boolean  $reset  True to reset the default values before loading the new row.
311
	 *
312
	 * @return  boolean  True if successful. False if row not found.
313
	 */
314
	public function load($keys = null, $reset = true)
315
	{
316
		// Before load
317
		if (!$this->beforeLoad($keys, $reset))
318
		{
319
			return false;
320
		}
321
322
		// Load
323
		if (!parent::load($keys, $reset))
324
		{
325
			return false;
326
		}
327
328
		// After load
329
		if (!$this->afterLoad($keys, $reset))
330
		{
331
			return false;
332
		}
333
334
		return true;
335
	}
336
337
	/**
338
	 * Called before delete().
339
	 *
340
	 * @param   integer  $pk        The primary key of the node to delete.
341
	 * @param   boolean  $children  True to delete child nodes, false to move them up a level.
342
	 *
343
	 * @return  boolean  True on success.
344
	 */
345
	protected function beforeDelete($pk = null, $children = true)
346
	{
347
		if ($this->_eventBeforeDelete)
348
		{
349
			// Import the plugin types
350
			$this->importPluginTypes();
351
352
			// Trigger the event
353
			$results = RFactory::getDispatcher()
354
				->trigger($this->_eventBeforeDelete, array($this, $pk, $children));
355
356
			if (count($results) && in_array(false, $results, true))
357
			{
358
				return false;
359
			}
360
		}
361
362
		return true;
363
	}
364
365
	/**
366
	 * Called after delete().
367
	 *
368
	 * @param   integer  $pk        The primary key of the node to delete.
369
	 * @param   boolean  $children  True to delete child nodes, false to move them up a level.
370
	 *
371
	 * @return  boolean  True on success.
372
	 */
373
	protected function afterDelete($pk = null, $children = true)
374
	{
375
		// Trigger after delete
376
		if ($this->_eventAfterDelete)
377
		{
378
			// Import the plugin types
379
			$this->importPluginTypes();
380
381
			// Trigger the event
382
			$results = RFactory::getDispatcher()
383
				->trigger($this->_eventAfterDelete, array($this, $pk, $children));
384
385
			if (count($results) && in_array(false, $results, true))
386
			{
387
				return false;
388
			}
389
		}
390
391
		return true;
392
	}
393
394
	/**
395
	 * Method to delete a node and, optionally, its child nodes from the table.
396
	 *
397
	 * @param   integer  $pk        The primary key of the node to delete.
398
	 * @param   boolean  $children  True to delete child nodes, false to move them up a level.
399
	 *
400
	 * @return  boolean  True on success.
401
	 */
402
	public function delete($pk = null, $children = true)
403
	{
404
		// Before delete
405
		if (!$this->beforeDelete($pk, $children))
406
		{
407
			return false;
408
		}
409
410
		// Delete
411
		if (!parent::delete($pk, $children))
412
		{
413
			return false;
414
		}
415
416
		// After delete
417
		if (!$this->afterDelete($pk, $children))
418
		{
419
			return false;
420
		}
421
422
		return true;
423
	}
424
425
	/**
426
	 * Called before check().
427
	 *
428
	 * @return  boolean  True if all checks pass.
429
	 */
430
	protected function beforeCheck()
431
	{
432
		if ($this->_eventBeforeCheck)
433
		{
434
			// Import the plugin types
435
			$this->importPluginTypes();
436
437
			// Trigger the event
438
			$results = RFactory::getDispatcher()
439
				->trigger($this->_eventBeforeCheck, array($this));
440
441
			if (count($results) && in_array(false, $results, true))
442
			{
443
				return false;
444
			}
445
		}
446
447
		return true;
448
	}
449
450
	/**
451
	 * Called after check().
452
	 *
453
	 * @return  boolean  True if all checks pass.
454
	 */
455
	protected function afterCheck()
456
	{
457
		// Trigger after check
458
		if ($this->_eventAfterCheck)
459
		{
460
			// Import the plugin types
461
			$this->importPluginTypes();
462
463
			// Trigger the event
464
			$results = RFactory::getDispatcher()
465
				->trigger($this->_eventAfterCheck, array($this));
466
467
			if (count($results) && in_array(false, $results, true))
468
			{
469
				return false;
470
			}
471
		}
472
473
		return true;
474
	}
475
476
	/**
477
	 * Checks that the object is valid and able to be stored.
478
	 *
479
	 * This method checks that the parent_id is non-zero and exists in the database.
480
	 * Note that the root node (parent_id = 0) cannot be manipulated with this class.
481
	 *
482
	 * @return  boolean  True if all checks pass.
483
	 */
484
	public function check()
485
	{
486
		// Before check
487
		if (!$this->beforeCheck())
488
		{
489
			return false;
490
		}
491
492
		// Check
493
		if (!parent::check())
494
		{
495
			return false;
496
		}
497
498
		// After check
499
		if (!$this->afterCheck())
500
		{
501
			return false;
502
		}
503
504
		return true;
505
	}
506
507
	/**
508
	 * Called before store().
509
	 *
510
	 * @param   boolean  $updateNulls  True to update null values as well.
511
	 *
512
	 * @return  boolean  True on success.
513
	 */
514
	protected function beforeStore($updateNulls = false)
515
	{
516
		if ($this->_eventBeforeStore)
517
		{
518
			// Import the plugin types
519
			$this->importPluginTypes();
520
521
			// Trigger the event
522
			$results = RFactory::getDispatcher()
523
				->trigger($this->_eventBeforeStore, array($this, $updateNulls));
524
525
			if (count($results) && in_array(false, $results, true))
526
			{
527
				return false;
528
			}
529
		}
530
531
		// Audit fields optional auto-update (on by default)
532
		if ($this->getOption('updateAuditFields', true))
533
		{
534
			RTable::updateAuditFields($this);
0 ignored issues
show
Bug introduced by
$this of type RTableNested is incompatible with the type RTable expected by parameter $tableInstance of RTable::updateAuditFields(). ( Ignorable by Annotation )

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

534
			RTable::updateAuditFields(/** @scrutinizer ignore-type */ $this);
Loading history...
535
		}
536
537
		return true;
538
	}
539
540
	/**
541
	 * Called after store().
542
	 *
543
	 * @param   boolean  $updateNulls  True to update null values as well.
544
	 *
545
	 * @return  boolean  True on success.
546
	 */
547
	protected function afterStore($updateNulls = false)
548
	{
549
		if ($this->_eventAfterStore)
550
		{
551
			// Import the plugin types
552
			$this->importPluginTypes();
553
554
			// Trigger the event
555
			$results = RFactory::getDispatcher()
556
				->trigger($this->_eventAfterStore, array($this, $updateNulls));
557
558
			if (count($results) && in_array(false, $results, true))
559
			{
560
				return false;
561
			}
562
		}
563
564
		return true;
565
	}
566
567
	/**
568
	 * Method to store a node in the database table.
569
	 *
570
	 * @param   boolean  $updateNulls  True to update null values as well.
571
	 *
572
	 * @return  boolean  True on success.
573
	 */
574
	public function store($updateNulls = false)
575
	{
576
		// Before store
577
		if (!$this->beforeStore($updateNulls))
578
		{
579
			return false;
580
		}
581
582
		// Store
583
		if (!parent::store($updateNulls))
584
		{
585
			return false;
586
		}
587
588
		// After store
589
		if (!$this->afterStore($updateNulls))
590
		{
591
			return false;
592
		}
593
594
		return true;
595
	}
596
597
	/**
598
	 * Override the parent checkin method to set checked_out = null instead of 0 so the foreign key doesn't fail.
599
	 *
600
	 * @param   mixed  $pk  An optional primary key value to check out.  If not set the instance property value is used.
601
	 *
602
	 * @return  boolean  True on success.
603
	 *
604
	 * @throws  UnexpectedValueException
605
	 */
606
	public function checkIn($pk = null)
607
	{
608
		// If there is no checked_out or checked_out_time field, just return true.
609
		if (!property_exists($this, 'checked_out') || !property_exists($this, 'checked_out_time'))
610
		{
611
			return true;
612
		}
613
614
		$k  = $this->_tbl_key;
615
		$pk = (is_null($pk)) ? $this->$k : $pk;
616
617
		// If no primary key is given, return false.
618
		if ($pk === null)
619
		{
620
			throw new UnexpectedValueException('Null primary key not allowed.');
621
		}
622
623
		// Check the row in by primary key.
624
		$query = $this->_db->getQuery(true);
625
		$query->update($this->_tbl);
626
		$query->set($this->_db->quoteName('checked_out') . ' = NULL');
627
		$query->set($this->_db->quoteName('checked_out_time') . ' = ' . $this->_db->quote($this->_db->getNullDate()));
628
		$query->where($this->_tbl_key . ' = ' . $this->_db->quote($pk));
0 ignored issues
show
Bug introduced by
Are you sure $this->_db->quote($pk) of type array|string 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

628
		$query->where($this->_tbl_key . ' = ' . /** @scrutinizer ignore-type */ $this->_db->quote($pk));
Loading history...
629
		$this->_db->setQuery($query);
630
631
		// Check for a database error.
632
		$this->_db->execute();
633
634
		// Set table values in the object.
635
		$this->checked_out      = null;
0 ignored issues
show
Bug Best Practice introduced by
The property checked_out does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
636
		$this->checked_out_time = '';
0 ignored issues
show
Bug Best Practice introduced by
The property checked_out_time does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
637
638
		return true;
639
	}
640
641
	/**
642
	 * Method to set the publishing state for a row or list of rows in the database
643
	 * table.  The method respects checked out rows by other users and will attempt
644
	 * to checkin rows that it can after adjustments are made.
645
	 *
646
	 * @param   mixed    $pks     An optional array of primary key values to update.
647
	 *                            If not set the instance property value is used.
648
	 * @param   integer  $state   The publishing state. eg. [0 = unpublished, 1 = published]
649
	 * @param   integer  $userId  The user id of the user performing the operation.
650
	 *
651
	 * @return  boolean  True on success; false if $pks is empty.
652
	 */
653
	public function publish($pks = null, $state = 1, $userId = 0)
654
	{
655
		// Use an easy to handle variable for database
656
		$db = $this->_db;
657
658
		// Initialise variables.
659
		$k = $this->_tbl_key;
660
661
		// Sanitize input.
662
		ArrayHelper::toInteger($pks);
663
		$userId = (int) $userId;
664
		$state  = (int) $state;
665
666
		// If there are no primary keys set check to see if the instance key is set.
667
		if (empty($pks))
668
		{
669
			if ($this->$k)
670
			{
671
				$pks = array($this->$k);
672
			}
673
674
			// Nothing to set publishing state on, return false.
675
			else
676
			{
677
				$this->setError(JText::_('JLIB_DATABASE_ERROR_NO_ROWS_SELECTED'));
0 ignored issues
show
Deprecated Code introduced by
The function JObject::setError() has been deprecated: 12.3 JError has been deprecated ( Ignorable by Annotation )

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

677
				/** @scrutinizer ignore-deprecated */ $this->setError(JText::_('JLIB_DATABASE_ERROR_NO_ROWS_SELECTED'));

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
678
679
				return false;
680
			}
681
		}
682
683
		// Build the main update query
684
		$query = $db->getQuery(true)
685
			->update($db->quoteName($this->_tbl))
686
			->set($db->quoteName($this->_tableFieldState) . ' = ' . (int) $state)
687
			->where($db->quoteName($k) . '=' . implode(' OR ' . $db->quoteName($k) . '=', $pks));
688
689
		// Determine if there is checkin support for the table.
690
		if (!property_exists($this, 'checked_out') || !property_exists($this, 'checked_out_time'))
691
		{
692
			$checkin = false;
693
		}
694
		else
695
		{
696
			$query->where('(checked_out = 0 OR checked_out IS NULL OR checked_out = ' . (int) $userId . ')');
697
			$checkin = true;
698
		}
699
700
		// Update the publishing state for rows with the given primary keys.
701
		$db->setQuery($query);
702
703
		try
704
		{
705
			$db->execute();
706
		}
707
		catch (RuntimeException $e)
708
		{
709
			JError::raiseWarning(500, $e->getMessage());
0 ignored issues
show
Deprecated Code introduced by
The function JError::raiseWarning() has been deprecated: 1.7 ( Ignorable by Annotation )

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

709
			/** @scrutinizer ignore-deprecated */ JError::raiseWarning(500, $e->getMessage());

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
710
			JLog::add(JText::sprintf('REDCORE_ERROR_QUERY', $db->dump()), JLog::ERROR, $this->_logPrefix . 'Queries');
0 ignored issues
show
Bug introduced by
Are you sure the usage of $db->dump() targeting JDatabaseDriver::__call() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

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

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

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

Loading history...
711
712
			return false;
713
		}
714
715
		// If checkin is supported and all rows were adjusted, check them in.
716
		if ($checkin && (count($pks) == $this->_db->getAffectedRows()))
717
		{
718
			// Checkin the rows.
719
			foreach ($pks as $pk)
720
			{
721
				$this->checkin($pk);
722
			}
723
		}
724
725
		// If the JTable instance value is in the list of primary keys that were set, set the instance.
726
		if (in_array($this->$k, $pks))
727
		{
728
			$this->{$this->_tableFieldState} = $state;
729
		}
730
731
		$this->setError('');
0 ignored issues
show
Deprecated Code introduced by
The function JObject::setError() has been deprecated: 12.3 JError has been deprecated ( Ignorable by Annotation )

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

731
		/** @scrutinizer ignore-deprecated */ $this->setError('');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
732
733
		return true;
734
	}
735
736
	/**
737
	 * Set a table option value.
738
	 *
739
	 * @param   string  $key  The key
740
	 * @param   mixed   $val  The default value
741
	 *
742
	 * @return  JTable
743
	 */
744
	public function setOption($key, $val)
745
	{
746
		$this->_options[$key] = $val;
747
748
		return $this;
749
	}
750
751
	/**
752
	 * Get a table option value.
753
	 *
754
	 * @param   string  $key      The key
755
	 * @param   mixed   $default  The default value
756
	 *
757
	 * @return  mixed  The value or the default value
758
	 */
759
	public function getOption($key, $default = null)
760
	{
761
		if (isset($this->_options[$key]))
762
		{
763
			return $this->_options[$key];
764
		}
765
766
		return $default;
767
	}
768
769
	/**
770
	 * Validate that the primary key has been set.
771
	 *
772
	 * @return  boolean  True if the primary key(s) have been set.
773
	 *
774
	 * @since   1.5.2
775
	 */
776
	public function hasPrimaryKey()
777
	{
778
		// For Joomla 3.2+ a native method has been provided
779
		if (method_exists(get_parent_class(), 'hasPrimaryKey'))
780
		{
781
			return parent::hasPrimaryKey();
782
		}
783
784
		// Otherwise, it checks if the only key field compatible for older Joomla versions is set or not
785
		if (isset($this->_tbl_key) && !empty($this->_tbl_key) && empty($this->{$this->_tbl_key}))
786
		{
787
			return false;
788
		}
789
790
		return true;
791
	}
792
793
	/**
794
	 * Get the columns from database table.
795
	 *
796
	 * @param   bool  $reload  flag to reload cache
797
	 *
798
	 * @return  mixed  An array of the field names, or false if an error occurs.
799
	 *
800
	 * @since   11.1
801
	 * @throws  UnexpectedValueException
802
	 */
803
	public function getFields($reload = false)
804
	{
805
		static $cache = null;
806
807
		if ($cache !== null && !$reload)
808
		{
809
			return $cache;
810
		}
811
812
		$dbo = $this->getDbo();
813
814
		$query = $dbo->getQuery(true);
815
816
		$query->select('*');
817
		$query->from('#__redcore_schemas');
818
819
		$assetName = $this->_tbl;
820
		$query->where('asset_id = ' . $dbo->q($assetName));
821
		$result = $dbo->setQuery($query)->loadAssoc();
822
823
		if (is_null($result))
824
		{
825
			$result = $this->createSchema($assetName);
826
		}
827
828
		$cachedOn = new \JDate($result['cached_on']);
829
		$now      = new \JDate;
830
831
		if ($now->toUnix() > ($cachedOn->toUnix() + 86400))
832
		{
833
			$this->updateSchema($assetName, $now);
834
		}
835
836
		// Decode the fields
837
		$fields = (array) json_decode($result['fields']);
838
839
		if (empty($fields))
840
		{
841
			$msg = JText::sprintf('REDCORE_TABLE_ERROR_NO_COLUMNS_FOUND', $this->_tbl);
842
843
			throw new UnexpectedValueException($msg, 500);
844
		}
845
846
		$cache = $fields;
847
848
		return $cache;
849
	}
850
851
	/**
852
	 * Method to cache the table schema in the logical schemas table
853
	 *
854
	 * @param   string  $assetName  the asset name of this table. standard format is "com_componentName.TableName"
855
	 *
856
	 * @return array
857
	 */
858
	private function createSchema($assetName)
859
	{
860
		$dbo   = $this->getDbo();
861
		$query = $dbo->getQuery(true);
862
863
		$query->insert('#__redcore_schemas');
864
		$query->set('asset_id = ' . $dbo->q($assetName));
865
866
		$fields = json_encode($dbo->getTableColumns($this->_tbl, false));
867
		$query->set('fields = ' . $dbo->q($fields));
868
869
		$now = new \JDate;
870
		$query->set('cached_on = ' . $dbo->q($now->toSql()));
871
872
		$dbo->setQuery($query)->execute();
873
874
		return array('asset_id' => $assetName, 'fields' => $fields, 'cached_on' => $now->toSql());
875
	}
876
877
	/**
878
	 * Method to update the table schema in the logical schemas table
879
	 *
880
	 * @param   string  $assetName  the asset name of this table. standard format is "com_componentName.TableName"
881
	 * @param   \JDate  $now        the current time
882
	 *
883
	 * @return array
884
	 */
885
	private function updateSchema($assetName, \JDate $now)
886
	{
887
		$dbo   = $this->getDbo();
888
		$query = $dbo->getQuery(true);
889
890
		$query->update('#__redcore_schemas');
891
892
		$fields = json_encode($dbo->getTableColumns($this->_tbl, false));
893
		$query->set('fields = ' . $dbo->q($fields));
894
		$query->set('cached_on = ' . $dbo->q($now->toSql()));
895
896
		$query->where('asset_id = ' . $dbo->q($assetName));
897
898
		$dbo->setQuery($query)->execute();
899
900
		return array('asset_id' => $assetName, 'fields' => $fields, 'cached_on' => $now->toSql());
901
	}
902
}
903