[ Index ]

PHP Cross Reference of Joomla 2.5.4 DE

title

Body

[close]

/libraries/joomla/database/ -> table.php (source)

   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  }


Generated: Tue Apr 3 11:40:28 2012 Cross-referenced by PHPXref 0.7.1