PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.16
VikBooking Hotel Booking Engine & PMS v1.8.16
1.8.16 1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 All 37 releases
← All changes | libraries/adapter/database/table.php +958 -0 1.6.2 → 1.8.16 View file →
@@ -1,0 +1,958 @@
1 +<?php
2 +/**
3 + * @package VikWP - Libraries
4 + * @subpackage adapter.database
5 + * @author E4J s.r.l.
6 + * @copyright Copyright (C) 2023 E4J s.r.l. All Rights Reserved.
7 + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 + * @link https://vikwp.com
9 + */
10 +
11 +// No direct access
12 +defined('ABSPATH') or die('No script kiddies please!');
13 +
14 +/**
15 + * Generic table class for database objects.
16 + *
17 + * @since 10.1.19
18 + */
19 +class JTable extends JObject
20 +{
21 + /**
22 + * Include paths for searching for Table classes.
23 + *
24 + * @var array
25 + */
26 + private static $_includePaths = array();
27 +
28 + /**
29 + * Table fields cache to prevent the static changed applied by PHP 8.1.
30 + * @link https://bugs.php.net/bug.php?id=81686
31 + *
32 + * @var array
33 + * @since 10.1.41
34 + */
35 + protected static $_tableFields = [];
36 +
37 + /**
38 + * Name of the database table to model.
39 + *
40 + * @var string
41 + */
42 + protected $_table = '';
43 +
44 + /**
45 + * Name of the primary key fields in the table.
46 + *
47 + * @var array
48 + */
49 + protected $_tableKeys = array();
50 +
51 + /**
52 + * Indicates that the primary keys autoincrement.
53 + *
54 + * @var boolean
55 + */
56 + protected $_autoincrement = true;
57 +
58 + /**
59 + * Array with alias for "special" columns such as ordering, hits etc etc
60 + *
61 + * @var array
62 + * @since 10.1.30
63 + */
64 + protected $_columnAlias = array();
65 +
66 + /**
67 + * Static method to get an instance of a Table class if it
68 + * can be found in the table include paths.
69 + *
70 + * @param string $type The type (name) of the Table class to get an instance of.
71 + * @param string $prefix An optional prefix for the table class name.
72 + * @param array $config An optional array of configuration values for the Table object.
73 + *
74 + * @return mixed A Table object if found, false otherwise.
75 + *
76 + * @see JTable::addIncludePath() to add include paths for searching for Table classes.
77 + */
78 + public static function getInstance($type, $prefix = 'JTable', $config = array())
79 + {
80 + // sanitize and prepare the table class name
81 + $type = preg_replace('/[^A-Z0-9_\.-]/i', '', $type);
82 + $tableClass = $prefix . ucfirst($type);
83 +
84 + // only try to load the class if it doesn't already exist
85 + if (!class_exists($tableClass))
86 + {
87 + // search for the class file in the JTable include paths.
88 + $paths = static::addIncludePath();
89 + $pathIndex = 0;
90 +
91 + // iterate until the class is loaded
92 + while (!class_exists($tableClass) && $pathIndex < count($paths))
93 + {
94 + if ($tryThis = JPath::find($paths[$pathIndex++], strtolower($type) . '.php'))
95 + {
96 + // import the class file
97 + include_once $tryThis;
98 + }
99 + }
100 +
101 + if (!class_exists($tableClass))
102 + {
103 + throw new Exception(sprintf('Table [%s] not found', $tableClass), 404);
104 + }
105 + }
106 +
107 + // instantiate a new table class and return it
108 + return new $tableClass($type);
109 + }
110 +
111 + /**
112 + * Adds a filesystem path where Table should search for table class files.
113 + *
114 + * @param mixed $path A filesystem path or array of filesystem paths to add.
115 + *
116 + * @return array An array of filesystem paths to find Table classes in.
117 + */
118 + public static function addIncludePath($path = null)
119 + {
120 + // if the internal paths have not been initialised, do so with the base table path
121 + if (empty(self::$_includePaths))
122 + {
123 + self::$_includePaths[] = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'tables';
124 + }
125 +
126 + if ($path)
127 + {
128 + // convert the passed path(s) to add to an array.
129 + $path = (array) $path;
130 +
131 + // add each individual new path
132 + foreach ($path as $dir)
133 + {
134 + // sanitize path
135 + $dir = trim($dir);
136 +
137 + // add to the front of the list so that custom paths are searched first
138 + if (!in_array($dir, self::$_includePaths))
139 + {
140 + array_unshift(self::$_includePaths, $dir);
141 + }
142 + }
143 + }
144 +
145 + return self::$_includePaths;
146 + }
147 +
148 + /**
149 + * Object constructor to set table and key fields.
150 + * In most cases this will be overridden by child classes to explicitly
151 + * set the table and key fields for a particular database table.
152 + *
153 + * @param string $table Name of the table to model.
154 + * @param mixed $key Name of the primary key field in the table
155 + * or array of field names that compose the primary key.
156 + */
157 + public function __construct($table, $key = 'id')
158 + {
159 + // set internal variables
160 + $this->_table = $table;
161 +
162 + // set the key to be an array.
163 + if (is_string($key))
164 + {
165 + $key = array($key);
166 + }
167 + else if (is_object($key))
168 + {
169 + $key = (array) $key;
170 + }
171 +
172 + $this->_tableKeys = $key;
173 +
174 + if (count($key) == 1)
175 + {
176 + $this->_autoincrement = true;
177 + }
178 + else
179 + {
180 + $this->_autoincrement = false;
181 + }
182 +
183 + // initialise the table properties
184 + $fields = $this->getFields();
185 +
186 + if ($fields)
187 + {
188 + foreach ($fields as $name => $v)
189 + {
190 + if (is_int($name))
191 + {
192 + // use the value in case we have a linear array
193 + $name = $v;
194 + }
195 +
196 + // add the field if it is not already present
197 + if (!property_exists($this, $name))
198 + {
199 + $this->{$name} = null;
200 + }
201 + }
202 + }
203 + }
204 +
205 + /**
206 + * Gets the columns from database table.
207 + *
208 + * @param boolean $reload Flag to reload cache.
209 + *
210 + * @return mixed An array of the field names, or false if an error occurs.
211 + *
212 + * @throws Exception
213 + */
214 + public function getFields($reload = false)
215 + {
216 + $key = $this->getTableName();
217 +
218 + if (!isset(static::$_tableFields[$key]) || $reload)
219 + {
220 + $dbo = JFactory::getDbo();
221 +
222 + // lookup the fields for this table only once
223 + $fields = $dbo->getTableColumns($this->_table, false);
224 +
225 + if (empty($fields))
226 + {
227 + throw new Exception(sprintf('No columns found for [%s] table', $this->_table));
228 + }
229 +
230 + static::$_tableFields[$key] = $fields;
231 + }
232 +
233 + return static::$_tableFields[$key];
234 + }
235 +
236 + /**
237 + * Method to get the database table name for the class.
238 + *
239 + * @return string The name of the database table being modeled.
240 + *
241 + * @since 10.1.30
242 + */
243 + public function getTableName()
244 + {
245 + return $this->_table;
246 + }
247 +
248 + /**
249 + * Method to get the primary key field name for the table.
250 + *
251 + * @param boolean $multiple True to return all primary keys (as an array) or false to return
252 + * just the first one (as a string).
253 + *
254 + * @return mixed Array of primary key field names or string containing the first primary key field.
255 + *
256 + * @since 10.1.35
257 + */
258 + public function getKeyName($multiple = false)
259 + {
260 + // Count the number of keys
261 + if (count($this->_tableKeys))
262 + {
263 + if ($multiple)
264 + {
265 + // If we want multiple keys, return the raw array.
266 + return $this->_tableKeys;
267 + }
268 + else
269 + {
270 + // If we want the standard method, just return the first key.
271 + return $this->_tableKeys[0];
272 + }
273 + }
274 +
275 + return '';
276 + }
277 +
278 + /**
279 + * Validate that the primary key has been set.
280 + *
281 + * @return boolean True if the primary key(s) have been set.
282 + *
283 + * @since 10.1.30
284 + */
285 + public function hasPrimaryKey()
286 + {
287 + if ($this->_autoincrement)
288 + {
289 + $empty = true;
290 +
291 + foreach ($this->_tableKeys as $key)
292 + {
293 + $empty = $empty && empty($this->$key);
294 + }
295 + }
296 + else
297 + {
298 + $dbo = JFactory::getDbo();
299 +
300 + $q = $dbo->getQuery(true)
301 + ->select('COUNT(*)')
302 + ->from($this->_table);
303 +
304 + $this->appendPrimaryKeys($q);
305 +
306 + $dbo->setQuery($q);
307 +
308 + $count = $dbo->loadResult();
309 +
310 + if ($count == 1)
311 + {
312 + $empty = false;
313 + }
314 + else
315 + {
316 + $empty = true;
317 + }
318 + }
319 +
320 + return !$empty;
321 + }
322 +
323 + /**
324 + * Method to append the primary keys for this table to a query.
325 + *
326 + * @param mixed $query A query object to append.
327 + * @param mixed $pk Optional primary key parameter.
328 + *
329 + * @return void
330 + *
331 + * @since 10.1.30
332 + */
333 + public function appendPrimaryKeys($query, $pk = null)
334 + {
335 + $dbo = JFactory::getDbo();
336 +
337 + if (is_null($pk))
338 + {
339 + foreach ($this->_tableKeys as $k)
340 + {
341 + $query->where($dbo->qn($k) . ' = ' . $dbo->q($this->{$k}));
342 + }
343 + }
344 + else
345 + {
346 + if (is_string($pk))
347 + {
348 + $pk = array($this->_tableKeys[0] => $pk);
349 + }
350 +
351 + $pk = (object) $pk;
352 +
353 + foreach ($this->_tableKeys as $k)
354 + {
355 + $query->where($dbo->qn($k) . ' = ' . $dbo->q($pk->{$k}));
356 + }
357 + }
358 + }
359 +
360 + /**
361 + * Method to reset class properties to the defaults set in the class
362 + * definition. It will ignore the primary key as well as any private class
363 + * properties (except $_errors).
364 + *
365 + * @return void
366 + *
367 + * @since 10.1.30
368 + */
369 + public function reset()
370 + {
371 + // get the default values for the class from the table
372 + foreach ($this->getFields() as $k => $v)
373 + {
374 + // if the property is not the primary key or private, reset it
375 + if (!in_array($k, $this->_tableKeys) && (strpos($k, '_') !== 0))
376 + {
377 + $this->{$k} = $v->Default;
378 + }
379 + }
380 +
381 + // reset table errors
382 + $this->_errors = array();
383 + }
384 +
385 + /**
386 + * Method to bind an associative array or object to the Table instance.This
387 + * method only binds properties that are publicly accessible and optionally
388 + * takes an array of properties to ignore when binding.
389 + *
390 + * @param mixed $src An associative array or object to bind to the Table instance.
391 + * @param mixed $ignore An optional array or space separated list of properties to ignore while binding.
392 + *
393 + * @return boolean True on success.
394 + *
395 + * @since 10.1.30
396 + *
397 + * @throws InvalidArgumentException
398 + */
399 + public function bind($src, $ignore = array())
400 + {
401 + // check if the source value is an array or object
402 + if (!is_object($src) && !is_array($src))
403 + {
404 + throw new InvalidArgumentException(
405 + sprintf(
406 + 'Could not bind the data source in %s::bind(), the source must be an array or object but a "%s" was given.',
407 + get_class($this),
408 + gettype($src)
409 + )
410 + );
411 + }
412 +
413 + // if the source value is an object, get its accessible properties
414 + if (is_object($src))
415 + {
416 + $src = get_object_vars($src);
417 + }
418 +
419 + $ignore = (array) $ignore;
420 +
421 + // bind the source value, excluding the ignored fields
422 + foreach ($this->getProperties() as $k => $v)
423 + {
424 + // only process fields not in the ignore array
425 + if (!in_array($k, $ignore))
426 + {
427 + if (isset($src[$k]))
428 + {
429 + $this->{$k} = $src[$k];
430 + }
431 + }
432 + }
433 +
434 + return true;
435 + }
436 +
437 + /**
438 + * Method to perform sanity checks on the Table instance properties to ensure they are safe to store in the database.
439 + *
440 + * Child classes should override this method to make sure the data they are storing in the database is safe and as expected before storage.
441 + *
442 + * @return boolean True if the instance is sane and able to be stored in the database.
443 + *
444 + * @since 10.1.30
445 + */
446 + public function check()
447 + {
448 + // inherit in children classes
449 + return true;
450 + }
451 +
452 + /**
453 + * Method to store a row in the database from the Table instance properties.
454 + *
455 + * If a primary key value is set the row with that primary key value will be updated with the instance property values.
456 + * If no primary key value is set a new row will be inserted into the database with the properties from the Table instance.
457 + *
458 + * @param boolean $updateNulls True to update fields even if they are null.
459 + *
460 + * @return boolean True on success.
461 + *
462 + * @since 10.1.30
463 + */
464 + public function store($updateNulls = false)
465 + {
466 + $dbo = JFactory::getDbo();
467 + $event = JEventDispatcher::getInstance();
468 +
469 + $k = $this->_tableKeys;
470 +
471 + // pre-processing by observers
472 + $event->trigger('onBeforeStore', array($updateNulls, $k));
473 +
474 + // If a primary key exists update the object, otherwise insert it.
475 + if ($this->hasPrimaryKey())
476 + {
477 + $result = $dbo->updateObject($this->_table, $this, $this->_tableKeys, $updateNulls);
478 + }
479 + else
480 + {
481 + $result = $dbo->insertObject($this->_table, $this, $this->_tableKeys[0]);
482 + }
483 +
484 + /**
485 + * The table is now able to propagate the error message faced during
486 + * the database insert/update execution.
487 + *
488 + * @since 10.1.58
489 + */
490 + if (!$result)
491 + {
492 + // propagate error message
493 + $this->setError($dbo->getLastError() ?: 'Database query error.');
494 + }
495 +
496 + // post-processing by observers
497 + $event->trigger('onAfterStore', array(&$result));
498 +
499 + return $result;
500 + }
501 +
502 + /**
503 + * Method to provide a shortcut to binding, checking and storing a Table instance to the database table.
504 + *
505 + * The method will check a row in once the data has been stored and if an ordering filter is present will attempt to reorder
506 + * the table rows based on the filter. The ordering filter is an instance property name. The rows that will be reordered
507 + * are those whose value matches the Table instance for the property specified.
508 + *
509 + * @param mixed $src An associative array or object to bind to the Table instance.
510 + * @param string $orderingFilter Filter for the order updating
511 + * @param mixed $ignore An optional array or space separated list of properties to ignore while binding.
512 + *
513 + * @return boolean True on success.
514 + *
515 + * @since 10.1.30
516 + */
517 + public function save($src, $orderingFilter = '', $ignore = '')
518 + {
519 + // attempt to bind the source to the instance
520 + if (!$this->bind($src, $ignore))
521 + {
522 + return false;
523 + }
524 +
525 + // run any sanity checks on the instance and verify that it is ready for storage
526 + if (!$this->check())
527 + {
528 + return false;
529 + }
530 +
531 + // attempt to store the properties to the database table
532 + if (!$this->store())
533 + {
534 + return false;
535 + }
536 +
537 + // ff an ordering filter is set, attempt reorder the rows in the table based on the filter and value.
538 + if ($orderingFilter)
539 + {
540 + $dbo = JFactory::getDbo();
541 +
542 + $filterValue = $this->{$orderingFilter};
543 + $this->reorder($orderingFilter ? $dbo->qn($orderingFilter) . ' = ' . $dbo->q($filterValue) : '');
544 + }
545 +
546 + return true;
547 + }
548 +
549 + /**
550 + * Method to delete a row from the database table by primary key value.
551 + *
552 + * @param mixed $pk An optional primary key value to delete. If not set the instance property value is used.
553 + *
554 + * @return boolean True on success.
555 + *
556 + * @since 10.1.30
557 + *
558 + * @throws UnexpectedValueException
559 + */
560 + public function delete($pk = null)
561 + {
562 + if (is_null($pk))
563 + {
564 + $pk = array();
565 +
566 + foreach ($this->_tableKeys as $key)
567 + {
568 + $pk[$key] = $this->{$key};
569 + }
570 + }
571 + else if (!is_array($pk))
572 + {
573 + $pk = array($this->_tableKeys[0] => $pk);
574 + }
575 +
576 + foreach ($this->_tableKeys as $key)
577 + {
578 + $pk[$key] = is_null($pk[$key]) ? $this->{$key} : $pk[$key];
579 +
580 + if ($pk[$key] === null)
581 + {
582 + throw new UnexpectedValueException('Null primary key not allowed.');
583 + }
584 +
585 + $this->{$key} = $pk[$key];
586 + }
587 +
588 + $dbo = JFactory::getDbo();
589 + $event = JEventDispatcher::getInstance();
590 +
591 + // pre-processing by observers
592 + $event->trigger('onBeforeDelete', array($pk));
593 +
594 + // delete the row by primary key
595 + $q = $dbo->getQuery(true)
596 + ->delete($this->_table);
597 +
598 + $this->appendPrimaryKeys($q, $pk);
599 +
600 + $dbo->setQuery($q);
601 +
602 + // check for a database error
603 + $dbo->execute();
604 +
605 + // post-processing by observers
606 + $event->trigger('onAfterDelete', array($pk));
607 +
608 + return true;
609 + }
610 +
611 + /**
612 + * Method to get the next ordering value for a group of rows defined by an SQL WHERE clause.
613 + *
614 + * This is useful for placing a new item last in a group of items in the table.
615 + *
616 + * @param string $where WHERE clause to use for selecting the MAX(ordering) for the table.
617 + *
618 + * @return integer The next ordering value.
619 + *
620 + * @since 10.1.30
621 + *
622 + * @throws UnexpectedValueException
623 + */
624 + public function getNextOrder($where = '')
625 + {
626 + // check if there is an ordering field set
627 + $orderingField = $this->getColumnAlias('ordering');
628 +
629 + if (!property_exists($this, $orderingField))
630 + {
631 + throw new UnexpectedValueException(sprintf('%s does not support ordering.', get_class($this)));
632 + }
633 +
634 + $dbo = JFactory::getDbo();
635 +
636 + // get the largest ordering value for a given where clause
637 + $q = $dbo->getQuery(true)
638 + ->select('MAX(' . $dbo->qn($orderingField) . ')')
639 + ->from($this->_table);
640 +
641 + if ($where)
642 + {
643 + $q->where($where);
644 + }
645 +
646 + $dbo->setQuery($q);
647 + $max = (int) $dbo->loadResult();
648 +
649 + // return the largest ordering value + 1
650 + return $max + 1;
651 + }
652 +
653 + /**
654 + * Method to compact the ordering values of rows in a group of rows defined by an SQL WHERE clause.
655 + *
656 + * @param string $where WHERE clause to use for limiting the selection of rows to compact the ordering values.
657 + *
658 + * @return mixed Boolean True on success.
659 + *
660 + * @since 10.1.30
661 + *
662 + * @throws UnexpectedValueException
663 + */
664 + public function reorder($where = '')
665 + {
666 + // check if there is an ordering field set
667 + $orderingField = $this->getColumnAlias('ordering');
668 +
669 + if (!property_exists($this, $orderingField))
670 + {
671 + throw new UnexpectedValueException(sprintf('%s does not support ordering.', get_class($this)));
672 + }
673 +
674 + $dbo = JFactory::getDbo();
675 +
676 + $quotedOrderingField = $dbo->qn($orderingField);
677 +
678 + $subquery = $dbo->getQuery(true)
679 + ->from($this->_table)
680 + ->selectRowNumber($quotedOrderingField, 'new_ordering');
681 +
682 + $query = $dbo->getQuery(true)
683 + ->update($this->_table)
684 + ->set($quotedOrderingField . ' = sq.new_ordering');
685 +
686 + $innerOn = array();
687 +
688 + // get the primary keys for the selection
689 + foreach ($this->_tableKeys as $i => $k)
690 + {
691 + $subquery->select($dbo->qn($k, 'pk__' . $i));
692 + $innerOn[] = $dbo->qn($k) . ' = sq.' . $dbo->qn('pk__' . $i);
693 + }
694 +
695 + // setup the extra where and ordering clause data
696 + if ($where)
697 + {
698 + $subquery->where($where);
699 + $query->where($where);
700 + }
701 +
702 + $subquery->where($quotedOrderingField . ' >= 0');
703 + $query->where($quotedOrderingField . ' >= 0');
704 +
705 + $query->innerJoin('(' . (string) $subquery . ') AS sq ON ' . implode(' AND ', $innerOn));
706 +
707 + $dbo->setQuery($query);
708 + $dbo->execute();
709 + }
710 +
711 + /**
712 + * Method to set the publishing state for a row or list of rows in the database table.
713 + *
714 + * The method respects checked out rows by other users and will attempt to checkin rows that it can after adjustments are made.
715 + *
716 + * @param mixed $pks An optional array of primary key values to update. If not set the instance property value is used.
717 + * @param integer $state The publishing state. eg. [0 = unpublished, 1 = published]
718 + * @param integer $userId The user ID of the user performing the operation.
719 + *
720 + * @return boolean True on success; false if $pks is empty.
721 + *
722 + * @since 10.1.30
723 + */
724 + public function publish($pks = null, $state = 1, $userId = 0)
725 + {
726 + // sanitize input
727 + $userId = (int) $userId;
728 + $state = (int) $state;
729 +
730 + if (!is_null($pks))
731 + {
732 + if (!is_array($pks))
733 + {
734 + $pks = array($pks);
735 + }
736 +
737 + foreach ($pks as $key => $pk)
738 + {
739 + if (!is_array($pk))
740 + {
741 + $pks[$key] = array($this->_tableKeys[0] => $pk);
742 + }
743 + }
744 + }
745 +
746 + // if there are no primary keys set check to see if the instance key is set
747 + if (empty($pks))
748 + {
749 + $pk = array();
750 +
751 + foreach ($this->_tableKeys as $key)
752 + {
753 + if ($this->{$key})
754 + {
755 + $pk[$key] = $this->{$key};
756 + }
757 + // we don't have a full primary key - return false
758 + else
759 + {
760 + $this->setError('JLIB_DATABASE_ERROR_NO_ROWS_SELECTED');
761 +
762 + return false;
763 + }
764 + }
765 +
766 + $pks = array($pk);
767 + }
768 +
769 + $dbo = JFactory::getDbo();
770 +
771 + $publishedField = $this->getColumnAlias('published');
772 +
773 + foreach ($pks as $pk)
774 + {
775 + // update the publishing state for rows with the given primary keys
776 + $query = $dbo->getQuery(true)
777 + ->update($this->_table)
778 + ->set($dbo->qn($publishedField) . ' = ' . (int) $state);
779 +
780 + // build the WHERE clause for the primary keys
781 + $this->appendPrimaryKeys($query, $pk);
782 +
783 + $dbo->setQuery($query);
784 +
785 + try
786 + {
787 + $dbo->execute();
788 + }
789 + catch (RuntimeException $e)
790 + {
791 + $this->setError($e->getMessage());
792 +
793 + return false;
794 + }
795 +
796 + // if the Table instance value is in the list of primary keys that were set, set the instance
797 + $ours = true;
798 +
799 + foreach ($this->_tableKeys as $key)
800 + {
801 + if ($this->{$key} != $pk[$key])
802 + {
803 + $ours = false;
804 + }
805 + }
806 +
807 + if ($ours)
808 + {
809 + $this->{$publishedField} = $state;
810 + }
811 + }
812 +
813 + return true;
814 + }
815 +
816 + /**
817 + * Method to return the real name of a "special" column such as ordering, hits, published
818 + * etc etc. In this way you are free to follow your db naming convention and use the
819 + * built in \Joomla functions.
820 + *
821 + * @param string $column Name of the "special" column (ie ordering, hits).
822 + *
823 + * @return string The string that identify the special.
824 + *
825 + * @since 10.1.30
826 + */
827 + public function getColumnAlias($column)
828 + {
829 + // get the column data if set
830 + if (isset($this->_columnAlias[$column]))
831 + {
832 + $return = $this->_columnAlias[$column];
833 + }
834 + else
835 + {
836 + $return = $column;
837 + }
838 +
839 + // sanitize the name
840 + $return = preg_replace('#[^A-Z0-9_]#i', '', $return);
841 +
842 + return $return;
843 + }
844 +
845 + /**
846 + * Method to register a column alias for a "special" column.
847 + *
848 + * @param string $column The "special" column (i.e. ordering).
849 + * @param string $columnAlias The real column name (i.e. foo_ordering).
850 + *
851 + * @return void
852 + *
853 + * @since 10.1.30
854 + */
855 + public function setColumnAlias($column, $columnAlias)
856 + {
857 + // santize the column name alias
858 + $column = strtolower($column);
859 + $column = preg_replace('#[^A-Z0-9_]#i', '', $column);
860 +
861 + // set the column alias internally
862 + $this->_columnAlias[$column] = $columnAlias;
863 + }
864 +
865 + /**
866 + * Method to load a row from the database by primary key and bind the fields to the Table instance properties.
867 + *
868 + * @param mixed $keys An optional primary key value to load the row by, or an array of fields to match.
869 + * If not set the instance property value is used.
870 + * @param boolean $reset True to reset the default values before loading the new row.
871 + *
872 + * @return boolean True if successful. False if row not found.
873 + *
874 + * @since 10.1.35
875 + */
876 + public function load($keys = null, $reset = true)
877 + {
878 + if (empty($keys))
879 + {
880 + $empty = true;
881 + $keys = array();
882 +
883 + // If empty, use the value of the current key
884 + foreach ($this->_tableKeys as $key)
885 + {
886 + $empty = $empty && empty($this->$key);
887 + $keys[$key] = $this->$key;
888 + }
889 +
890 + // If empty primary key there's is no need to load anything
891 + if ($empty)
892 + {
893 + return true;
894 + }
895 + }
896 + else if (!is_array($keys))
897 + {
898 + // Load by primary key.
899 + $keyCount = count($this->_tableKeys);
900 +
901 + if ($keyCount)
902 + {
903 + if ($keyCount > 1)
904 + {
905 + throw new InvalidArgumentException('Table has multiple primary keys specified, only one primary key value provided.');
906 + }
907 +
908 + $keys = array($this->getKeyName() => $keys);
909 + }
910 + else
911 + {
912 + throw new RuntimeException('No table keys defined.');
913 + }
914 + }
915 +
916 + if ($reset)
917 + {
918 + $this->reset();
919 + }
920 +
921 + $dbo = JFactory::getDbo();
922 +
923 + // Initialise the query.
924 + $query = $dbo->getQuery(true)
925 + ->select('*')
926 + ->from($this->_table);
927 + $fields = array_keys($this->getProperties());
928 +
929 + foreach ($keys as $field => $value)
930 + {
931 + // Check that $field is in the table.
932 + if (!in_array($field, $fields))
933 + {
934 + throw new UnexpectedValueException(sprintf('Missing field in database: %s &#160; %s.', get_class($this), $field));
935 + }
936 +
937 + // Add the search tuple to the query.
938 + $query->where($dbo->quoteName($field) . ' = ' . $dbo->quote($value));
939 + }
940 +
941 + $dbo->setQuery($query);
942 +
943 + $row = $dbo->loadAssoc();
944 +
945 + // check that we have a result
946 + if (empty($row))
947 + {
948 + $result = false;
949 + }
950 + else
951 + {
952 + // Bind the object with the row and return.
953 + $result = $this->bind($row);
954 + }
955 +
956 + return $result;
957 + }
958 +}