| [ Index ] |
PHP Cross Reference of Joomla 2.5.4 DE |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * @package Joomla.Platform 4 * @subpackage Database 5 * 6 * @copyright Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved. 7 * @license GNU General Public License version 2 or later; see LICENSE 8 */ 9 10 defined('JPATH_PLATFORM') or die; 11 12 /** 13 * Abstract Table class 14 * 15 * Parent class to all tables. 16 * 17 * @package Joomla.Platform 18 * @subpackage Table 19 * @link http://docs.joomla.org/JTable 20 * @since 11.1 21 * @tutorial Joomla.Platform/jtable.cls 22 */ 23 abstract class JTable extends JObject 24 { 25 /** 26 * Name of the database table to model. 27 * 28 * @var string 29 * @since 11.1 30 */ 31 protected $_tbl = ''; 32 33 /** 34 * Name of the primary key field in the table. 35 * 36 * @var string 37 * @since 11.1 38 */ 39 protected $_tbl_key = ''; 40 41 /** 42 * JDatabase connector object. 43 * 44 * @var JDatabase 45 * @since 11.1 46 */ 47 protected $_db; 48 49 /** 50 * Should rows be tracked as ACL assets? 51 * 52 * @var boolean 53 * @since 11.1 54 */ 55 protected $_trackAssets = false; 56 57 /** 58 * The rules associated with this record. 59 * 60 * @var JAccessRules A JAccessRules object. 61 * @since 11.1 62 */ 63 protected $_rules; 64 65 /** 66 * Indicator that the tables have been locked. 67 * 68 * @var boolean 69 * @since 11.1 70 */ 71 protected $_locked = false; 72 73 /** 74 * Object constructor to set table and key fields. In most cases this will 75 * be overridden by child classes to explicitly set the table and key fields 76 * for a particular database table. 77 * 78 * @param string $table Name of the table to model. 79 * @param string $key Name of the primary key field in the table. 80 * @param JDatabase &$db JDatabase connector object. 81 * 82 * @since 11.1 83 */ 84 public function __construct($table, $key, &$db) 85 { 86 // Set internal variables. 87 $this->_tbl = $table; 88 $this->_tbl_key = $key; 89 $this->_db = &$db; 90 91 // Initialise the table properties. 92 if ($fields = $this->getFields()) 93 { 94 foreach ($fields as $name => $v) 95 { 96 // Add the field if it is not already present. 97 if (!property_exists($this, $name)) 98 { 99 $this->$name = null; 100 } 101 } 102 } 103 104 // If we are tracking assets, make sure an access field exists and initially set the default. 105 if (property_exists($this, 'asset_id')) 106 { 107 $this->_trackAssets = true; 108 } 109 110 // If the access property exists, set the default. 111 if (property_exists($this, 'access')) 112 { 113 $this->access = (int) JFactory::getConfig()->get('access'); 114 } 115 } 116 117 /** 118 * Get the columns from database table. 119 * 120 * @return mixed An array of the field names, or false if an error occurs. 121 * 122 * @since 11.1 123 */ 124 public function getFields() 125 { 126 static $cache = null; 127 128 if ($cache === null) 129 { 130 // Lookup the fields for this table only once. 131 $name = $this->_tbl; 132 $fields = $this->_db->getTableColumns($name, false); 133 134 if (empty($fields)) 135 { 136 $e = new JException(JText::_('JLIB_DATABASE_ERROR_COLUMNS_NOT_FOUND')); 137 $this->setError($e); 138 return false; 139 } 140 $cache = $fields; 141 } 142 143 return $cache; 144 } 145 146 /** 147 * Static method to get an instance of a JTable class if it can be found in 148 * the table include paths. To add include paths for searching for JTable 149 * classes @see JTable::addIncludePath(). 150 * 151 * @param string $type The type (name) of the JTable class to get an instance of. 152 * @param string $prefix An optional prefix for the table class name. 153 * @param array $config An optional array of configuration values for the JTable object. 154 * 155 * @return mixed A JTable object if found or boolean false if one could not be found. 156 * 157 * @link http://docs.joomla.org/JTable/getInstance 158 * @since 11.1 159 */ 160 public static function getInstance($type, $prefix = 'JTable', $config = array()) 161 { 162 // Sanitize and prepare the table class name. 163 $type = preg_replace('/[^A-Z0-9_\.-]/i', '', $type); 164 $tableClass = $prefix . ucfirst($type); 165 166 // Only try to load the class if it doesn't already exist. 167 if (!class_exists($tableClass)) 168 { 169 // Search for the class file in the JTable include paths. 170 jimport('joomla.filesystem.path'); 171 172 if ($path = JPath::find(JTable::addIncludePath(), strtolower($type) . '.php')) 173 { 174 // Import the class file. 175 include_once $path; 176 177 // If we were unable to load the proper class, raise a warning and return false. 178 if (!class_exists($tableClass)) 179 { 180 JError::raiseWarning(0, JText::sprintf('JLIB_DATABASE_ERROR_CLASS_NOT_FOUND_IN_FILE', $tableClass)); 181 return false; 182 } 183 } 184 else 185 { 186 // If we were unable to find the class file in the JTable include paths, raise a warning and return false. 187 JError::raiseWarning(0, JText::sprintf('JLIB_DATABASE_ERROR_NOT_SUPPORTED_FILE_NOT_FOUND', $type)); 188 return false; 189 } 190 } 191 192 // If a database object was passed in the configuration array use it, otherwise get the global one from JFactory. 193 $db = isset($config['dbo']) ? $config['dbo'] : JFactory::getDbo(); 194 195 // Instantiate a new table class and return it. 196 return new $tableClass($db); 197 } 198 199 /** 200 * Add a filesystem path where JTable should search for table class files. 201 * You may either pass a string or an array of paths. 202 * 203 * @param mixed $path A filesystem path or array of filesystem paths to add. 204 * 205 * @return array An array of filesystem paths to find JTable classes in. 206 * 207 * @link http://docs.joomla.org/JTable/addIncludePath 208 * @since 11.1 209 */ 210 public static function addIncludePath($path = null) 211 { 212 // Declare the internal paths as a static variable. 213 static $_paths; 214 215 // If the internal paths have not been initialised, do so with the base table path. 216 if (!isset($_paths)) 217 { 218 $_paths = array(dirname(__FILE__) . '/table'); 219 } 220 221 // Convert the passed path(s) to add to an array. 222 settype($path, 'array'); 223 224 // If we have new paths to add, do so. 225 if (!empty($path) && !in_array($path, $_paths)) 226 { 227 // Check and add each individual new path. 228 foreach ($path as $dir) 229 { 230 // Sanitize path. 231 $dir = trim($dir); 232 233 // Add to the front of the list so that custom paths are searched first. 234 array_unshift($_paths, $dir); 235 } 236 } 237 238 return $_paths; 239 } 240 241 /** 242 * Method to compute the default name of the asset. 243 * The default name is in the form table_name.id 244 * where id is the value of the primary key of the table. 245 * 246 * @return string 247 * 248 * @since 11.1 249 */ 250 protected function _getAssetName() 251 { 252 $k = $this->_tbl_key; 253 return $this->_tbl . '.' . (int) $this->$k; 254 } 255 256 /** 257 * Method to return the title to use for the asset table. In 258 * tracking the assets a title is kept for each asset so that there is some 259 * context available in a unified access manager. Usually this would just 260 * return $this->title or $this->name or whatever is being used for the 261 * primary name of the row. If this method is not overridden, the asset name is used. 262 * 263 * @return string The string to use as the title in the asset table. 264 * 265 * @link http://docs.joomla.org/JTable/getAssetTitle 266 * @since 11.1 267 */ 268 protected function _getAssetTitle() 269 { 270 return $this->_getAssetName(); 271 } 272 273 /** 274 * Method to get the parent asset under which to register this one. 275 * By default, all assets are registered to the ROOT node with ID 1. 276 * The extended class can define a table and id to lookup. If the 277 * asset does not exist it will be created. 278 * 279 * @param JTable $table A JTable object for the asset parent. 280 * @param integer $id Id to look up 281 * 282 * @return integer 283 * 284 * @since 11.1 285 */ 286 protected function _getAssetParentId($table = null, $id = null) 287 { 288 // For simple cases, parent to the asset root. 289 if (empty($table) || empty($id)) 290 { 291 return 1; 292 } 293 294 return 1; 295 } 296 297 /** 298 * Method to get the database table name for the class. 299 * 300 * @return string The name of the database table being modeled. 301 * 302 * @since 11.1 303 * 304 * @link http://docs.joomla.org/JTable/getTableName 305 */ 306 public function getTableName() 307 { 308 return $this->_tbl; 309 } 310 311 /** 312 * Method to get the primary key field name for the table. 313 * 314 * @return string The name of the primary key for the table. 315 * 316 * @link http://docs.joomla.org/JTable/getKeyName 317 * @since 11.1 318 */ 319 public function getKeyName() 320 { 321 return $this->_tbl_key; 322 } 323 324 /** 325 * Method to get the JDatabase connector object. 326 * 327 * @return JDatabase The internal database connector object. 328 * 329 * @link http://docs.joomla.org/JTable/getDBO 330 * @since 11.1 331 */ 332 public function getDbo() 333 { 334 return $this->_db; 335 } 336 337 /** 338 * Method to set the JDatabase connector object. 339 * 340 * @param object &$db A JDatabase connector object to be used by the table object. 341 * 342 * @return boolean True on success. 343 * 344 * @link http://docs.joomla.org/JTable/setDBO 345 * @since 11.1 346 */ 347 public function setDBO(&$db) 348 { 349 // Make sure the new database object is a JDatabase. 350 if (!($db instanceof JDatabase)) 351 { 352 return false; 353 } 354 355 $this->_db = &$db; 356 357 return true; 358 } 359 360 /** 361 * Method to set rules for the record. 362 * 363 * @param mixed $input A JAccessRules object, JSON string, or array. 364 * 365 * @return void 366 * 367 * @since 11.1 368 */ 369 public function setRules($input) 370 { 371 if ($input instanceof JAccessRules) 372 { 373 $this->_rules = $input; 374 } 375 else 376 { 377 $this->_rules = new JAccessRules($input); 378 } 379 } 380 381 /** 382 * Method to get the rules for the record. 383 * 384 * @return JAccessRules object 385 * 386 * @since 11.1 387 */ 388 public function getRules() 389 { 390 return $this->_rules; 391 } 392 393 /** 394 * Method to reset class properties to the defaults set in the class 395 * definition. It will ignore the primary key as well as any private class 396 * properties. 397 * 398 * @return void 399 * 400 * @link http://docs.joomla.org/JTable/reset 401 * @since 11.1 402 */ 403 public function reset() 404 { 405 // Get the default values for the class from the table. 406 foreach ($this->getFields() as $k => $v) 407 { 408 // If the property is not the primary key or private, reset it. 409 if ($k != $this->_tbl_key && (strpos($k, '_') !== 0)) 410 { 411 $this->$k = $v->Default; 412 } 413 } 414 } 415 416 /** 417 * Method to bind an associative array or object to the JTable instance.This 418 * method only binds properties that are publicly accessible and optionally 419 * takes an array of properties to ignore when binding. 420 * 421 * @param mixed $src An associative array or object to bind to the JTable instance. 422 * @param mixed $ignore An optional array or space separated list of properties to ignore while binding. 423 * 424 * @return boolean True on success. 425 * 426 * @link http://docs.joomla.org/JTable/bind 427 * @since 11.1 428 */ 429 public function bind($src, $ignore = array()) 430 { 431 // If the source value is not an array or object return false. 432 if (!is_object($src) && !is_array($src)) 433 { 434 $e = new JException(JText::sprintf('JLIB_DATABASE_ERROR_BIND_FAILED_INVALID_SOURCE_ARGUMENT', get_class($this))); 435 $this->setError($e); 436 return false; 437 } 438 439 // If the source value is an object, get its accessible properties. 440 if (is_object($src)) 441 { 442 $src = get_object_vars($src); 443 } 444 445 // If the ignore value is a string, explode it over spaces. 446 if (!is_array($ignore)) 447 { 448 $ignore = explode(' ', $ignore); 449 } 450 451 // Bind the source value, excluding the ignored fields. 452 foreach ($this->getProperties() as $k => $v) 453 { 454 // Only process fields not in the ignore array. 455 if (!in_array($k, $ignore)) 456 { 457 if (isset($src[$k])) 458 { 459 $this->$k = $src[$k]; 460 } 461 } 462 } 463 464 return true; 465 } 466 467 /** 468 * Method to load a row from the database by primary key and bind the fields 469 * to the JTable instance properties. 470 * 471 * @param mixed $keys An optional primary key value to load the row by, or an array of fields to match. If not 472 * set the instance property value is used. 473 * @param boolean $reset True to reset the default values before loading the new row. 474 * 475 * @return boolean True if successful. False if row not found or on error (internal error state set in that case). 476 * 477 * @link http://docs.joomla.org/JTable/load 478 * @since 11.1 479 */ 480 public function load($keys = null, $reset = true) 481 { 482 if (empty($keys)) 483 { 484 // If empty, use the value of the current key 485 $keyName = $this->_tbl_key; 486 $keyValue = $this->$keyName; 487 488 // If empty primary key there's is no need to load anything 489 if (empty($keyValue)) 490 { 491 return true; 492 } 493 494 $keys = array($keyName => $keyValue); 495 } 496 elseif (!is_array($keys)) 497 { 498 // Load by primary key. 499 $keys = array($this->_tbl_key => $keys); 500 } 501 502 if ($reset) 503 { 504 $this->reset(); 505 } 506 507 // Initialise the query. 508 $query = $this->_db->getQuery(true); 509 $query->select('*'); 510 $query->from($this->_tbl); 511 $fields = array_keys($this->getProperties()); 512 513 foreach ($keys as $field => $value) 514 { 515 // Check that $field is in the table. 516 if (!in_array($field, $fields)) 517 { 518 $e = new JException(JText::sprintf('JLIB_DATABASE_ERROR_CLASS_IS_MISSING_FIELD', get_class($this), $field)); 519 $this->setError($e); 520 return false; 521 } 522 // Add the search tuple to the query. 523 $query->where($this->_db->quoteName($field) . ' = ' . $this->_db->quote($value)); 524 } 525 526 $this->_db->setQuery($query); 527 528 try 529 { 530 $row = $this->_db->loadAssoc(); 531 } 532 catch (JDatabaseException $e) 533 { 534 $je = new JException($e->getMessage()); 535 $this->setError($je); 536 return false; 537 } 538 539 // Legacy error handling switch based on the JError::$legacy switch. 540 // @deprecated 12.1 541 if (JError::$legacy && $this->_db->getErrorNum()) 542 { 543 $e = new JException($this->_db->getErrorMsg()); 544 $this->setError($e); 545 return false; 546 } 547 548 // Check that we have a result. 549 if (empty($row)) 550 { 551 $e = new JException(JText::_('JLIB_DATABASE_ERROR_EMPTY_ROW_RETURNED')); 552 $this->setError($e); 553 return false; 554 } 555 556 // Bind the object with the row and return. 557 return $this->bind($row); 558 } 559 560 /** 561 * Method to perform sanity checks on the JTable instance properties to ensure 562 * they are safe to store in the database. Child classes should override this 563 * method to make sure the data they are storing in the database is safe and 564 * as expected before storage. 565 * 566 * @return boolean True if the instance is sane and able to be stored in the database. 567 * 568 * @link http://docs.joomla.org/JTable/check 569 * @since 11.1 570 */ 571 public function check() 572 { 573 return true; 574 } 575 576 /** 577 * Method to store a row in the database from the JTable instance properties. 578 * If a primary key value is set the row with that primary key value will be 579 * updated with the instance property values. If no primary key value is set 580 * a new row will be inserted into the database with the properties from the 581 * JTable instance. 582 * 583 * @param boolean $updateNulls True to update fields even if they are null. 584 * 585 * @return boolean True on success. 586 * 587 * @link http://docs.joomla.org/JTable/store 588 * @since 11.1 589 */ 590 public function store($updateNulls = false) 591 { 592 // Initialise variables. 593 $k = $this->_tbl_key; 594 595 // The asset id field is managed privately by this class. 596 if ($this->_trackAssets) 597 { 598 unset($this->asset_id); 599 } 600 601 // If a primary key exists update the object, otherwise insert it. 602 if ($this->$k) 603 { 604 $stored = $this->_db->updateObject($this->_tbl, $this, $this->_tbl_key, $updateNulls); 605 } 606 else 607 { 608 $stored = $this->_db->insertObject($this->_tbl, $this, $this->_tbl_key); 609 } 610 611 // If the store failed return false. 612 if (!$stored) 613 { 614 $e = new JException(JText::sprintf('JLIB_DATABASE_ERROR_STORE_FAILED', get_class($this), $this->_db->getErrorMsg())); 615 $this->setError($e); 616 return false; 617 } 618 619 // If the table is not set to track assets return true. 620 if (!$this->_trackAssets) 621 { 622 return true; 623 } 624 625 if ($this->_locked) 626 { 627 $this->_unlock(); 628 } 629 630 // 631 // Asset Tracking 632 // 633 634 $parentId = $this->_getAssetParentId(); 635 $name = $this->_getAssetName(); 636 $title = $this->_getAssetTitle(); 637 638 $asset = JTable::getInstance('Asset', 'JTable', array('dbo' => $this->getDbo())); 639 $asset->loadByName($name); 640 641 // Re-inject the asset id. 642 $this->asset_id = $asset->id; 643 644 // Check for an error. 645 if ($error = $asset->getError()) 646 { 647 $this->setError($error); 648 return false; 649 } 650 651 // Specify how a new or moved node asset is inserted into the tree. 652 if (empty($this->asset_id) || $asset->parent_id != $parentId) 653 { 654 $asset->setLocation($parentId, 'last-child'); 655 } 656 657 // Prepare the asset to be stored. 658 $asset->parent_id = $parentId; 659 $asset->name = $name; 660 $asset->title = $title; 661 662 if ($this->_rules instanceof JAccessRules) 663 { 664 $asset->rules = (string) $this->_rules; 665 } 666 667 if (!$asset->check() || !$asset->store($updateNulls)) 668 { 669 $this->setError($asset->getError()); 670 return false; 671 } 672 673 if (empty($this->asset_id)) 674 { 675 // Update the asset_id field in this table. 676 $this->asset_id = (int) $asset->id; 677 678 $query = $this->_db->getQuery(true); 679 $query->update($this->_db->quoteName($this->_tbl)); 680 $query->set('asset_id = ' . (int) $this->asset_id); 681 $query->where($this->_db->quoteName($k) . ' = ' . (int) $this->$k); 682 $this->_db->setQuery($query); 683 684 if (!$this->_db->query()) 685 { 686 $e = new JException(JText::sprintf('JLIB_DATABASE_ERROR_STORE_FAILED_UPDATE_ASSET_ID', $this->_db->getErrorMsg())); 687 $this->setError($e); 688 return false; 689 } 690 } 691 692 return true; 693 } 694 695 /** 696 * Method to provide a shortcut to binding, checking and storing a JTable 697 * instance to the database table. The method will check a row in once the 698 * data has been stored and if an ordering filter is present will attempt to 699 * reorder the table rows based on the filter. The ordering filter is an instance 700 * property name. The rows that will be reordered are those whose value matches 701 * the JTable instance for the property specified. 702 * 703 * @param mixed $src An associative array or object to bind to the JTable instance. 704 * @param string $orderingFilter Filter for the order updating 705 * @param mixed $ignore An optional array or space separated list of properties 706 * to ignore while binding. 707 * 708 * @return boolean True on success. 709 * 710 * @link http://docs.joomla.org/JTable/save 711 * @since 11.1 712 */ 713 public function save($src, $orderingFilter = '', $ignore = '') 714 { 715 // Attempt to bind the source to the instance. 716 if (!$this->bind($src, $ignore)) 717 { 718 return false; 719 } 720 721 // Run any sanity checks on the instance and verify that it is ready for storage. 722 if (!$this->check()) 723 { 724 return false; 725 } 726 727 // Attempt to store the properties to the database table. 728 if (!$this->store()) 729 { 730 return false; 731 } 732 733 // Attempt to check the row in, just in case it was checked out. 734 if (!$this->checkin()) 735 { 736 return false; 737 } 738 739 // If an ordering filter is set, attempt reorder the rows in the table based on the filter and value. 740 if ($orderingFilter) 741 { 742 $filterValue = $this->$orderingFilter; 743 $this->reorder($orderingFilter ? $this->_db->quoteName($orderingFilter) . ' = ' . $this->_db->Quote($filterValue) : ''); 744 } 745 746 // Set the error to empty and return true. 747 $this->setError(''); 748 749 return true; 750 } 751 752 /** 753 * Method to delete a row from the database table by primary key value. 754 * 755 * @param mixed $pk An optional primary key value to delete. If not set the instance property value is used. 756 * 757 * @return boolean True on success. 758 * 759 * @link http://docs.joomla.org/JTable/delete 760 * @since 11.1 761 */ 762 public function delete($pk = null) 763 { 764 // Initialise variables. 765 $k = $this->_tbl_key; 766 $pk = (is_null($pk)) ? $this->$k : $pk; 767 768 // If no primary key is given, return false. 769 if ($pk === null) 770 { 771 $e = new JException(JText::_('JLIB_DATABASE_ERROR_NULL_PRIMARY_KEY')); 772 $this->setError($e); 773 return false; 774 } 775 776 // If tracking assets, remove the asset first. 777 if ($this->_trackAssets) 778 { 779 // Get and the asset name. 780 $this->$k = $pk; 781 $name = $this->_getAssetName(); 782 $asset = JTable::getInstance('Asset'); 783 784 if ($asset->loadByName($name)) 785 { 786 if (!$asset->delete()) 787 { 788 $this->setError($asset->getError()); 789 return false; 790 } 791 } 792 else 793 { 794 $this->setError($asset->getError()); 795 return false; 796 } 797 } 798 799 // Delete the row by primary key. 800 $query = $this->_db->getQuery(true); 801 $query->delete(); 802 $query->from($this->_tbl); 803 $query->where($this->_tbl_key . ' = ' . $this->_db->quote($pk)); 804 $this->_db->setQuery($query); 805 806 // Check for a database error. 807 if (!$this->_db->query()) 808 { 809 $e = new JException(JText::sprintf('JLIB_DATABASE_ERROR_DELETE_FAILED', get_class($this), $this->_db->getErrorMsg())); 810 $this->setError($e); 811 return false; 812 } 813 814 return true; 815 } 816 817 /** 818 * Method to check a row out if the necessary properties/fields exist. To 819 * prevent race conditions while editing rows in a database, a row can be 820 * checked out if the fields 'checked_out' and 'checked_out_time' are available. 821 * While a row is checked out, any attempt to store the row by a user other 822 * than the one who checked the row out should be held until the row is checked 823 * in again. 824 * 825 * @param integer $userId The Id of the user checking out the row. 826 * @param mixed $pk An optional primary key value to check out. If not set 827 * the instance property value is used. 828 * 829 * @return boolean True on success. 830 * 831 * @link http://docs.joomla.org/JTable/checkOut 832 * @since 11.1 833 */ 834 public function checkOut($userId, $pk = null) 835 { 836 // If there is no checked_out or checked_out_time field, just return true. 837 if (!property_exists($this, 'checked_out') || !property_exists($this, 'checked_out_time')) 838 { 839 return true; 840 } 841 842 // Initialise variables. 843 $k = $this->_tbl_key; 844 $pk = (is_null($pk)) ? $this->$k : $pk; 845 846 // If no primary key is given, return false. 847 if ($pk === null) 848 { 849 $e = new JException(JText::_('JLIB_DATABASE_ERROR_NULL_PRIMARY_KEY')); 850 $this->setError($e); 851 return false; 852 } 853 854 // Get the current time in MySQL format. 855 $time = JFactory::getDate()->toSql(); 856 857 // Check the row out by primary key. 858 $query = $this->_db->getQuery(true); 859 $query->update($this->_tbl); 860 $query->set($this->_db->quoteName('checked_out') . ' = ' . (int) $userId); 861 $query->set($this->_db->quoteName('checked_out_time') . ' = ' . $this->_db->quote($time)); 862 $query->where($this->_tbl_key . ' = ' . $this->_db->quote($pk)); 863 $this->_db->setQuery($query); 864 865 if (!$this->_db->query()) 866 { 867 $e = new JException(JText::sprintf('JLIB_DATABASE_ERROR_CHECKOUT_FAILED', get_class($this), $this->_db->getErrorMsg())); 868 $this->setError($e); 869 return false; 870 } 871 872 // Set table values in the object. 873 $this->checked_out = (int) $userId; 874 $this->checked_out_time = $time; 875 876 return true; 877 } 878 879 /** 880 * Method to check a row in if the necessary properties/fields exist. Checking 881 * a row in will allow other users the ability to edit the row. 882 * 883 * @param mixed $pk An optional primary key value to check out. If not set the instance property value is used. 884 * 885 * @return boolean True on success. 886 * 887 * @link http://docs.joomla.org/JTable/checkIn 888 * @since 11.1 889 */ 890 public function checkIn($pk = null) 891 { 892 // If there is no checked_out or checked_out_time field, just return true. 893 if (!property_exists($this, 'checked_out') || !property_exists($this, 'checked_out_time')) 894 { 895 return true; 896 } 897 898 // Initialise variables. 899 $k = $this->_tbl_key; 900 $pk = (is_null($pk)) ? $this->$k : $pk; 901 902 // If no primary key is given, return false. 903 if ($pk === null) 904 { 905 $e = new JException(JText::_('JLIB_DATABASE_ERROR_NULL_PRIMARY_KEY')); 906 $this->setError($e); 907 return false; 908 } 909 910 // Check the row in by primary key. 911 $query = $this->_db->getQuery(true); 912 $query->update($this->_tbl); 913 $query->set($this->_db->quoteName('checked_out') . ' = 0'); 914 $query->set($this->_db->quoteName('checked_out_time') . ' = ' . $this->_db->quote($this->_db->getNullDate())); 915 $query->where($this->_tbl_key . ' = ' . $this->_db->quote($pk)); 916 $this->_db->setQuery($query); 917 918 // Check for a database error. 919 if (!$this->_db->query()) 920 { 921 $e = new JException(JText::sprintf('JLIB_DATABASE_ERROR_CHECKIN_FAILED', get_class($this), $this->_db->getErrorMsg())); 922 $this->setError($e); 923 return false; 924 } 925 926 // Set table values in the object. 927 $this->checked_out = 0; 928 $this->checked_out_time = ''; 929 930 return true; 931 } 932 933 /** 934 * Method to increment the hits for a row if the necessary property/field exists. 935 * 936 * @param mixed $pk An optional primary key value to increment. If not set the instance property value is used. 937 * 938 * @return boolean True on success. 939 * 940 * @link http://docs.joomla.org/JTable/hit 941 * @since 11.1 942 */ 943 public function hit($pk = null) 944 { 945 // If there is no hits field, just return true. 946 if (!property_exists($this, 'hits')) 947 { 948 return true; 949 } 950 951 // Initialise variables. 952 $k = $this->_tbl_key; 953 $pk = (is_null($pk)) ? $this->$k : $pk; 954 955 // If no primary key is given, return false. 956 if ($pk === null) 957 { 958 return false; 959 } 960 961 // Check the row in by primary key. 962 $query = $this->_db->getQuery(true); 963 $query->update($this->_tbl); 964 $query->set($this->_db->quoteName('hits') . ' = (' . $this->_db->quoteName('hits') . ' + 1)'); 965 $query->where($this->_tbl_key . ' = ' . $this->_db->quote($pk)); 966 $this->_db->setQuery($query); 967 968 // Check for a database error. 969 if (!$this->_db->query()) 970 { 971 $e = new JException(JText::sprintf('JLIB_DATABASE_ERROR_HIT_FAILED', get_class($this), $this->_db->getErrorMsg())); 972 $this->setError($e); 973 return false; 974 } 975 976 // Set table values in the object. 977 $this->hits++; 978 979 return true; 980 } 981 982 /** 983 * Method to determine if a row is checked out and therefore uneditable by 984 * a user. If the row is checked out by the same user, then it is considered 985 * not checked out -- as the user can still edit it. 986 * 987 * @param integer $with The userid to preform the match with, if an item is checked 988 * out by this user the function will return false. 989 * @param integer $against The userid to perform the match against when the function 990 * is used as a static function. 991 * 992 * @return boolean True if checked out. 993 * 994 * @link http://docs.joomla.org/JTable/isCheckedOut 995 * @since 11.1 996 * @todo This either needs to be static or not. 997 */ 998 public function isCheckedOut($with = 0, $against = null) 999 { 1000 // Handle the non-static case. 1001 if (isset($this) && ($this instanceof JTable) && is_null($against)) 1002 { 1003 $against = $this->get('checked_out'); 1004 } 1005 1006 // The item is not checked out or is checked out by the same user. 1007 if (!$against || ($against == $with)) 1008 { 1009 return false; 1010 } 1011 1012 $db = JFactory::getDBO(); 1013 $db->setQuery('SELECT COUNT(userid)' . ' FROM ' . $db->quoteName('#__session') . ' WHERE ' . $db->quoteName('userid') . ' = ' . (int) $against); 1014 $checkedOut = (boolean) $db->loadResult(); 1015 1016 // If a session exists for the user then it is checked out. 1017 return $checkedOut; 1018 } 1019 1020 /** 1021 * Method to get the next ordering value for a group of rows defined by an SQL WHERE clause. 1022 * This is useful for placing a new item last in a group of items in the table. 1023 * 1024 * @param string $where WHERE clause to use for selecting the MAX(ordering) for the table. 1025 * 1026 * @return mixed Boolean false an failure or the next ordering value as an integer. 1027 * 1028 * @link http://docs.joomla.org/JTable/getNextOrder 1029 * @since 11.1 1030 */ 1031 public function getNextOrder($where = '') 1032 { 1033 // If there is no ordering field set an error and return false. 1034 if (!property_exists($this, 'ordering')) 1035 { 1036 $e = new JException(JText::sprintf('JLIB_DATABASE_ERROR_CLASS_DOES_NOT_SUPPORT_ORDERING', get_class($this))); 1037 $this->setError($e); 1038 return false; 1039 } 1040 1041 // Get the largest ordering value for a given where clause. 1042 $query = $this->_db->getQuery(true); 1043 $query->select('MAX(ordering)'); 1044 $query->from($this->_tbl); 1045 1046 if ($where) 1047 { 1048 $query->where($where); 1049 } 1050 1051 $this->_db->setQuery($query); 1052 $max = (int) $this->_db->loadResult(); 1053 1054 // Check for a database error. 1055 if ($this->_db->getErrorNum()) 1056 { 1057 $e = new JException(JText::sprintf('JLIB_DATABASE_ERROR_GET_NEXT_ORDER_FAILED', get_class($this), $this->_db->getErrorMsg())); 1058 $this->setError($e); 1059 1060 return false; 1061 } 1062 1063 // Return the largest ordering value + 1. 1064 return ($max + 1); 1065 } 1066 1067 /** 1068 * Method to compact the ordering values of rows in a group of rows 1069 * defined by an SQL WHERE clause. 1070 * 1071 * @param string $where WHERE clause to use for limiting the selection of rows to compact the ordering values. 1072 * 1073 * @return mixed Boolean true on success. 1074 * 1075 * @link http://docs.joomla.org/JTable/reorder 1076 * @since 11.1 1077 */ 1078 public function reorder($where = '') 1079 { 1080 // If there is no ordering field set an error and return false. 1081 if (!property_exists($this, 'ordering')) 1082 { 1083 $e = new JException(JText::sprintf('JLIB_DATABASE_ERROR_CLASS_DOES_NOT_SUPPORT_ORDERING', get_class($this))); 1084 $this->setError($e); 1085 return false; 1086 } 1087 1088 // Initialise variables. 1089 $k = $this->_tbl_key; 1090 1091 // Get the primary keys and ordering values for the selection. 1092 $query = $this->_db->getQuery(true); 1093 $query->select($this->_tbl_key . ', ordering'); 1094 $query->from($this->_tbl); 1095 $query->where('ordering >= 0'); 1096 $query->order('ordering'); 1097 1098 // Setup the extra where and ordering clause data. 1099 if ($where) 1100 { 1101 $query->where($where); 1102 } 1103 1104 $this->_db->setQuery($query); 1105 $rows = $this->_db->loadObjectList(); 1106 1107 // Check for a database error. 1108 if ($this->_db->getErrorNum()) 1109 { 1110 $e = new JException(JText::sprintf('JLIB_DATABASE_ERROR_REORDER_FAILED', get_class($this), $this->_db->getErrorMsg())); 1111 $this->setError($e); 1112 1113 return false; 1114 } 1115 1116 // Compact the ordering values. 1117 foreach ($rows as $i => $row) 1118 { 1119 // Make sure the ordering is a positive integer. 1120 if ($row->ordering >= 0) 1121 { 1122 // Only update rows that are necessary. 1123 if ($row->ordering != $i + 1) 1124 { 1125 // Update the row ordering field. 1126 $query = $this->_db->getQuery(true); 1127 $query->update($this->_tbl); 1128 $query->set('ordering = ' . ($i + 1)); 1129 $query->where($this->_tbl_key . ' = ' . $this->_db->quote($row->$k)); 1130 $this->_db->setQuery($query); 1131 1132 // Check for a database error. 1133 if (!$this->_db->query()) 1134 { 1135 $e = new JException( 1136 JText::sprintf('JLIB_DATABASE_ERROR_REORDER_UPDATE_ROW_FAILED', get_class($this), $i, $this->_db->getErrorMsg()) 1137 ); 1138 $this->setError($e); 1139 1140 return false; 1141 } 1142 } 1143 } 1144 } 1145 1146 return true; 1147 } 1148 1149 /** 1150 * Method to move a row in the ordering sequence of a group of rows defined by an SQL WHERE clause. 1151 * Negative numbers move the row up in the sequence and positive numbers move it down. 1152 * 1153 * @param integer $delta The direction and magnitude to move the row in the ordering sequence. 1154 * @param string $where WHERE clause to use for limiting the selection of rows to compact the 1155 * ordering values. 1156 * 1157 * @return mixed Boolean true on success. 1158 * 1159 * @link http://docs.joomla.org/JTable/move 1160 * @since 11.1 1161 */ 1162 public function move($delta, $where = '') 1163 { 1164 // If there is no ordering field set an error and return false. 1165 if (!property_exists($this, 'ordering')) 1166 { 1167 $e = new JException(JText::sprintf('JLIB_DATABASE_ERROR_CLASS_DOES_NOT_SUPPORT_ORDERING', get_class($this))); 1168 $this->setError($e); 1169 return false; 1170 } 1171 1172 // If the change is none, do nothing. 1173 if (empty($delta)) 1174 { 1175 return true; 1176 } 1177 1178 // Initialise variables. 1179 $k = $this->_tbl_key; 1180 $row = null; 1181 $query = $this->_db->getQuery(true); 1182 1183 // Select the primary key and ordering values from the table. 1184 $query->select($this->_tbl_key . ', ordering'); 1185 $query->from($this->_tbl); 1186 1187 // If the movement delta is negative move the row up. 1188 if ($delta < 0) 1189 { 1190 $query->where('ordering < ' . (int) $this->ordering); 1191 $query->order('ordering DESC'); 1192 } 1193 // If the movement delta is positive move the row down. 1194 elseif ($delta > 0) 1195 { 1196 $query->where('ordering > ' . (int) $this->ordering); 1197 $query->order('ordering ASC'); 1198 } 1199 1200 // Add the custom WHERE clause if set. 1201 if ($where) 1202 { 1203 $query->where($where); 1204 } 1205 1206 // Select the first row with the criteria. 1207 $this->_db->setQuery($query, 0, 1); 1208 $row = $this->_db->loadObject(); 1209 1210 // If a row is found, move the item. 1211 if (!empty($row)) 1212 { 1213 // Update the ordering field for this instance to the row's ordering value. 1214 $query = $this->_db->getQuery(true); 1215 $query->update($this->_tbl); 1216 $query->set('ordering = ' . (int) $row->ordering); 1217 $query->where($this->_tbl_key . ' = ' . $this->_db->quote($this->$k)); 1218 $this->_db->setQuery($query); 1219 1220 // Check for a database error. 1221 if (!$this->_db->query()) 1222 { 1223 $e = new JException(JText::sprintf('JLIB_DATABASE_ERROR_MOVE_FAILED', get_class($this), $this->_db->getErrorMsg())); 1224 $this->setError($e); 1225 1226 return false; 1227 } 1228 1229 // Update the ordering field for the row to this instance's ordering value. 1230 $query = $this->_db->getQuery(true); 1231 $query->update($this->_tbl); 1232 $query->set('ordering = ' . (int) $this->ordering); 1233 $query->where($this->_tbl_key . ' = ' . $this->_db->quote($row->$k)); 1234 $this->_db->setQuery($query); 1235 1236 // Check for a database error. 1237 if (!$this->_db->query()) 1238 { 1239 $e = new JException(JText::sprintf('JLIB_DATABASE_ERROR_MOVE_FAILED', get_class($this), $this->_db->getErrorMsg())); 1240 $this->setError($e); 1241 1242 return false; 1243 } 1244 1245 // Update the instance value. 1246 $this->ordering = $row->ordering; 1247 } 1248 else 1249 { 1250 // Update the ordering field for this instance. 1251 $query = $this->_db->getQuery(true); 1252 $query->update($this->_tbl); 1253 $query->set('ordering = ' . (int) $this->ordering); 1254 $query->where($this->_tbl_key . ' = ' . $this->_db->quote($this->$k)); 1255 $this->_db->setQuery($query); 1256 1257 // Check for a database error. 1258 if (!$this->_db->query()) 1259 { 1260 $e = new JException(JText::sprintf('JLIB_DATABASE_ERROR_MOVE_FAILED', get_class($this), $this->_db->getErrorMsg())); 1261 $this->setError($e); 1262 1263 return false; 1264 } 1265 } 1266 1267 return true; 1268 } 1269 1270 /** 1271 * Method to set the publishing state for a row or list of rows in the database 1272 * table. The method respects checked out rows by other users and will attempt 1273 * to checkin rows that it can after adjustments are made. 1274 * 1275 * @param mixed $pks An optional array of primary key values to update. If not set the instance property value is used. 1276 * @param integer $state The publishing state. eg. [0 = unpublished, 1 = published] 1277 * @param integer $userId The user id of the user performing the operation. 1278 * 1279 * @return boolean True on success. 1280 * 1281 * @link http://docs.joomla.org/JTable/publish 1282 * @since 11.1 1283 */ 1284 public function publish($pks = null, $state = 1, $userId = 0) 1285 { 1286 // Initialise variables. 1287 $k = $this->_tbl_key; 1288 1289 // Sanitize input. 1290 JArrayHelper::toInteger($pks); 1291 $userId = (int) $userId; 1292 $state = (int) $state; 1293 1294 // If there are no primary keys set check to see if the instance key is set. 1295 if (empty($pks)) 1296 { 1297 if ($this->$k) 1298 { 1299 $pks = array($this->$k); 1300 } 1301 // Nothing to set publishing state on, return false. 1302 else 1303 { 1304 $e = new JException(JText::_('JLIB_DATABASE_ERROR_NO_ROWS_SELECTED')); 1305 $this->setError($e); 1306 1307 return false; 1308 } 1309 } 1310 1311 // Update the publishing state for rows with the given primary keys. 1312 $query = $this->_db->getQuery(true); 1313 $query->update($this->_tbl); 1314 $query->set('published = ' . (int) $state); 1315 1316 // Determine if there is checkin support for the table. 1317 if (property_exists($this, 'checked_out') || property_exists($this, 'checked_out_time')) 1318 { 1319 $query->where('(checked_out = 0 OR checked_out = ' . (int) $userId . ')'); 1320 $checkin = true; 1321 } 1322 else 1323 { 1324 $checkin = false; 1325 } 1326 1327 // Build the WHERE clause for the primary keys. 1328 $query->where($k . ' = ' . implode(' OR ' . $k . ' = ', $pks)); 1329 1330 $this->_db->setQuery($query); 1331 1332 // Check for a database error. 1333 if (!$this->_db->query()) 1334 { 1335 $e = new JException(JText::sprintf('JLIB_DATABASE_ERROR_PUBLISH_FAILED', get_class($this), $this->_db->getErrorMsg())); 1336 $this->setError($e); 1337 1338 return false; 1339 } 1340 1341 // If checkin is supported and all rows were adjusted, check them in. 1342 if ($checkin && (count($pks) == $this->_db->getAffectedRows())) 1343 { 1344 // Checkin the rows. 1345 foreach ($pks as $pk) 1346 { 1347 $this->checkin($pk); 1348 } 1349 } 1350 1351 // If the JTable instance value is in the list of primary keys that were set, set the instance. 1352 if (in_array($this->$k, $pks)) 1353 { 1354 $this->published = $state; 1355 } 1356 1357 $this->setError(''); 1358 return true; 1359 } 1360 1361 /** 1362 * Generic check for whether dependencies exist for this object in the database schema 1363 * 1364 * Can be overloaded/supplemented by the child class 1365 * 1366 * @param mixed $pk An optional primary key value check the row for. If not 1367 * set the instance property value is used. 1368 * @param array $joins An optional array to compiles standard joins formatted like: 1369 * [label => 'Label', name => 'table name' , idfield => 'field', joinfield => 'field'] 1370 * 1371 * @return boolean True on success. 1372 * 1373 * @deprecated 12.1 1374 * @link http://docs.joomla.org/JTable/canDelete 1375 * @since 11.1 1376 */ 1377 public function canDelete($pk = null, $joins = null) 1378 { 1379 // Deprecation warning. 1380 JLog::add('JTable::canDelete() is deprecated.', JLog::WARNING, 'deprecated'); 1381 1382 // Initialise variables. 1383 $k = $this->_tbl_key; 1384 $pk = (is_null($pk)) ? $this->$k : $pk; 1385 1386 // If no primary key is given, return false. 1387 if ($pk === null) 1388 { 1389 return false; 1390 } 1391 1392 if (is_array($joins)) 1393 { 1394 // Get a query object. 1395 $query = $this->_db->getQuery(true); 1396 1397 // Setup the basic query. 1398 $query->select($this->_db->quoteName($this->_tbl_key)); 1399 $query->from($this->_db->quoteName($this->_tbl)); 1400 $query->where($this->_db->quoteName($this->_tbl_key) . ' = ' . $this->_db->quote($this->$k)); 1401 $query->group($this->_db->quoteName($this->_tbl_key)); 1402 1403 // For each join add the select and join clauses to the query object. 1404 foreach ($joins as $table) 1405 { 1406 $query->select('COUNT(DISTINCT ' . $table['idfield'] . ') AS ' . $table['idfield']); 1407 $query->join('LEFT', $table['name'] . ' ON ' . $table['joinfield'] . ' = ' . $k); 1408 } 1409 1410 // Get the row object from the query. 1411 $this->_db->setQuery((string) $query, 0, 1); 1412 $row = $this->_db->loadObject(); 1413 1414 // Check for a database error. 1415 if ($this->_db->getErrorNum()) 1416 { 1417 $this->setError($this->_db->getErrorMsg()); 1418 1419 return false; 1420 } 1421 1422 $msg = array(); 1423 $i = 0; 1424 1425 foreach ($joins as $table) 1426 { 1427 $k = $table['idfield'] . $i; 1428 1429 if ($row->$k) 1430 { 1431 $msg[] = JText::_($table['label']); 1432 } 1433 1434 $i++; 1435 } 1436 1437 if (count($msg)) 1438 { 1439 $this->setError("noDeleteRecord" . ": " . implode(', ', $msg)); 1440 1441 return false; 1442 } 1443 else 1444 { 1445 return true; 1446 } 1447 } 1448 1449 return true; 1450 } 1451 1452 /** 1453 * Method to export the JTable instance properties to an XML string. 1454 * 1455 * @param boolean $mapKeysToText True to map foreign keys to text values. 1456 * 1457 * @return string XML string representation of the instance. 1458 * 1459 * @deprecated 12.1 1460 * @link http://docs.joomla.org/JTable/toXML 1461 * @since 11.1 1462 */ 1463 public function toXML($mapKeysToText = false) 1464 { 1465 // Deprecation warning. 1466 JLog::add('JTable::toXML() is deprecated.', JLog::WARNING, 'deprecated'); 1467 1468 // Initialise variables. 1469 $xml = array(); 1470 $map = $mapKeysToText ? ' mapkeystotext="true"' : ''; 1471 1472 // Open root node. 1473 $xml[] = '<record table="' . $this->_tbl . '"' . $map . '>'; 1474 1475 // Get the publicly accessible instance properties. 1476 foreach (get_object_vars($this) as $k => $v) 1477 { 1478 // If the value is null or non-scalar, or the field is internal ignore it. 1479 if (!is_scalar($v) || ($v === null) || ($k[0] == '_')) 1480 { 1481 continue; 1482 } 1483 1484 $xml[] = ' <' . $k . '><![CDATA[' . $v . ']]></' . $k . '>'; 1485 } 1486 1487 // Close root node. 1488 $xml[] = '</record>'; 1489 1490 // Return the XML array imploded over new lines. 1491 return implode("\n", $xml); 1492 } 1493 1494 /** 1495 * Method to lock the database table for writing. 1496 * 1497 * @return boolean True on success. 1498 * 1499 * @since 11.1 1500 * @throws JDatabaseException 1501 */ 1502 protected function _lock() 1503 { 1504 $this->_db->lockTable($this->_tbl); 1505 $this->_locked = true; 1506 1507 return true; 1508 } 1509 1510 /** 1511 * Method to unlock the database table for writing. 1512 * 1513 * @return boolean True on success. 1514 * 1515 * @since 11.1 1516 */ 1517 protected function _unlock() 1518 { 1519 $this->_db->unlockTables(); 1520 $this->_locked = false; 1521 1522 return true; 1523 } 1524 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Tue Apr 3 11:40:28 2012 | Cross-referenced by PHPXref 0.7.1 |