PluginProbe
VikBooking Hotel Booking Engine & PMS / trunk
VikBooking Hotel Booking Engine & PMS vtrunk
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 1.7.3 All 36 releases
vikbooking / libraries / adapter / database / table.php

table.php in VikBooking Hotel Booking Engine & PMS trunk, at libraries/adapter/database/table.php

959 lines 22.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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