PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / trunk
VikAppointments Services Booking Calendar vtrunk
trunk 1.2.17 1.2.18 1.2.19
vikappointments / libraries / adapter / database / table.php
vikappointments / libraries / adapter / database Last commit date
query 2 years ago tables 2 years ago database.php 1 year ago helper.php 2 years ago query.php 5 months ago table.php 1 year ago
table.php
959 lines
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 }
959