/modx/core/xpdo/om/xpdoobject.class.php
PHP | 2192 lines | 1449 code | 76 blank | 667 comment | 528 complexity | 5596bac0e53ac49189027212d5efcc41 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, LGPL-2.1, GPL-2.0, GPL-3.0, LGPL-2.0
Large files files are truncated, but you can click here to view the full file
- <?php
- /*
- * Copyright 2010-2011 by MODX, LLC.
- *
- * This file is part of xPDO.
- *
- * xPDO is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software
- * Foundation; either version 2 of the License, or (at your option) any later
- * version.
- *
- * xPDO is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * xPDO; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
- * Suite 330, Boston, MA 02111-1307 USA
- */
- /**
- * The base persistent xPDO object classes.
- *
- * This file contains the base persistent object classes, which your user-
- * defined classes will extend when implementing an xPDO object model.
- *
- * @package xpdo
- * @subpackage om
- */
- /**
- * The base persistent xPDO object class.
- *
- * This is the basis for the entire xPDO object model, and can also be used by a
- * class generator {@link xPDOGenerator}, ultimately allowing custom classes to
- * be user-defined in a web interface and framework-generated at runtime.
- *
- * @abstract This is an abstract class, and is not represented by an actual
- * table; it simply defines the member variables and functions needed for object
- * persistence. All xPDOObject derivatives must define both a PHP 4 style
- * constructor which calls a PHP 5 style __construct() method with the same
- * parameters. This is necessary to allow instantiation of further derived
- * classes without knowing the name of the class ahead of time in PHP 4. Note
- * that this does not meet E_STRICT compliance in PHP 5, but is the only sane
- * way to achieve consistency between the PHP 4 and 5 inheritence models.
- *
- * @package xpdo
- * @subpackage om
- */
- class xPDOObject {
- /**
- * A convenience reference to the xPDO object.
- * @var xPDO
- * @access public
- */
- public $xpdo= null;
- /**
- * Name of the data source container the object belongs to.
- * @var string
- * @access public
- */
- public $container= null;
- /**
- * Names of the fields in the data table, fully-qualified with a table name.
- *
- * NOTE: For use in table joins to qualify fields with the same name.
- *
- * @var array
- * @access public
- */
- public $fieldNames= null;
- /**
- * The actual class name of an instance.
- * @var string
- */
- public $_class= null;
- /**
- * The package the class is a part of.
- * @var string
- */
- public $_package= null;
- /**
- * An alias for this instance of the class.
- * @var string
- */
- public $_alias= null;
- /**
- * The primary key field (or an array of primary key fields) for this object.
- * @var string|array
- * @access public
- */
- public $_pk= null;
- /**
- * The php native type of the primary key field.
- *
- * NOTE: Will be an array if multiple primary keys are specified for the object.
- *
- * @var string|array
- * @access public
- */
- public $_pktype= null;
- /**
- * Name of the actual table representing this class.
- * @var string
- * @access public
- */
- public $_table= null;
- /**
- * An array of meta data for the table.
- * @var string
- * @access public
- */
- public $_tableMeta= null;
- /**
- * An array of field names that have been modified.
- * @var array
- * @access public
- */
- public $_dirty= array ();
- /**
- * An array of field names that have not been loaded from the source.
- * @var array
- * @access public
- */
- public $_lazy= array ();
- /**
- * An array of key-value pairs representing the fields of the instance.
- * @var array
- * @access public
- */
- public $_fields= array ();
- /**
- * An array of metadata definitions for each field in the class.
- * @var array
- * @access public
- */
- public $_fieldMeta= array ();
- /**
- * An array of aggregate foreign key relationships for the class.
- * @var array
- * @access public
- */
- public $_aggregates= array ();
- /**
- * An array of composite foreign key relationships for the class.
- * @var array
- * @access public
- */
- public $_composites= array ();
- /**
- * An array of object instances related to this object instance.
- * @var array
- * @access public
- */
- public $_relatedObjects= array ();
- /**
- * A validator object responsible for this object instance.
- * @var xPDOValidator
- * @access public
- */
- public $_validator = null;
- /**
- * An array of validation rules for this object instance.
- * @var array
- * @access public
- */
- public $_validationRules = array();
- /**
- * An array of field names that have been already validated.
- * @var array
- * @access public
- */
- public $_validated= array ();
- /**
- * Indicates if the validation map has been loaded.
- * @var boolean
- * @access public
- */
- public $_validationLoaded= false;
- /**
- * Indicates if the instance is transient (and thus new).
- * @var boolean
- * @access public
- */
- public $_new= true;
- /**
- * Indicates the cacheability of the instance.
- * @var boolean
- */
- public $_cacheFlag= true;
- /**
- * A collection of various options that can be used on the instance.
- * @var array
- */
- public $_options= array();
- /**
- * Responsible for loading a result set from the database.
- *
- * @static
- * @param xPDO &$xpdo A valid xPDO instance.
- * @param string $className Name of the class.
- * @param xPDOCriteria $criteria A valid xPDOCriteria instance.
- * @return PDOStatement A reference to a PDOStatement representing the
- * result set.
- */
- public static function & _loadRows(& $xpdo, $className, $criteria) {
- $rows= null;
- if ($criteria->prepare()) {
- if ($xpdo->getDebug() === true) $xpdo->log(xPDO::LOG_LEVEL_DEBUG, "Attempting to execute query using PDO statement object: " . print_r($criteria->sql, true) . print_r($criteria->bindings, true));
- $tstart= $xpdo->getMicroTime();
- if (!$criteria->stmt->execute()) {
- $tend= $xpdo->getMicroTime();
- $totaltime= $tend - $tstart;
- $xpdo->queryTime= $xpdo->queryTime + $totaltime;
- $xpdo->executedQueries= $xpdo->executedQueries + 1;
- $errorInfo= $criteria->stmt->errorInfo();
- $xpdo->log(xPDO::LOG_LEVEL_ERROR, 'Error ' . $criteria->stmt->errorCode() . " executing statement: \n" . print_r($errorInfo, true));
- if (($errorInfo[1] == '1146' || $errorInfo[1] == '1') && $xpdo->getOption(xPDO::OPT_AUTO_CREATE_TABLES)) {
- if ($xpdo->getManager() && $xpdo->manager->createObjectContainer($className)) {
- $tstart= $xpdo->getMicroTime();
- if (!$criteria->stmt->execute()) {
- $xpdo->log(xPDO::LOG_LEVEL_ERROR, "Error " . $criteria->stmt->errorCode() . " executing statement: \n" . print_r($criteria->stmt->errorInfo(), true));
- }
- $tend= $xpdo->getMicroTime();
- $totaltime= $tend - $tstart;
- $xpdo->queryTime= $xpdo->queryTime + $totaltime;
- $xpdo->executedQueries= $xpdo->executedQueries + 1;
- } else {
- $xpdo->log(xPDO::LOG_LEVEL_ERROR, "Error " . $xpdo->errorCode() . " attempting to create object container for class {$className}:\n" . print_r($xpdo->errorInfo(), true));
- }
- }
- }
- $rows= & $criteria->stmt;
- } else {
- $errorInfo = $xpdo->errorInfo();
- $xpdo->log(xPDO::LOG_LEVEL_ERROR, "Error preparing statement for query: {$criteria->sql} - " . print_r($errorInfo, true));
- if (($errorInfo[1] == '1146' || $errorInfo[1] == '1') && $xpdo->getOption(xPDO::OPT_AUTO_CREATE_TABLES)) {
- if ($xpdo->getManager() && $xpdo->manager->createObjectContainer($className)) {
- if (!$criteria->prepare()) {
- $xpdo->log(xPDO::LOG_LEVEL_ERROR, "Error preparing statement for query: {$criteria->sql} - " . print_r($errorInfo, true));
- } else {
- $tstart= $xpdo->getMicroTime();
- if (!$criteria->stmt->execute()) {
- $xpdo->log(xPDO::LOG_LEVEL_ERROR, "Error " . $criteria->stmt->errorCode() . " executing statement: \n" . print_r($criteria->stmt->errorInfo(), true));
- }
- $tend= $xpdo->getMicroTime();
- $totaltime= $tend - $tstart;
- $xpdo->queryTime= $xpdo->queryTime + $totaltime;
- $xpdo->executedQueries= $xpdo->executedQueries + 1;
- }
- } else {
- $xpdo->log(xPDO::LOG_LEVEL_ERROR, "Error " . $xpdo->errorCode() . " attempting to create object container for class {$className}:\n" . print_r($xpdo->errorInfo(), true));
- }
- }
- }
- return $rows;
- }
- /**
- * Loads an instance from an associative array.
- *
- * @static
- * @param xPDO &$xpdo A valid xPDO instance.
- * @param string $className Name of the class.
- * @param xPDOQuery|string $criteria A valid xPDOQuery instance or relation alias.
- * @param array $row The associative array containing the instance data.
- * @return xPDOObject A new xPDOObject derivative representing a data row.
- */
- public static function _loadInstance(& $xpdo, $className, $criteria, $row) {
- $rowPrefix= '';
- if (is_object($criteria) && $criteria instanceof xPDOQuery) {
- $alias = $criteria->getAlias();
- $actualClass = $criteria->getClass();
- } elseif (is_string($criteria) && !empty($criteria)) {
- $alias = $criteria;
- $actualClass = $className;
- } else {
- $alias = $className;
- $actualClass= $className;
- }
- if (isset($row["{$className}_class_key"])) {
- $actualClass= $row["{$className}_class_key"];
- $rowPrefix= $className . '_';
- }
- elseif (isset ($row["{$alias}_class_key"])) {
- $actualClass= $row["{$alias}_class_key"];
- $rowPrefix= $alias . '_';
- }
- elseif (isset ($row['class_key'])) {
- $actualClass= $row['class_key'];
- }
- $instance= $xpdo->newObject($actualClass);
- if (is_object($instance) && $instance instanceof xPDOObject) {
- if (strpos(strtolower(key($row)), strtolower($alias . '_')) === 0) {
- $rowPrefix= $alias . '_';
- }
- elseif (strpos(strtolower(key($row)), strtolower($className . '_')) === 0) {
- $rowPrefix= $className . '_';
- }
- else {
- $pk = $xpdo->getPK($actualClass);
- if (is_array($pk)) $pk = reset($pk);
- if (isset($row["{$alias}_{$pk}"])) {
- $rowPrefix= $alias . '_';
- }
- elseif ($actualClass !== $className && $actualClass !== $alias && isset($row["{$actualClass}_{$pk}"])) {
- $rowPrefix= $actualClass . '_';
- }
- elseif ($className !== $alias && isset($row["{$className}_{$pk}"])) {
- $rowPrefix= $className . '_';
- }
- }
- if (!$instance instanceof $className) {
- $xpdo->log(xPDO::LOG_LEVEL_ERROR, "Instantiated a derived class {$actualClass} that is not a subclass of the requested class {$className}");
- }
- $instance->_lazy= $actualClass !== $className ? array_keys($xpdo->getFields($className)) : array_keys($instance->_fields);
- $instance->fromArray($row, $rowPrefix, true, true);
- $instance->_dirty= array ();
- $instance->_new= false;
- }
- return $instance;
- }
- /**
- * Responsible for loading an instance into a collection.
- *
- * @static
- * @param xPDO &$xpdo A valid xPDO instance.
- * @param array &$objCollection The collection to load the instance into.
- * @param string $className Name of the class.
- * @param mixed $criteria A valid primary key, criteria array, or xPDOCriteria instance.
- * @param boolean|integer $cacheFlag Indicates if the objects should be cached and
- * optionally, by specifying an integer value, for how many seconds.
- */
- public static function _loadCollectionInstance(xPDO & $xpdo, array & $objCollection, $className, $criteria, $row, $fromCache, $cacheFlag=true) {
- $loaded = false;
- if ($obj= xPDOObject :: _loadInstance($xpdo, $className, $criteria, $row)) {
- if (($cacheKey= $obj->getPrimaryKey()) && !$obj->isLazy()) {
- if (is_array($cacheKey)) {
- $pkval= implode('-', $cacheKey);
- } else {
- $pkval= $cacheKey;
- }
- if ($xpdo->getOption(xPDO::OPT_CACHE_DB_COLLECTIONS, array(), 1) == 2 && $xpdo->_cacheEnabled && $cacheFlag) {
- if (!$fromCache) {
- $pkCriteria = $xpdo->newQuery($className, $cacheKey, $cacheFlag);
- $xpdo->toCache($pkCriteria, $obj, $cacheFlag);
- } else {
- $obj->_cacheFlag= true;
- }
- }
- $objCollection[$pkval]= $obj;
- $loaded = true;
- } else {
- $objCollection[]= $obj;
- $loaded = true;
- }
- }
- return $loaded;
- }
- /**
- * Load an instance of an xPDOObject or derivative class.
- *
- * @static
- * @param xPDO &$xpdo A valid xPDO instance.
- * @param string $className Name of the class.
- * @param mixed $criteria A valid primary key, criteria array, or
- * xPDOCriteria instance.
- * @param boolean|integer $cacheFlag Indicates if the objects should be
- * cached and optionally, by specifying an integer value, for how many
- * seconds.
- * @return object|null An instance of the requested class, or null if it
- * could not be instantiated.
- */
- public static function load(xPDO & $xpdo, $className, $criteria, $cacheFlag= true) {
- $instance= null;
- $fromCache= false;
- if ($className= $xpdo->loadClass($className)) {
- if (!is_object($criteria)) {
- $criteria= $xpdo->getCriteria($className, $criteria, $cacheFlag);
- }
- if (is_object($criteria)) {
- $criteria = $xpdo->addDerivativeCriteria($className, $criteria);
- $row= null;
- if ($xpdo->_cacheEnabled && $criteria->cacheFlag && $cacheFlag) {
- $row= $xpdo->fromCache($criteria, $className);
- }
- if ($row === null || !is_array($row)) {
- if ($rows= xPDOObject :: _loadRows($xpdo, $className, $criteria)) {
- $row= $rows->fetch(PDO::FETCH_ASSOC);
- $rows->closeCursor();
- }
- } else {
- $fromCache= true;
- }
- if (!is_array($row)) {
- if ($xpdo->getDebug() === true) $xpdo->log(xPDO::LOG_LEVEL_DEBUG, "Fetched empty result set from statement: " . print_r($criteria->sql, true) . " with bindings: " . print_r($criteria->bindings, true));
- } else {
- $instance= xPDOObject :: _loadInstance($xpdo, $className, $criteria, $row);
- if (is_object($instance)) {
- if (!$fromCache && $cacheFlag && $xpdo->_cacheEnabled) {
- $xpdo->toCache($criteria, $instance, $cacheFlag);
- if ($xpdo->getOption(xPDO::OPT_CACHE_DB_OBJECTS_BY_PK) && ($cacheKey= $instance->getPrimaryKey()) && !$instance->isLazy()) {
- $pkCriteria = $xpdo->newQuery($className, $cacheKey, $cacheFlag);
- $xpdo->toCache($pkCriteria, $instance, $cacheFlag);
- }
- }
- if ($xpdo->getDebug() === true) $xpdo->log(xPDO::LOG_LEVEL_DEBUG, "Loaded object instance: " . print_r($instance->toArray('', true), true));
- }
- }
- } else {
- $xpdo->log(xPDO::LOG_LEVEL_ERROR, 'No valid statement could be found in or generated from the given criteria.');
- }
- } else {
- $xpdo->log(xPDO::LOG_LEVEL_ERROR, 'Invalid class specified: ' . $className);
- }
- return $instance;
- }
- /**
- * Load a collection of xPDOObject instances.
- *
- * @static
- * @param xPDO &$xpdo A valid xPDO instance.
- * @param string $className Name of the class.
- * @param mixed $criteria A valid primary key, criteria array, or xPDOCriteria instance.
- * @param boolean|integer $cacheFlag Indicates if the objects should be
- * cached and optionally, by specifying an integer value, for how many
- * seconds.
- * @return array An array of xPDOObject instances or an empty array if no instances are loaded.
- */
- public static function loadCollection(xPDO & $xpdo, $className, $criteria= null, $cacheFlag= true) {
- $objCollection= array ();
- $fromCache = false;
- if (!$className= $xpdo->loadClass($className)) return $objCollection;
- $rows= false;
- $fromCache= false;
- $collectionCaching = (integer) $xpdo->getOption(xPDO::OPT_CACHE_DB_COLLECTIONS, array(), 1);
- if (!is_object($criteria)) {
- $criteria= $xpdo->getCriteria($className, $criteria, $cacheFlag);
- }
- if (is_object($criteria)) {
- $criteria = $xpdo->addDerivativeCriteria($className, $criteria);
- }
- if ($collectionCaching > 0 && $xpdo->_cacheEnabled && $cacheFlag) {
- $rows= $xpdo->fromCache($criteria);
- $fromCache = (is_array($rows) && !empty($rows));
- }
- if (!$fromCache && is_object($criteria)) {
- $rows= xPDOObject :: _loadRows($xpdo, $className, $criteria);
- }
- if (is_array ($rows)) {
- foreach ($rows as $row) {
- xPDOObject :: _loadCollectionInstance($xpdo, $objCollection, $className, $criteria, $row, $fromCache, $cacheFlag);
- }
- } elseif (is_object($rows)) {
- $cacheRows = array();
- while ($row = $rows->fetch(PDO::FETCH_ASSOC)) {
- xPDOObject :: _loadCollectionInstance($xpdo, $objCollection, $className, $criteria, $row, $fromCache, $cacheFlag);
- if ($collectionCaching > 0 && $xpdo->_cacheEnabled && $cacheFlag && !$fromCache) $cacheRows[] = $row;
- }
- if ($collectionCaching > 0 && $xpdo->_cacheEnabled && $cacheFlag && !$fromCache) $rows =& $cacheRows;
- }
- if (!$fromCache && $xpdo->_cacheEnabled && $collectionCaching > 0 && $cacheFlag && !empty($rows)) {
- $xpdo->toCache($criteria, $rows, $cacheFlag);
- }
- return $objCollection;
- }
- /**
- * Load a collection of xPDOObject instances and a graph of related objects.
- *
- * @static
- * @param xPDO &$xpdo A valid xPDO instance.
- * @param string $className Name of the class.
- * @param string|array $graph A related object graph in array or JSON
- * format, e.g. array('relationAlias'=>array('subRelationAlias'=>array()))
- * or {"relationAlias":{"subRelationAlias":{}}}. Note that the empty arrays
- * are necessary in order for the relation to be recognized.
- * @param mixed $criteria A valid primary key, criteria array, or xPDOCriteria instance.
- * @param boolean|integer $cacheFlag Indicates if the objects should be
- * cached and optionally, by specifying an integer value, for how many
- * seconds.
- * @return array An array of xPDOObject instances or an empty array if no instances are loaded.
- */
- public static function loadCollectionGraph(xPDO & $xpdo, $className, $graph, $criteria, $cacheFlag) {
- $objCollection = array();
- if ($query= $xpdo->newQuery($className, $criteria, $cacheFlag)) {
- $query = $xpdo->addDerivativeCriteria($className, $query);
- $query->bindGraph($graph);
- $rows = array();
- $fromCache = false;
- $collectionCaching = (integer) $xpdo->getOption(xPDO::OPT_CACHE_DB_COLLECTIONS, array(), 1);
- if ($collectionCaching > 0 && $xpdo->_cacheEnabled && $cacheFlag) {
- $rows= $xpdo->fromCache($query);
- $fromCache = !empty($rows);
- }
- if (!$fromCache) {
- $stmt= $query->prepare();
- if ($stmt && $stmt->execute()) {
- $objCollection= $query->hydrateGraph($stmt, $cacheFlag);
- }
- } elseif (!empty($rows)) {
- $objCollection= $query->hydrateGraph($rows, $cacheFlag);
- }
- }
- return $objCollection;
- }
- /**
- * Get a set of column names from an xPDOObject for use in SQL queries.
- *
- * @static
- * @param xPDO &$xpdo A reference to an initialized xPDO instance.
- * @param string $className The class name to get columns from.
- * @param string $tableAlias An optional alias for the table in the query.
- * @param string $columnPrefix An optional prefix to prepend to each column name.
- * @param array $columns An optional array of field names to include or exclude
- * (include is default behavior).
- * @param boolean $exclude Determines if any specified columns should be included
- * or excluded from the set of results.
- * @return string A comma-delimited list of the field names for use in a SELECT clause.
- */
- public static function getSelectColumns(xPDO & $xpdo, $className, $tableAlias= '', $columnPrefix= '', $columns= array (), $exclude= false) {
- $columnarray= array ();
- $aColumns= $xpdo->getFields($className);
- if ($aColumns) {
- if (!empty ($tableAlias)) {
- $tableAlias= $xpdo->escape($tableAlias);
- $tableAlias.= '.';
- }
- foreach (array_keys($aColumns) as $k) {
- if ($exclude && in_array($k, $columns)) {
- continue;
- }
- elseif (empty ($columns)) {
- $columnarray[$k]= "{$tableAlias}" . $xpdo->escape($k);
- }
- elseif ($exclude || in_array($k, $columns)) {
- $columnarray[$k]= "{$tableAlias}" . $xpdo->escape($k);
- } else {
- continue;
- }
- if (!empty ($columnPrefix)) {
- $columnarray[$k]= $columnarray[$k] . " AS " . $xpdo->escape("{$columnPrefix}{$k}");
- }
- }
- }
- return implode(', ', $columnarray);
- }
- /**
- * Constructor
- *
- * Do not call the constructor directly; see {@link xPDO::newObject()}.
- *
- * All derivatives of xPDOObject must redeclare this method, and must call
- * the parent method explicitly before any additional logic is executed, e.g.
- *
- * <code>
- * public function __construct(xPDO & $xpdo) {
- * parent :: __construct($xpdo);
- * // Any additional constructor tasks here
- * }
- * </code>
- *
- * @access public
- * @param xPDO &$xpdo A reference to a valid xPDO instance.
- * @return xPDOObject
- */
- public function __construct(xPDO & $xpdo) {
- $this->xpdo= & $xpdo;
- $this->container= $xpdo->config['dbname'];
- $this->_class= get_class($this);
- $pos= strrpos($this->_class, '_');
- if ($pos !== false && substr($this->_class, $pos + 1) == $xpdo->config['dbtype']) {
- $this->_class= substr($this->_class, 0, $pos);
- }
- $this->_package= $xpdo->getPackage($this->_class);
- $this->_alias= $this->_class;
- $this->_table= $xpdo->getTableName($this->_class);
- $this->_tableMeta= $xpdo->getTableMeta($this->_class);
- $this->_fields= $xpdo->getFields($this->_class);
- $this->_fieldMeta= $xpdo->getFieldMeta($this->_class);
- $this->_aggregates= $xpdo->getAggregates($this->_class);
- $this->_composites= $xpdo->getComposites($this->_class);
- $classVars= array ();
- if ($relatedObjs= array_merge($this->_aggregates, $this->_composites)) {
- if ($this->getOption(xPDO::OPT_HYDRATE_RELATED_OBJECTS)) $classVars= get_object_vars($this);
- foreach ($relatedObjs as $aAlias => $aMeta) {
- if (!array_key_exists($aAlias, $this->_relatedObjects)) {
- if ($aMeta['cardinality'] == 'many') {
- $this->_relatedObjects[$aAlias]= array ();
- }
- else {
- $this->_relatedObjects[$aAlias]= null;
- }
- }
- if ($this->getOption(xPDO::OPT_HYDRATE_RELATED_OBJECTS) && !array_key_exists($aAlias, $classVars)) {
- $this->$aAlias= & $this->_relatedObjects[$aAlias];
- $classVars[$aAlias]= 1;
- }
- }
- }
- if ($this->getOption(xPDO::OPT_HYDRATE_FIELDS)) {
- if (!$this->getOption(xPDO::OPT_HYDRATE_RELATED_OBJECTS)) $classVars= get_object_vars($this);
- foreach ($this->_fields as $fldKey => $fldVal) {
- if (!array_key_exists($fldKey, $classVars)) {
- $this->$fldKey= & $this->_fields[$fldKey];
- }
- }
- }
- $this->setDirty();
- }
- /**
- * Get an option value for this instance.
- *
- * @param string $key The option key to retrieve a value for.
- * @param array|null $options An optional array to search for a value in first.
- * @param mixed $default A default value to return if no value is found; null is the default.
- * @return mixed The value of the option or the provided default if it is not set.
- */
- public function getOption($key, $options = null, $default = null) {
- if (is_array($options) && array_key_exists($key, $options)) {
- $value= $options[$key];
- } elseif (array_key_exists($key, $this->_options)) {
- $value= $this->_options[$key];
- } else {
- $value= $this->xpdo->getOption($key, null, $default);
- }
- return $value;
- }
- /**
- * Set an option value for this instance.
- *
- * @param string $key The option key to set a value for.
- * @param mixed $value A value to assign to the option.
- */
- public function setOption($key, $value) {
- $this->_options[$key]= $value;
- }
- /**
- * Set a field value by the field key or name.
- *
- * @todo Define and implement field validation.
- *
- * @param string $k The field key or name.
- * @param mixed $v The value to set the field to.
- * @param string|callable $vType A string indicating the format of the
- * provided value parameter, or a callable function that should be used to
- * set the field value, overriding the default behavior.
- * @return boolean Determines whether the value was set successfully and was
- * determined to be dirty (i.e. different from the previous value).
- */
- public function set($k, $v= null, $vType= '') {
- $set= false;
- $callback= '';
- $callable= !empty($vType) && is_callable($vType, false, $callback) ? true : false;
- $oldValue= null;
- if (is_string($k) && !empty($k)) {
- if (array_key_exists($k, $this->_fieldMeta)) {
- $oldValue= $this->_fields[$k];
- if (isset ($this->_fieldMeta[$k]['index']) && $this->_fieldMeta[$k]['index'] === 'pk' && isset ($this->_fieldMeta[$k]['generated'])) {
- if (!$this->_fieldMeta[$k]['generated'] === 'callback') {
- return false;
- }
- }
- if ($callable && $callback) {
- $set = $callback($k, $v, $this);
- } else {
- if (is_string($v) && $this->getOption(xPDO::OPT_ON_SET_STRIPSLASHES)) {
- $v= stripslashes($v);
- }
- if ($oldValue !== $v) {
- //type validation
- $phptype= $this->_fieldMeta[$k]['phptype'];
- $dbtype= $this->_fieldMeta[$k]['dbtype'];
- $allowNull= isset($this->_fieldMeta[$k]['null']) ? (boolean) $this->_fieldMeta[$k]['null'] : true;
- if ($v === null) {
- if ($allowNull) {
- $this->_fields[$k]= null;
- $set= true;
- } else {
- $this->xpdo->log(xPDO::LOG_LEVEL_ERROR, "{$this->_class}: Attempt to set NOT NULL field {$k} to NULL");
- }
- }
- else {
- switch ($phptype) {
- case 'timestamp' :
- case 'datetime' :
- $ts= false;
- if (preg_match('/int/i', $dbtype)) {
- if (strtolower($vType) == 'integer' || is_int($v) || $v == '0') {
- $ts= (integer) $v;
- } else {
- $ts= strtotime($v);
- }
- if ($ts === false) {
- $ts= 0;
- }
- $this->_fields[$k]= $ts;
- $set= true;
- } else {
- if ($vType == 'utc' || in_array($v, $this->xpdo->driver->_currentTimestamps) || $v === '0000-00-00 00:00:00') {
- $this->_fields[$k]= (string) $v;
- $set= true;
- } else {
- if (strtolower($vType) == 'integer' || is_int($v)) {
- $ts= intval($v);
- } elseif (is_string($v) && !empty($v)) {
- $ts= strtotime($v);
- }
- if ($ts !== false) {
- $this->_fields[$k]= strftime('%Y-%m-%d %H:%M:%S', $ts);
- $set= true;
- }
- }
- }
- break;
- case 'date' :
- if (preg_match('/int/i', $dbtype)) {
- if (strtolower($vType) == 'integer' || is_int($v) || $v == '0') {
- $ts= (integer) $v;
- } else {
- $ts= strtotime($v);
- }
- if ($ts === false) {
- $ts= 0;
- }
- $this->_fields[$k]= $ts;
- $set= true;
- } else {
- if ($vType == 'utc' || in_array($v, $this->xpdo->driver->_currentDates) || $v === '0000-00-00') {
- $this->_fields[$k]= $v;
- $set= true;
- } else {
- if (strtolower($vType) == 'integer' || is_int($v)) {
- $ts= intval($v);
- } elseif (is_string($v) && !empty($v)) {
- $ts= strtotime($v);
- }
- $ts= strtotime($v);
- if ($ts !== false) {
- $this->_fields[$k]= strftime('%Y-%m-%d', $ts);
- $set= true;
- }
- }
- }
- break;
- case 'boolean' :
- $this->_fields[$k]= intval($v);
- $set= true;
- break;
- case 'integer' :
- $this->_fields[$k]= intval($v);
- $set= true;
- break;
- case 'array' :
- if (is_object($v) && $v instanceof xPDOObject) {
- $v = $v->toArray();
- }
- if (is_array($v)) {
- $this->_fields[$k]= serialize($v);
- $set= true;
- }
- break;
- case 'json' :
- if (is_object($v) && $v instanceof xPDOObject) {
- $v = $v->toArray();
- }
- if (is_string($v)) {
- $v= $this->xpdo->fromJSON($v, true);
- }
- if (is_array($v)) {
- $this->_fields[$k]= $this->xpdo->toJSON($v);
- $set= true;
- }
- break;
- default :
- $this->_fields[$k]= $v;
- $set= true;
- }
- }
- }
- }
- } elseif ($this->getOption(xPDO::OPT_HYDRATE_ADHOC_FIELDS)) {
- $oldValue= isset($this->_fields[$k]) ? $this->_fields[$k] : null;
- if ($callable) {
- $set = $callback($k, $v, $this);
- } else {
- $this->_fields[$k]= $v;
- $set= true;
- }
- }
- if ($set && $oldValue !== $this->_fields[$k]) {
- $this->setDirty($k);
- } else {
- $set= false;
- }
- if ($set && $this->getOption(xPDO::OPT_HYDRATE_FIELDS) && !isset ($this->$k)) {
- $this->$k= & $this->_fields[$k];
- }
- } else {
- $this->xpdo->log(xPDO::LOG_LEVEL_ERROR, 'xPDOObject - Called set() with an invalid field name: ' . print_r($k, 1));
- }
- return $set;
- }
- /**
- * Get a field value (or a set of values) by the field key(s) or name(s).
- *
- * Warning: do not use the $format parameter if retrieving multiple values of
- * different types, as the format string will be applied to all types, most
- * likely with unpredicatable results. Optionally, you can supply an associate
- * array of format strings with the field key as the key for the format array.
- *
- * @param string|array $k A string (or an array of strings) representing the field
- * key or name.
- * @param string|array $format An optional variable (or an array of variables) to
- * format the return value(s).
- * @param mixed $formatTemplate An additional optional variable that can be used in
- * formatting the return value(s).
- * @return mixed The value(s) of the field(s) requested.
- */
- public function get($k, $format = null, $formatTemplate= null) {
- $value= null;
- if (is_array($k)) {
- if ($this->isLazy()) {
- $this->_loadFieldData($k);
- }
- foreach ($k as $key) {
- if (array_key_exists($key, $this->_fields)) {
- if (is_array($format) && isset ($format[$key])) {
- $formatTpl= null;
- if (is_array ($formatTemplate) && isset ($formatTemplate[$key])) {
- $formatTpl= $formatTemplate[$key];
- }
- $value[$key]= $this->get($key, $format[$key], $formatTpl);
- } elseif (!empty ($format) && is_string($format)) {
- $value[$key]= $this->get($key, $format, $formatTemplate);
- } else {
- $value[$key]= $this->get($key);
- }
- }
- }
- } elseif (is_string($k) && !empty($k)) {
- if (array_key_exists($k, $this->_fields)) {
- if ($this->isLazy($k)) {
- $this->_loadFieldData($k);
- }
- $dbType= $this->_getDataType($k);
- $fieldType= $this->_getPHPType($k);
- $value= $this->_fields[$k];
- if ($value !== null) {
- switch ($fieldType) {
- case 'boolean' :
- $value= (boolean) $value;
- break;
- case 'integer' :
- $value= intval($value);
- if (is_string($format) && !empty ($format)) {
- if (strpos($format, 're:') === 0) {
- if (!empty ($formatTemplate) && is_string($formatTemplate)) {
- $value= preg_replace(substr($format, 3), $formatTemplate, $value);
- }
- } else {
- $value= sprintf($format, $value);
- }
- }
- break;
- case 'float' :
- $value= (float) $value;
- if (is_string($format) && !empty ($format)) {
- if (strpos($format, 're:') === 0) {
- if (!empty ($formatTemplate) && is_string($formatTemplate)) {
- $value= preg_replace(substr($format, 3), $formatTemplate, $value);
- }
- } else {
- $value= sprintf($format, $value);
- }
- }
- break;
- case 'timestamp' :
- case 'datetime' :
- if (preg_match('/int/i', $dbType)) {
- $ts= intval($value);
- } elseif (in_array($value, $this->xpdo->driver->_currentTimestamps)) {
- $ts= time();
- } else {
- $ts= strtotime($value);
- }
- if ($ts !== false && !empty($value)) {
- if (is_string($format) && !empty ($format)) {
- if (strpos($format, 're:') === 0) {
- $value= date('Y-m-d H:M:S', $ts);
- if (!empty ($formatTemplate) && is_string($formatTemplate)) {
- $value= preg_replace(substr($format, 3), $formatTemplate, $value);
- }
- } elseif (strpos($format, '%') === false) {
- $value= date($format, $ts);
- } else {
- $value= strftime($format, $ts);
- }
- } else {
- $value= strftime('%Y-%m-%d %H:%M:%S', $ts);
- }
- }
- break;
- case 'date' :
- if (preg_match('/int/i', $dbType)) {
- $ts= intval($value);
- } elseif (in_array($value, $this->xpdo->driver->_currentDates)) {
- $ts= time();
- } else {
- $ts= strtotime($value);
- }
- if ($ts !== false && !empty($value)) {
- if (is_string($format) && !empty ($format)) {
- if (strpos($format, 're:') === 0) {
- $value= strftime('%Y-%m-%d', $ts);
- if (!empty ($formatTemplate) && is_string($formatTemplate)) {
- $value= preg_replace(substr($format, 3), $formatTemplate, $value);
- }
- } elseif (strpos($format, '%') === false) {
- $value= date($format, $ts);
- } elseif ($ts !== false) {
- $value= strftime($format, $ts);
- }
- } else {
- $value= strftime('%Y-%m-%d', $ts);
- }
- }
- break;
- case 'array' :
- if (is_string($value)) {
- $value= unserialize($value);
- }
- break;
- case 'json' :
- if (is_string($value) && strlen($value) > 1) {
- $value= $this->xpdo->fromJSON($value, true);
- }
- break;
- default :
- if (is_string($format) && !empty ($format)) {
- if (strpos($format, 're:') === 0) {
- if (!empty ($formatTemplate) && is_string($formatTemplate)) {
- $value= preg_replace(substr($format, 3), $formatTemplate, $value);
- }
- } else {
- $value= sprintf($format, $value);
- }
- }
- break;
- }
- }
- }
- }
- return $value;
- }
- /**
- * Gets an object related to this instance by a foreign key relationship.
- *
- * Use this for 1:? (one:zero-or-one) or 1:1 relationships, which you can
- * distinguish by setting the nullability of the field representing the
- * foreign key.
- *
- * For all 1:* relationships for this instance, see {@link getMany()}.
- *
- * @see xPDOObject::getMany()
- * @see xPDOObject::addOne()
- * @see xPDOObject::addMany()
- *
- * @param string $alias Alias of the foreign class representing the related
- * object.
- * @param object $criteria xPDOCriteria object to get the related objects
- * @param boolean|integer $cacheFlag Indicates if the object should be
- * cached and optionally, by specifying an integer value, for how many
- * seconds.
- * @return xPDOObject|null The related object or null if no instance exists.
- */
- public function & getOne($alias, $criteria= null, $cacheFlag= true) {
- $object= null;
- if ($fkdef= $this->getFKDefinition($alias)) {
- $k= $fkdef['local'];
- $fk= $fkdef['foreign'];
- if (isset ($this->_relatedObjects[$alias])) {
- if (is_object($this->_relatedObjects[$alias])) {
- $object= & $this->_relatedObjects[$alias];
- return $object;
- }
- }
- if ($criteria === null) {
- $criteria= array ($fk => $this->get($k));
- }
- if ($object= $this->xpdo->getObject($fkdef['class'], $criteria, $cacheFlag)) {
- $this->_relatedObjects[$alias]= $object;
- }
- } else {
- $this->xpdo->log(xPDO::LOG_LEVEL_WARN, "Could not getOne: foreign key definition for alias {$alias} not found.");
- }
- return $object;
- }
- /**
- * Gets a collection of objects related by aggregate or composite relations.
- *
- * @see xPDOObject::getOne()
- * @see xPDOObject::addOne()
- * @see xPDOObject::addMany()
- *
- * @param string $alias Alias of the foreign class representing the related
- * object.
- * @param object $criteria xPDOCriteria object to get the related objects
- * @param boolean|integer $cacheFlag Indicates if the objects should be
- * cached and optionally, by specifying an integer value, for how many
- * seconds.
- * @return array A collection of related objects or an empty array.
- */
- public function & getMany($alias, $criteria= null, $cacheFlag= true) {
- $collection= $this->_getRelatedObjectsByFK($alias, $criteria, $cacheFlag);
- return $collection;
- }
- /**
- * Adds an object related to this instance by a foreign key relationship.
- *
- * @see xPDOObject::getOne()
- * @see xPDOObject::getMany()
- * @see xPDOObject::addMany()
- *
- * @param mixed &$obj A single object to be related to this instance.
- * @param string $alias The relation alias of the related object (only
- * required if more than one relation exists to the same foreign class).
- * @return boolean True if the related object was added to this object.
- */
- public function addOne(& $obj, $alias= '') {
- $added= false;
- if (is_object($obj)) {
- if (empty ($alias)) {
- if ($obj->_alias == $obj->_class) {
- $aliases = $this->_getAliases($obj->_class, 1);
- if (!empty($aliases)) {
- $obj->_alias = reset($aliases);
- }
- }
- $alias= $obj->_alias;
- }
- $fkMeta= $this->getFKDefinition($alias);
- if ($fkMeta && $fkMeta['cardinality'] === 'one') {
- $obj->_alias= $alias;
- $fk= $fkMeta['foreign'];
- $key= $fkMeta['local'];
- $owner= isset ($fkMeta['owner']) ? $fkMeta['owner'] : 'local';
- $kval= $this->get($key);
- $fkval= $obj->get($fk);
- if ($owner == 'local') {
- $obj->set($fk, $kval);
- }
- else {
- $this->set($key, $fkval);
- }
- $this->_relatedObjects[$obj->_alias]= $obj;
- $this->setDirty($key);
- $added= true;
- } else {
- $this->xpdo->log(xPDO::LOG_LEVEL_WARN, "Foreign key definition for class {$obj->class}, alias {$obj->_alias} not found, or cardinality is not 'one'.");
- }
- } else {
- $this->xpdo->log(xPDO::LOG_LEVEL_WARN, "Attempt to add a non-object to a relation with alias ({$alias})");
- }
- if (!$added) {
- $this->xpdo->log(xPDO::LOG_LEVEL_WARN, "Could not add related object! " . (is_object($obj) ? print_r($obj->toArray(), true) : ''));
- }
- return $added;
- }
- /**
- * Adds an object or collection of objects related to this class.
- *
- * This method adds an object or collection of objects in a one-to-
- * many foreign key relationship with this object to the internal list of
- * related objects. By adding these related objects, you can cascade
- * {@link xPDOObject::save()}, {@link xPDOObject::remove()}, and other
- * operations based on the type of relationships defined.
- *
- * @see xPDOObject::addOne()
- * @see xPDOObject::getOne()
- * @see xPDOObject::getMany()
- *
- * @param mixed &$obj A single object or collection of objects to be related
- * to this instance via the intersection class.
- * @param string $alias An optional alias, required only for instances where
- * you have mo…
Large files files are truncated, but you can click here to view the full file