RTable::doDelete()   C
last analyzed

Complexity

Conditions 14
Paths 30

Size

Total Lines 89
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 36
c 0
b 0
f 0
dl 0
loc 89
rs 6.2666
cc 14
nc 30
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
use Joomla\Utilities\ArrayHelper;
13
14
/**
15
 * redCORE Base Table
16
 *
17
 * @package     Redcore
18
 * @subpackage  Base
19
 * @since       1.0
20
 */
21
class RTable extends JTable
22
{
23
	/**
24
	 * The options.
25
	 *
26
	 * @var  array
27
	 */
28
	protected $_options = array();
29
30
	/**
31
	 * Prefix to add to log files
32
	 *
33
	 * @var  string
34
	 */
35
	protected $_logPrefix = 'redcore';
36
37
	/**
38
	 * The table name without the prefix. Ex: cursos_courses
39
	 *
40
	 * @var  string
41
	 */
42
	protected $_tableName = null;
43
44
	/**
45
	 * The table key column. Usually: id
46
	 *
47
	 * @var  string
48
	 */
49
	protected $_tableKey = 'id';
50
51
	/**
52
	 * Field name to publish/unpublish/trash table registers. Ex: state
53
	 *
54
	 * @var  string
55
	 */
56
	protected $_tableFieldState = 'state';
57
58
	/**
59
	 * Field name to keep creator user (created_by)
60
	 *
61
	 * @var  string
62
	 */
63
	protected $_tableFieldCreatedBy = 'created_by';
64
65
	/**
66
	 * Field name to keep latest modifier user (modified_by)
67
	 *
68
	 * @var  string
69
	 */
70
	protected $_tableFieldModifiedBy = 'modified_by';
71
72
	/**
73
	 * Field name to keep created date (created_date)
74
	 *
75
	 * @var  string
76
	 */
77
	protected $_tableFieldCreatedDate = 'created_date';
78
79
	/**
80
	 * Field name to keep latest modified user (modified_date)
81
	 *
82
	 * @var  string
83
	 */
84
	protected $_tableFieldModifiedDate = 'modified_date';
85
86
	/**
87
	 * Format for audit date fields (created_date, modified_date)
88
	 *
89
	 * @var  string
90
	 */
91
	protected $_auditDateFormat = 'Y-m-d H:i:s';
92
93
	/**
94
	 * An array of plugin types to import.
95
	 *
96
	 * @var  array
97
	 */
98
	protected $_pluginTypesToImport = array();
99
100
	/**
101
	 * Event name to trigger before load().
102
	 *
103
	 * @var  string
104
	 */
105
	protected $_eventBeforeLoad;
106
107
	/**
108
	 * Event name to trigger after load().
109
	 *
110
	 * @var  string
111
	 */
112
	protected $_eventAfterLoad;
113
114
	/**
115
	 * Event name to trigger before delete().
116
	 *
117
	 * @var  string
118
	 */
119
	protected $_eventBeforeDelete;
120
121
	/**
122
	 * Event name to trigger after delete().
123
	 *
124
	 * @var  string
125
	 */
126
	protected $_eventAfterDelete;
127
128
	/**
129
	 * Event name to trigger before check().
130
	 *
131
	 * @var  string
132
	 */
133
	protected $_eventBeforeCheck;
134
135
	/**
136
	 * Event name to trigger after check().
137
	 *
138
	 * @var  string
139
	 */
140
	protected $_eventAfterCheck;
141
142
	/**
143
	 * Event name to trigger before store().
144
	 *
145
	 * @var  string
146
	 */
147
	protected $_eventBeforeStore;
148
149
	/**
150
	 * Event name to trigger after store().
151
	 *
152
	 * @var  string
153
	 */
154
	protected $_eventAfterStore;
155
156
	/**
157
	 * Constructor
158
	 *
159
	 * @param   JDatabaseDriver  &$db  A database connector object
160
	 *
161
	 * @throws  UnexpectedValueException
162
	 */
163
	public function __construct(&$db)
164
	{
165
		// Keep checking _tbl value for standard defined tables
166
		if (empty($this->_tbl) && !empty($this->_tableName))
167
		{
168
			// Add the table prefix
169
			$this->_tbl = '#__' . $this->_tableName;
170
		}
171
172
		$key = $this->_tbl_key;
173
174
		if (empty($key) && !empty($this->_tbl_keys))
175
		{
176
			$key = $this->_tbl_keys;
177
		}
178
179
		// Keep checking _tbl_key for standard defined tables
180
		if (empty($key) && !empty($this->_tableKey))
181
		{
182
			$this->_tbl_key = $this->_tableKey;
183
			$key = $this->_tbl_key;
184
		}
185
186
		if (empty($this->_tbl) || empty($key))
187
		{
188
			throw new UnexpectedValueException(sprintf('Missing data to initialize %s table | id: %s', $this->_tbl, $key));
189
		}
190
191
		parent::__construct($this->_tbl, $key, $db);
192
	}
193
194
	/**
195
	 * Method to bind an associative array or object to the JTable instance.This
196
	 * method only binds properties that are publicly accessible and optionally
197
	 * takes an array of properties to ignore when binding.
198
	 *
199
	 * @param   mixed  $src     An associative array or object to bind to the JTable instance.
200
	 * @param   mixed  $ignore  An optional array or space separated list of properties to ignore while binding.
201
	 *
202
	 * @return  boolean  True on success.
203
	 *
204
	 * @throws  InvalidArgumentException
205
	 */
206
	public function bind($src, $ignore = array())
207
	{
208
		if (isset($src['params']) && is_array($src['params']))
209
		{
210
			$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...
211
			$registry->loadArray($src['params']);
212
			$src['params'] = (string) $registry;
213
		}
214
215
		if (isset($src['metadata']) && is_array($src['metadata']))
216
		{
217
			$registry = new JRegistry;
218
			$registry->loadArray($src['metadata']);
219
			$src['metadata'] = (string) $registry;
220
		}
221
222
		if (isset($src['rules']) && is_array($src['rules']))
223
		{
224
			$rules = new JAccessRules($src['rules']);
225
			$this->setRules($rules);
226
		}
227
228
		return parent::bind($src, $ignore);
229
	}
230
231
	/**
232
	 * Import the plugin types.
233
	 *
234
	 * @return  void
235
	 */
236
	private function importPluginTypes()
237
	{
238
		foreach ($this->_pluginTypesToImport as $type)
239
		{
240
			JPluginHelper::importPlugin($type);
241
		}
242
	}
243
244
	/**
245
	 * Called before load().
246
	 *
247
	 * @param   mixed    $keys   An optional primary key value to load the row by, or an array of fields to match.  If not
248
	 *                           set the instance property value is used.
249
	 * @param   boolean  $reset  True to reset the default values before loading the new row.
250
	 *
251
	 * @return  boolean  True if successful. False if row not found.
252
	 */
253
	protected function beforeLoad($keys = null, $reset = true)
254
	{
255
		if ($this->_eventBeforeLoad)
256
		{
257
			// Import the plugin types
258
			$this->importPluginTypes();
259
260
			// Trigger the event
261
			$results = RFactory::getDispatcher()
262
				->trigger($this->_eventBeforeLoad, array($this, $keys, $reset));
263
264
			if (count($results) && in_array(false, $results, true))
265
			{
266
				return false;
267
			}
268
		}
269
270
		return true;
271
	}
272
273
	/**
274
	 * Called after load().
275
	 *
276
	 * @param   mixed    $keys   An optional primary key value to load the row by, or an array of fields to match.  If not
277
	 *                           set the instance property value is used.
278
	 * @param   boolean  $reset  True to reset the default values before loading the new row.
279
	 *
280
	 * @return  boolean  True if successful. False if row not found.
281
	 */
282
	protected function afterLoad($keys = null, $reset = true)
283
	{
284
		if ($this->_eventAfterLoad)
285
		{
286
			// Import the plugin types
287
			$this->importPluginTypes();
288
289
			// Trigger the event
290
			$results = RFactory::getDispatcher()
291
				->trigger($this->_eventAfterLoad, array($this, $keys, $reset));
292
293
			if (count($results) && in_array(false, $results, true))
294
			{
295
				return false;
296
			}
297
		}
298
299
		return true;
300
	}
301
302
	/**
303
	 * Method to load a row from the database by primary key and bind the fields
304
	 * to the JTable instance properties.
305
	 *
306
	 * @param   mixed    $keys   An optional primary key value to load the row by, or an array of fields to match.  If not
307
	 *                           set the instance property value is used.
308
	 * @param   boolean  $reset  True to reset the default values before loading the new row.
309
	 *
310
	 * @return  boolean  True if successful. False if row not found.
311
	 */
312
	public function load($keys = null, $reset = true)
313
	{
314
		// Before load
315
		if (!$this->beforeLoad($keys, $reset))
316
		{
317
			return false;
318
		}
319
320
		// Load
321
		if (!parent::load($keys, $reset))
322
		{
323
			return false;
324
		}
325
326
		// After load
327
		if (!$this->afterLoad($keys, $reset))
328
		{
329
			return false;
330
		}
331
332
		return true;
333
	}
334
335
	/**
336
	 * Called before delete().
337
	 *
338
	 * @param   mixed  $pk  An optional primary key value to delete.  If not set the instance property value is used.
339
	 *
340
	 * @return  boolean  True on success.
341
	 */
342
	protected function beforeDelete($pk = null)
343
	{
344
		if ($this->_eventBeforeDelete)
345
		{
346
			// Import the plugin types
347
			$this->importPluginTypes();
348
349
			// Trigger the event
350
			$results = RFactory::getDispatcher()
351
				->trigger($this->_eventBeforeDelete, array($this, $pk));
352
353
			if (count($results) && in_array(false, $results, true))
354
			{
355
				return false;
356
			}
357
		}
358
359
		return true;
360
	}
361
362
	/**
363
	 * Called after delete().
364
	 *
365
	 * @param   mixed  $pk  An optional primary key value to delete.  If not set the instance property value is used.
366
	 *
367
	 * @return  boolean  True on success.
368
	 */
369
	protected function afterDelete($pk = null)
370
	{
371
		// Trigger after delete
372
		if ($this->_eventAfterDelete)
373
		{
374
			// Import the plugin types
375
			$this->importPluginTypes();
376
377
			// Trigger the event
378
			$results = RFactory::getDispatcher()
379
				->trigger($this->_eventAfterDelete, array($this, $pk));
380
381
			if (count($results) && in_array(false, $results, true))
382
			{
383
				return false;
384
			}
385
		}
386
387
		return true;
388
	}
389
390
	/**
391
	 * Deletes this row in database (or if provided, the row of key $pk)
392
	 *
393
	 * @param   mixed  $pk  An optional primary key value to delete.  If not set the instance property value is used.
394
	 *
395
	 * @return  boolean  True on success.
396
	 */
397
	public function delete($pk = null)
398
	{
399
		// Before delete
400
		if (!$this->beforeDelete($pk))
401
		{
402
			return false;
403
		}
404
405
		// Delete
406
		if (!$this->doDelete($pk))
407
		{
408
			return false;
409
		}
410
411
		// After delete
412
		if (!$this->afterDelete($pk))
413
		{
414
			return false;
415
		}
416
417
		return true;
418
	}
419
420
	/**
421
	 * Called before check().
422
	 *
423
	 * @return  boolean  True if all checks pass.
424
	 */
425
	protected function beforeCheck()
426
	{
427
		if ($this->_eventBeforeCheck)
428
		{
429
			// Import the plugin types
430
			$this->importPluginTypes();
431
432
			// Trigger the event
433
			$results = RFactory::getDispatcher()
434
				->trigger($this->_eventBeforeCheck, array($this));
435
436
			if (count($results) && in_array(false, $results, true))
437
			{
438
				return false;
439
			}
440
		}
441
442
		return true;
443
	}
444
445
	/**
446
	 * Called after check().
447
	 *
448
	 * @return  boolean  True if all checks pass.
449
	 */
450
	protected function afterCheck()
451
	{
452
		// Trigger after check
453
		if ($this->_eventAfterCheck)
454
		{
455
			// Import the plugin types
456
			$this->importPluginTypes();
457
458
			// Trigger the event
459
			$results = RFactory::getDispatcher()
460
				->trigger($this->_eventAfterCheck, array($this));
461
462
			if (count($results) && in_array(false, $results, true))
463
			{
464
				return false;
465
			}
466
		}
467
468
		return true;
469
	}
470
471
	/**
472
	 * Checks that the object is valid and able to be stored.
473
	 *
474
	 * This method checks that the parent_id is non-zero and exists in the database.
475
	 * Note that the root node (parent_id = 0) cannot be manipulated with this class.
476
	 *
477
	 * @return  boolean  True if all checks pass.
478
	 */
479
	public function check()
480
	{
481
		// Before check
482
		if (!$this->beforeCheck())
483
		{
484
			return false;
485
		}
486
487
		// Check
488
		if (!parent::check())
489
		{
490
			return false;
491
		}
492
493
		// After check
494
		if (!$this->afterCheck())
495
		{
496
			return false;
497
		}
498
499
		return true;
500
	}
501
502
	/**
503
	 * Called before store().
504
	 *
505
	 * @param   boolean  $updateNulls  True to update null values as well.
506
	 *
507
	 * @return  boolean  True on success.
508
	 */
509
	protected function beforeStore($updateNulls = false)
510
	{
511
		if ($this->_eventBeforeStore)
512
		{
513
			// Import the plugin types
514
			$this->importPluginTypes();
515
516
			// Trigger the event
517
			$results = RFactory::getDispatcher()
518
				->trigger($this->_eventBeforeStore, array($this, $updateNulls));
519
520
			if (count($results) && in_array(false, $results, true))
521
			{
522
				return false;
523
			}
524
		}
525
526
		// Audit fields optional auto-update (on by default)
527
		if ($this->getOption('updateAuditFields', true))
528
		{
529
			self::updateAuditFields($this);
530
		}
531
532
		return true;
533
	}
534
535
	/**
536
	 * Called after store().
537
	 *
538
	 * @param   boolean  $updateNulls  True to update null values as well.
539
	 *
540
	 * @return  boolean  True on success.
541
	 */
542
	protected function afterStore($updateNulls = false)
543
	{
544
		if ($this->_eventAfterStore)
545
		{
546
			// Import the plugin types
547
			$this->importPluginTypes();
548
549
			// Trigger the event
550
			$results = RFactory::getDispatcher()
551
				->trigger($this->_eventAfterStore, array($this, $updateNulls));
552
553
			if (count($results) && in_array(false, $results, true))
554
			{
555
				return false;
556
			}
557
		}
558
559
		return true;
560
	}
561
562
	/**
563
	 * Method to store a node in the database table.
564
	 *
565
	 * @param   boolean  $updateNulls  True to update null values as well.
566
	 *
567
	 * @return  boolean  True on success.
568
	 */
569
	public function store($updateNulls = false)
570
	{
571
		// Before store
572
		if (!$this->beforeStore($updateNulls))
573
		{
574
			return false;
575
		}
576
577
		// Store
578
		if (!parent::store($updateNulls))
579
		{
580
			return false;
581
		}
582
583
		// After store
584
		if (!$this->afterStore($updateNulls))
585
		{
586
			return false;
587
		}
588
589
		return true;
590
	}
591
592
	/**
593
	 * Override the parent checkin method to set checked_out = null instead of 0 so the foreign key doesn't fail.
594
	 *
595
	 * @param   mixed  $pk  An optional primary key value to check out.  If not set the instance property value is used.
596
	 *
597
	 * @return  boolean  True on success.
598
	 *
599
	 * @throws  UnexpectedValueException
600
	 */
601
	public function checkIn($pk = null)
602
	{
603
		// If there is no checked_out or checked_out_time field, just return true.
604
		if (!property_exists($this, 'checked_out') || !property_exists($this, 'checked_out_time'))
605
		{
606
			return true;
607
		}
608
609
		$k = $this->_tbl_key;
610
		$pk = (is_null($pk)) ? $this->$k : $pk;
611
612
		// If no primary key is given, return false.
613
		if ($pk === null)
614
		{
615
			throw new UnexpectedValueException('Null primary key not allowed.');
616
		}
617
618
		// Check the row in by primary key.
619
		$query = $this->_db->getQuery(true);
620
		$query->update($this->_tbl);
621
		$query->set($this->_db->quoteName('checked_out') . ' = NULL');
622
		$query->set($this->_db->quoteName('checked_out_time') . ' = ' . $this->_db->quote($this->_db->getNullDate()));
623
		$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

623
		$query->where($this->_tbl_key . ' = ' . /** @scrutinizer ignore-type */ $this->_db->quote($pk));
Loading history...
624
		$this->_db->setQuery($query);
625
626
		// Check for a database error.
627
		$this->_db->execute();
628
629
		// Set table values in the object.
630
		$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...
631
		$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...
632
633
		return true;
634
	}
635
636
	/**
637
	 * Method to set the publishing state for a row or list of rows in the database
638
	 * table.  The method respects checked out rows by other users and will attempt
639
	 * to checkin rows that it can after adjustments are made.
640
	 *
641
	 * @param   mixed    $pks     An optional array of primary key values to update.
642
	 *                            If not set the instance property value is used.
643
	 * @param   integer  $state   The publishing state. eg. [0 = unpublished, 1 = published]
644
	 * @param   integer  $userId  The user id of the user performing the operation.
645
	 *
646
	 * @return  boolean  True on success; false if $pks is empty.
647
	 */
648
	public function publish($pks = null, $state = 1, $userId = 0)
649
	{
650
		// Use an easy to handle variable for database
651
		$db = $this->_db;
652
653
		// Initialise variables.
654
		$k = $this->_tbl_key;
655
656
		// Sanitize input.
657
		ArrayHelper::toInteger($pks);
658
		$userId = (int) $userId;
659
		$state  = (int) $state;
660
661
		// If there are no primary keys set check to see if the instance key is set.
662
		if (empty($pks))
663
		{
664
			if ($this->$k)
665
			{
666
				$pks = array($this->$k);
667
			}
668
669
			// Nothing to set publishing state on, return false.
670
			else
671
			{
672
				$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

672
				/** @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...
673
674
				return false;
675
			}
676
		}
677
678
		// Build the main update query
679
		$query = $db->getQuery(true)
680
			->update($db->quoteName($this->_tbl))
681
			->set($db->quoteName($this->_tableFieldState) . ' = ' . (int) $state)
682
			->where($db->quoteName($k) . '=' . implode(' OR ' . $db->quoteName($k) . '=', $pks));
683
684
		// Determine if there is checkin support for the table.
685
		if (!property_exists($this, 'checked_out') || !property_exists($this, 'checked_out_time'))
686
		{
687
			$checkin = false;
688
		}
689
		else
690
		{
691
			$query->where('(checked_out = 0 OR checked_out IS NULL OR checked_out = ' . (int) $userId . ')');
692
			$checkin = true;
693
		}
694
695
		// Update the publishing state for rows with the given primary keys.
696
		$db->setQuery($query);
697
698
		try
699
		{
700
			$db->execute();
701
		}
702
		catch (RuntimeException $e)
703
		{
704
			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

704
			/** @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...
705
			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...
706
707
			return false;
708
		}
709
710
		// If checkin is supported and all rows were adjusted, check them in.
711
		if ($checkin && (count($pks) == $this->_db->getAffectedRows()))
712
		{
713
			// Checkin the rows.
714
			foreach ($pks as $pk)
715
			{
716
				$this->checkin($pk);
717
			}
718
		}
719
720
		// If the JTable instance value is in the list of primary keys that were set, set the instance.
721
		if (in_array($this->$k, $pks))
722
		{
723
			$this->{$this->_tableFieldState} = $state;
724
		}
725
726
		$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

726
		/** @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...
727
728
		return true;
729
	}
730
731
	/**
732
	 * Delete one or more registers
733
	 *
734
	 * @param   string/array  $pk  Array of ids or ids comma separated
0 ignored issues
show
Documentation Bug introduced by
The doc comment string/array at position 0 could not be parsed: Unknown type name 'string/array' at position 0 in string/array.
Loading history...
735
	 *
736
	 * @return  boolean  Deleted successfuly?
737
	 */
738
	protected function doDelete($pk = null)
739
	{
740
		// Initialise variables.
741
		$k = $this->_tbl_key;
742
743
		// Multiple keys
744
		$multiplePrimaryKeys = count($this->_tbl_keys) > 1;
745
746
		// We are dealing with multiple primary keys
747
		if ($multiplePrimaryKeys)
748
		{
749
			// Received an array of ids?
750
			if (is_null($pk))
751
			{
752
				$pk = array();
753
754
				foreach ($this->_tbl_keys AS $key)
755
				{
756
					$pk[$key] = $this->$key;
757
				}
758
			}
759
			elseif (is_array($pk))
760
			{
761
				$pk = array();
762
763
				foreach ($this->_tbl_keys AS $key)
764
				{
765
					$pk[$key] = !empty($pk[$key]) ? $pk[$key] : $this->$key;
766
				}
767
			}
768
		}
769
		// Standard Joomla delete method
770
		else
771
		{
772
			if (is_array($pk))
773
			{
774
				// Sanitize input.
775
				ArrayHelper::toInteger($pk);
776
				$pk = RHelperArray::quote($pk);
777
				$pk = implode(',', $pk);
778
				$multipleDelete = true;
0 ignored issues
show
Unused Code introduced by
The assignment to $multipleDelete is dead and can be removed.
Loading history...
779
			}
780
			// Try the instance property value
781
			elseif (empty($pk) && $this->{$k})
782
			{
783
				$pk = $this->{$k};
784
			}
785
		}
786
787
		// If no primary key is given, return false.
788
		if ($pk === null)
789
		{
790
			return false;
791
		}
792
793
		// Implement JObservableInterface: Pre-processing by observers
794
		$this->_observers->update('onBeforeDelete', array($pk));
795
796
		// Delete the row by primary key.
797
		$query = $this->_db->getQuery(true);
798
		$query->delete($this->_db->quoteName($this->_tbl));
799
800
		if ($multiplePrimaryKeys)
801
		{
802
			foreach ($this->_tbl_keys AS $k)
803
			{
804
				$query->where($this->_db->quoteName($k) . ' = ' . $this->_db->quote($pk[$k]));
0 ignored issues
show
Bug introduced by
Are you sure $this->_db->quote($pk[$k]) 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

804
				$query->where($this->_db->quoteName($k) . ' = ' . /** @scrutinizer ignore-type */ $this->_db->quote($pk[$k]));
Loading history...
805
			}
806
		}
807
		else
808
		{
809
			$query->where($this->_db->quoteName($this->_tbl_key) . ' IN (' . $pk . ')');
810
		}
811
812
		$this->_db->setQuery($query);
813
		$this->_db->execute();
814
815
		// Check for a database error.
816
		if ($this->_db->getErrorNum())
0 ignored issues
show
Deprecated Code introduced by
The function JDatabase::getErrorNum() has been deprecated: 13.3 (Platform) & 4.0 (CMS) ( Ignorable by Annotation )

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

816
		if (/** @scrutinizer ignore-deprecated */ $this->_db->getErrorNum())

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...
817
		{
818
			$this->setError($this->_db->getErrorMsg());
0 ignored issues
show
Deprecated Code introduced by
The function JDatabase::getErrorMsg() has been deprecated: 13.3 (Platform) & 4.0 (CMS) ( Ignorable by Annotation )

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

818
			$this->setError(/** @scrutinizer ignore-deprecated */ $this->_db->getErrorMsg());

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...
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

818
			/** @scrutinizer ignore-deprecated */ $this->setError($this->_db->getErrorMsg());

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...
819
820
			return false;
821
		}
822
823
		// Implement JObservableInterface: Post-processing by observers
824
		$this->_observers->update('onAfterDelete', array($pk));
825
826
		return true;
827
	}
828
829
	/**
830
	 * Get a table instance.
831
	 *
832
	 * @param   string  $name    The table name
833
	 * @param   mixed   $client  The client. null = auto, 1 = admin, 0 = frontend
834
	 * @param   array   $config  An optional array of configuration
835
	 * @param   string  $option  Component name, use for call table from another extension
836
	 *
837
	 * @return  RTable  The table
838
	 *
839
	 * @throws  InvalidArgumentException
840
	 */
841
	public static function getAutoInstance($name, $client = null, array $config = array(), $option = 'auto')
842
	{
843
		if ($option === 'auto')
844
		{
845
			$option = JFactory::getApplication()->input->getString('option', '');
846
847
			// Add com_ to the element name if not exist
848
			$option = (strpos($option, 'com_') === 0 ? '' : 'com_') . $option;
849
850
			if ($option == 'com_installer')
851
			{
852
				$installer = JInstaller::getInstance();
853
				$option    = $installer->manifestClass->getElement($installer);
854
			}
855
		}
856
857
		$componentName = ucfirst(strtolower(substr($option, 4)));
858
		$prefix        = $componentName . 'Table';
859
860
		if (is_null($client))
861
		{
862
			$client = (int) JFactory::getApplication()->isClient('administrator');
863
		}
864
865
		// Admin
866
		if ($client === 1)
867
		{
868
			JTable::addIncludePath(JPATH_ADMINISTRATOR . '/components/' . $option . '/tables');
869
		}
870
871
		// Site
872
		elseif ($client === 0)
873
		{
874
			JTable::addIncludePath(JPATH_SITE . '/components/' . $option . '/tables');
875
		}
876
877
		else
878
		{
879
			throw new InvalidArgumentException(
880
				sprintf('Cannot instanciate the table %s in component %s. Invalid client %s.', $name, $option, $client)
881
			);
882
		}
883
884
		$table = self::getInstance($name, $prefix, $config);
885
886
		if (!$table instanceof JTable)
887
		{
888
			throw new InvalidArgumentException(
889
				sprintf('Cannot instanciate the table %s in component %s from client %s.', $name, $option, $client)
890
			);
891
		}
892
893
		return $table;
894
	}
895
896
	/**
897
	 * Get a backend table instance
898
	 *
899
	 * @param   string  $name    The table name
900
	 * @param   array   $config  An optional array of configuration
901
	 * @param   string  $option  Component name, use for call table from another extension
902
	 *
903
	 * @return  RTable  The table
904
	 */
905
	public static function getAdminInstance($name, array $config = array(), $option = 'auto')
906
	{
907
		return self::getAutoInstance($name, 1, $config, $option);
908
	}
909
910
	/**
911
	 * Get a frontend table instance
912
	 *
913
	 * @param   string  $name    The table name
914
	 * @param   array   $config  An optional array of configuration
915
	 * @param   string  $option  Component name, use for call table from another extension
916
	 *
917
	 * @return  RTable  The table
918
	 */
919
	public static function getFrontInstance($name, array $config = array(), $option = 'auto')
920
	{
921
		return self::getAutoInstance($name, 0, $config, $option);
922
	}
923
924
	/**
925
	 * Set a table option value.
926
	 *
927
	 * @param   string  $key  The key
928
	 * @param   mixed   $val  The default value
929
	 *
930
	 * @return  JTable
931
	 */
932
	public function setOption($key, $val)
933
	{
934
		$this->_options[$key] = $val;
935
936
		return $this;
937
	}
938
939
	/**
940
	 * Get a table option value.
941
	 *
942
	 * @param   string  $key      The key
943
	 * @param   mixed   $default  The default value
944
	 *
945
	 * @return  mixed  The value or the default value
946
	 */
947
	public function getOption($key, $default = null)
948
	{
949
		if (isset($this->_options[$key]))
950
		{
951
			return $this->_options[$key];
952
		}
953
954
		return $default;
955
	}
956
957
	/**
958
	 * Validate that the primary key has been set.
959
	 *
960
	 * @return  boolean  True if the primary key(s) have been set.
961
	 *
962
	 * @since   1.5.2
963
	 */
964
	public function hasPrimaryKey()
965
	{
966
		// For Joomla 3.2+ a native method has been provided
967
		if (method_exists(get_parent_class(), 'hasPrimaryKey'))
968
		{
969
			return parent::hasPrimaryKey();
970
		}
971
972
		// Otherwise, it checks if the only key field compatible for older Joomla versions is set or not
973
		if (isset($this->_tbl_key) && !empty($this->_tbl_key) && empty($this->{$this->_tbl_key}))
974
		{
975
			return false;
976
		}
977
978
		return true;
979
	}
980
981
	/**
982
	 * Method to update audit fields using a static function, to reuse in non-children classes like RNestedTable
983
	 *
984
	 * @param   RTable  &$tableInstance  Table instance
985
	 *
986
	 * @return  void
987
	 *
988
	 * @since   1.5.2
989
	 */
990
	public static function updateAuditFields(&$tableInstance)
991
	{
992
		$tableFieldCreatedBy = $tableInstance->get('_tableFieldCreatedBy');
993
		$tableFieldCreatedDate = $tableInstance->get('_tableFieldCreatedDate');
994
		$tableFieldModifiedBy = $tableInstance->get('_tableFieldModifiedBy');
995
		$tableFieldModifiedDate = $tableInstance->get('_tableFieldModifiedDate');
996
		$auditDateFormat = $tableInstance->get('_auditDateFormat');
997
998
		// Optional created_by field updated when present
999
		if (!$tableInstance->hasPrimaryKey() && property_exists($tableInstance, $tableFieldCreatedBy))
0 ignored issues
show
Bug introduced by
It seems like $tableFieldCreatedBy can also be of type null; however, parameter $property of property_exists() does only seem to accept string, 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

999
		if (!$tableInstance->hasPrimaryKey() && property_exists($tableInstance, /** @scrutinizer ignore-type */ $tableFieldCreatedBy))
Loading history...
1000
		{
1001
			$user = JFactory::getUser();
1002
1003
			if ($user->id)
1004
			{
1005
				$tableInstance->{$tableFieldCreatedBy} = $user->id;
1006
			}
1007
			else
1008
			{
1009
				$tableInstance->{$tableFieldCreatedBy} = null;
1010
			}
1011
		}
1012
1013
		// Optional created_date field updated when present
1014
		if (!$tableInstance->hasPrimaryKey() && property_exists($tableInstance, $tableFieldCreatedDate))
1015
		{
1016
			$tableInstance->{$tableFieldCreatedDate} = JFactory::getDate()->format($auditDateFormat);
1017
		}
1018
1019
		// Optional modified_by field updated when present
1020
		if (property_exists($tableInstance, $tableFieldModifiedBy))
1021
		{
1022
			if (!isset($user))
1023
			{
1024
				$user = JFactory::getUser();
1025
			}
1026
1027
			if ($user->id)
1028
			{
1029
				$tableInstance->{$tableFieldModifiedBy} = $user->id;
1030
			}
1031
			else
1032
			{
1033
				$tableInstance->{$tableFieldModifiedBy} = null;
1034
			}
1035
		}
1036
1037
		// Optional modified_date field updated when present
1038
		if (property_exists($tableInstance, $tableFieldModifiedDate))
1039
		{
1040
			$tableInstance->{$tableFieldModifiedDate} = JFactory::getDate()->format($auditDateFormat);
1041
		}
1042
	}
1043
1044
	/**
1045
	 * Get the columns from database table.
1046
	 *
1047
	 * @param   bool  $reload  flag to reload cache
1048
	 *
1049
	 * @return  mixed  An array of the field names, or false if an error occurs.
1050
	 *
1051
	 * @since   11.1
1052
	 * @throws  UnexpectedValueException
1053
	 */
1054
	public function getFields($reload = false)
1055
	{
1056
		static $cache = null;
1057
1058
		if ($cache !== null && !$reload)
1059
		{
1060
			return $cache;
1061
		}
1062
1063
		$dbo = $this->getDbo();
1064
1065
		$query = $dbo->getQuery(true);
1066
1067
		$query->select('*');
1068
		$query->from('#__redcore_schemas');
1069
1070
		$assetName = $this->_tbl;
1071
		$query->where('asset_id = ' . $dbo->q($assetName));
1072
		$result = $dbo->setQuery($query)->loadAssoc();
1073
1074
		if (is_null($result))
1075
		{
1076
			$result = $this->createSchema($assetName);
1077
		}
1078
1079
		$cachedOn = new \JDate($result['cached_on']);
1080
		$now = new \JDate;
1081
1082
		if ($now->toUnix() > ($cachedOn->toUnix() + 86400))
1083
		{
1084
			$this->updateSchema($assetName, $now);
1085
		}
1086
1087
		// Decode the fields
1088
		$fields = (array) json_decode($result['fields']);
1089
1090
		if (empty($fields))
1091
		{
1092
			$msg = JText::sprintf('REDCORE_TABLE_ERROR_NO_COLUMNS_FOUND', $this->_tbl);
1093
1094
			throw new UnexpectedValueException($msg, 500);
1095
		}
1096
1097
		$cache = $fields;
1098
1099
		return $cache;
1100
	}
1101
1102
	/**
1103
	 * Method to cache the table schema in the logical schemas table
1104
	 *
1105
	 * @param   string  $assetName  the asset name of this table. standard format is "#__TableName"
1106
	 *
1107
	 * @return array
1108
	 */
1109
	private function createSchema($assetName)
1110
	{
1111
		$dbo = $this->getDbo();
1112
		$query = $dbo->getQuery(true);
1113
1114
		$query->insert('#__redcore_schemas');
1115
		$query->set('asset_id = ' . $dbo->q($assetName));
1116
1117
		$fields = json_encode($dbo->getTableColumns($this->_tbl, false));
1118
		$query->set('fields = ' . $dbo->q($fields));
1119
1120
		$now = new \JDate;
1121
		$query->set('cached_on = ' . $dbo->q($now->toSql()));
1122
1123
		$dbo->setQuery($query)->execute();
1124
1125
		return array('asset_id' => $assetName, 'fields' => $fields, 'cached_on' => $now->toSql());
1126
	}
1127
1128
	/**
1129
	 * Method to update the table schema in the logical schemas table
1130
	 *
1131
	 * @param   string  $assetName  the asset name of this table. standard format is "#__TableName"
1132
	 * @param   \JDate  $now        the current time
1133
	 *
1134
	 * @return array
1135
	 */
1136
	public function updateSchema($assetName = null, \JDate $now = null)
1137
	{
1138
		$assetName = $assetName ?: $this->_tbl;
1139
		$now = $now ?: \JFactory::getDate();
1140
1141
		$dbo = $this->getDbo();
1142
		$query = $dbo->getQuery(true);
1143
1144
		$query->update('#__redcore_schemas');
1145
1146
		$fields = json_encode($dbo->getTableColumns($assetName, false));
1147
		$query->set('fields = ' . $dbo->q($fields));
1148
		$query->set('cached_on = ' . $dbo->q($now->toSql()));
1149
1150
		$query->where('asset_id = ' . $dbo->q($assetName));
1151
1152
		$dbo->setQuery($query)->execute();
1153
1154
		return array('asset_id' => $assetName, 'fields' => $fields, 'cached_on' => $now->toSql());
1155
	}
1156
}
1157