1
0
mirror of synced 2025-01-22 08:11:40 +03:00

moving towards transparent persistence.

This commit is contained in:
romanb 2008-12-18 14:08:11 +00:00
parent 2aea62a498
commit 9971057076
64 changed files with 2872 additions and 2858 deletions

View File

@ -36,70 +36,6 @@
*/ */
final class Doctrine final class Doctrine
{ {
/**
* DEPRECATED ATTRIBUTE CONSTANT NAMES AND VALUES
*
* REMOVE BEFORE 1.0
* These were deprecated either because they are not used anymore or because they didn't follow the naming pattern required
* by Doctrine attributes
*
* Attribute names must be ATTR_{ATTR_NAME} and the values must be {ATTR_NAME}_NAME_OF_VALUE
*/
const LIMIT_ROWS = 1;
const LIMIT_RECORDS = 2;
const FETCH_IMMEDIATE = 0;
const FETCH_BATCH = 1;
const FETCH_OFFSET = 3;
const FETCH_LAZY_OFFSET = 4;
const FETCH_VHOLDER = 1;
const FETCH_RECORD = 2;
const FETCH_ARRAY = 3;
/**
* VERSION
*/
const VERSION = '2.0.0';
/**
* ERROR CONSTANTS
*/
const ERR = -1;
const ERR_SYNTAX = -2;
const ERR_CONSTRAINT = -3;
const ERR_NOT_FOUND = -4;
const ERR_ALREADY_EXISTS = -5;
const ERR_UNSUPPORTED = -6;
const ERR_MISMATCH = -7;
const ERR_INVALID = -8;
const ERR_NOT_CAPABLE = -9;
const ERR_TRUNCATED = -10;
const ERR_INVALID_NUMBER = -11;
const ERR_INVALID_DATE = -12;
const ERR_DIVZERO = -13;
const ERR_NODBSELECTED = -14;
const ERR_CANNOT_CREATE = -15;
const ERR_CANNOT_DELETE = -16;
const ERR_CANNOT_DROP = -17;
const ERR_NOSUCHTABLE = -18;
const ERR_NOSUCHFIELD = -19;
const ERR_NEED_MORE_DATA = -20;
const ERR_NOT_LOCKED = -21;
const ERR_VALUE_COUNT_ON_ROW = -22;
const ERR_INVALID_DSN = -23;
const ERR_CONNECT_FAILED = -24;
const ERR_EXTENSION_NOT_FOUND = -25;
const ERR_NOSUCHDB = -26;
const ERR_ACCESS_VIOLATION = -27;
const ERR_CANNOT_REPLACE = -28;
const ERR_CONSTRAINT_NOT_NULL = -29;
const ERR_DEADLOCK = -30;
const ERR_CANNOT_ALTER = -31;
const ERR_MANAGER = -32;
const ERR_MANAGER_PARSE = -33;
const ERR_LOADMODULE = -34;
const ERR_INSUFFICIENT_DATA = -35;
const ERR_CLASS_NAME = -36;
/** /**
* PDO derived constants * PDO derived constants
*/ */
@ -142,14 +78,6 @@ final class Doctrine
const PARAM_NULL = 0; const PARAM_NULL = 0;
const PARAM_STMT = 4; const PARAM_STMT = 4;
const PARAM_STR = 2; const PARAM_STR = 2;
/**
* ATTRIBUTE CONSTANTS
*/
/**
* PDO derived attributes
*/
const ATTR_AUTOCOMMIT = 0; const ATTR_AUTOCOMMIT = 0;
const ATTR_PREFETCH = 1; const ATTR_PREFETCH = 1;
const ATTR_TIMEOUT = 2; const ATTR_TIMEOUT = 2;
@ -170,387 +98,6 @@ final class Doctrine
const ATTR_STRINGIFY_FETCHES = 17; const ATTR_STRINGIFY_FETCHES = 17;
const ATTR_MAX_COLUMN_LEN = 18; const ATTR_MAX_COLUMN_LEN = 18;
/**
* Doctrine constants
*/
const ATTR_LISTENER = 100;
const ATTR_QUOTE_IDENTIFIER = 101; // manager/session attribute
const ATTR_FIELD_CASE = 102; // manager/session attribute
const ATTR_IDXNAME_FORMAT = 103; // manager/session attribute
const ATTR_SEQNAME_FORMAT = 104; // manager/session attribute
const ATTR_SEQCOL_NAME = 105; // class attribute
const ATTR_CMPNAME_FORMAT = 118; // ??
const ATTR_DBNAME_FORMAT = 117; // manager/session attribute
const ATTR_TBLCLASS_FORMAT = 119; // manager/session attribute
const ATTR_TBLNAME_FORMAT = 120; // manager/session attribute
const ATTR_EXPORT = 140; // manager/session attribute
const ATTR_DECIMAL_PLACES = 141; // manager/session attribute
const ATTR_PORTABILITY = 106; // manager/session attribute
const ATTR_COLL_KEY = 108; // class attribute
const ATTR_QUERY_LIMIT = 109; // manager/session attribute
const ATTR_DEFAULT_TABLE_TYPE = 112; // manager/session attribute
const ATTR_DEF_TEXT_LENGTH = 113; // manager/session attribute
const ATTR_DEF_VARCHAR_LENGTH = 114; // manager/session attribute
const ATTR_DEF_TABLESPACE = 115; // manager/session attribute
const ATTR_EMULATE_DATABASE = 116; // manager/session attribute
const ATTR_USE_NATIVE_ENUM = 117; // manager/session attribute
const ATTR_DEFAULT_SEQUENCE = 133; // ??
const ATTR_FETCHMODE = 118; // deprecated? might use them again for associations
const ATTR_NAME_PREFIX = 121; // ??
const ATTR_CREATE_TABLES = 122; // manager/session attribute
const ATTR_COLL_LIMIT = 123; // manager/session attribute
const ATTR_RESULT_CACHE = 150; // manager/session attribute
const ATTR_RESULT_CACHE_LIFESPAN = 151; // manager/session attribute
const ATTR_LOAD_REFERENCES = 153; // class attribute
const ATTR_RECORD_LISTENER = 154;
const ATTR_THROW_EXCEPTIONS = 155; // manager/session attribute
const ATTR_DEFAULT_PARAM_NAMESPACE = 156; // ??
const ATTR_QUERY_CACHE = 157; // manager/session attribute
const ATTR_QUERY_CACHE_LIFESPAN = 158; // manager/session attribute
const ATTR_MODEL_LOADING = 161; // manager/session attribute
const ATTR_HYDRATE = 163; // ??
const ATTR_IDENTIFIER = 164; // ??
const ATTR_METADATA_CACHE = 165; // manager/session attribute
const ATTR_METADATA_CACHE_LIFESPAN = 166; // manager/session attribute
/**
* QUERY_LIMIT CONSTANTS
*/
/**
* QUERY_LIMIT_ROWS
*
* constant for row limiting
*
* @see self::ATTR_QUERY_LIMIT
*/
const QUERY_LIMIT_ROWS = 1;
/**
* constant for record limiting
* @see self::ATTR_QUERY_LIMIT
*/
const QUERY_LIMIT_RECORDS = 2;
/**
* FETCHMODE CONSTANTS
*/
/**
* IMMEDIATE FETCHING
* mode for immediate fetching
* @see self::ATTR_FETCHMODE
* @deprecated???
*/
const FETCHMODE_IMMEDIATE = 0;
/**
* FETCHMODE_BATCH
*
* mode for batch fetching
*
* @see self::ATTR_FETCHMODE
* @deprecated???
*/
const FETCHMODE_BATCH = 1;
/**
* FETCHMODE_OFFSET
*
* mode for offset fetching
*
* @see self::ATTR_FETCHMODE
* @deprecated???
*/
const FETCHMODE_OFFSET = 3;
/**
* FETCHMODE_LAZY_OFFSET
*
* mode for lazy offset fetching
*
* @see self::ATTR_FETCHMODE
* @deprecated???
*/
const FETCHMODE_LAZY_OFFSET = 4;
/**
* FETCHMODE CONSTANTS
*/
/**
* FETCHMODE_VHOLDER
*
* @see self::ATTR_FETCHMODE
* @deprecated???
*/
const FETCHMODE_VHOLDER = 1;
/**
* FETCHMODE_RECORD
*
* Specifies that the fetch method shall return Doctrine_Entity
* objects as the elements of the result set.
*
* This is the default fetchmode.
*
* @see self::ATTR_FETCHMODE
* @deprecated???
*/
const FETCHMODE_RECORD = 2;
/**
* FETCHMODE_ARRAY
*
* @see self::ATTR_FETCHMODE
* @deprecated???
*/
const FETCHMODE_ARRAY = 3;
/**
* PORTABILITY CONSTANTS
*/
/**
* PORTABILITY_NONE
*
* Portability: turn off all portability features.
*
* @see self::ATTR_PORTABILITY
*/
const PORTABILITY_NONE = 0;
/**
* PORTABILITY_FIX_CASE
*
* Portability: convert names of tables and fields to case defined in the
* "field_case" option when using the query*(), fetch*() methods.
*
* @see self::ATTR_PORTABILITY
*/
const PORTABILITY_FIX_CASE = 1;
/**
* PORTABILITY_RTRIM
*
* Portability: right trim the data output by query*() and fetch*().
*
* @see self::ATTR_PORTABILITY
*/
const PORTABILITY_RTRIM = 2;
/**
* PORTABILITY_DELETE_COUNT
*
* Portability: force reporting the number of rows deleted.
*
* @see self::ATTR_PORTABILITY
*/
const PORTABILITY_DELETE_COUNT = 4;
/**
* PORTABILITY_EMPTY_TO_NULL
*
* Portability: convert empty values to null strings in data output by
* query*() and fetch*().
*
* @see self::ATTR_PORTABILITY
*/
const PORTABILITY_EMPTY_TO_NULL = 8;
/**
* PORTABILITY_FIX_ASSOC_FIELD_NAMES
*
* Portability: removes database/table qualifiers from associative indexes
*
* @see self::ATTR_PORTABILITY
*/
const PORTABILITY_FIX_ASSOC_FIELD_NAMES = 16;
/**
* PORTABILITY_EXPR
*
* Portability: makes Doctrine_Expression throw exception for unportable RDBMS expressions
*
* @see self::ATTR_PORTABILITY
*/
const PORTABILITY_EXPR = 32;
/**
* PORTABILITY_ALL
*
* Portability: turn on all portability features.
*
* @see self::ATTR_PORTABILITY
*/
const PORTABILITY_ALL = 63;
/**
* LOCK CONSTANTS
*/
/**
* LOCK_OPTIMISTIC
*
* mode for optimistic locking
* @see self::ATTR_LOCK
* @deprecated???
*/
const LOCK_OPTIMISTIC = 0;
/**
* LOCK_PESSIMISTIC
*
* mode for pessimistic locking
*
* @see self::ATTR_LOCK
* @deprecated???
*/
const LOCK_PESSIMISTIC = 1;
/**
* EXPORT CONSTANTS
*/
/**
* EXPORT_NONE
*
* @see self::ATTR_EXPORT
*/
const EXPORT_NONE = 0;
/**
* EXPORT_TABLES
*
* @see self::ATTR_EXPORT
*/
const EXPORT_TABLES = 1;
/**
* EXPORT_CONSTRAINTS
*
* @see self::ATTR_EXPORT
*/
const EXPORT_CONSTRAINTS = 2;
/**
* EXPORT_PLUGINS
*
* @see self::ATTR_EXPORT
*/
const EXPORT_PLUGINS = 4;
/**
* EXPORT_ALL
*
* @see self::ATTR_EXPORT
*/
const EXPORT_ALL = 7;
/**
* HYDRATE CONSTANTS
*/
/**
* HYDRATE_RECORD
*
* @see self::ATTR_HYDRATE
*/
const HYDRATE_RECORD = 2;
/**
* HYDRATE_ARRAY
*
* @see self::ATTR_HYDRATE
*/
const HYDRATE_ARRAY = 3;
/**
* HYDRATE_NONE
*
* @see self::ATTR_HYDRATE
*/
const HYDRATE_NONE = 4;
/* new hydration modes. move to Query class when it's time. */
const HYDRATE_IDENTITY_OBJECT = 2; // default, auto-adds PKs, produces object graphs
const HYDRATE_IDENTITY_ARRAY = 3; // auto-adds PKs, produces array graphs
const HYDRATE_SCALAR = 5; // produces flat result list with scalar values
const HYDRATE_SINGLE_SCALAR = 6; // produces a single scalar value
//const HYDRATE_NONE = 4; // produces a result set as it's returned by the db
/**
* VALIDATE CONSTANTS
*
* @see self::ATTR_VALIDATE
*/
const VALIDATE_NONE = 0;
/**
* VALIDATE_LENGTHS
*
* @see self::ATTR_VALIDATE
*/
const VALIDATE_LENGTHS = 1;
/**
* VALIDATE_TYPES
*
* @see self::ATTR_VALIDATE
*/
const VALIDATE_TYPES = 2;
/**
* VALIDATE_CONSTRAINTS
*
* @see self::ATTR_VALIDATE
* Not used? Purpose?
*/
const VALIDATE_CONSTRAINTS = 4;
/**
* VALIDATE_ALL
*
* @see self::ATTR_VALIDATE
*/
const VALIDATE_ALL = 7;
/**
* IDENTIFIER_AUTOINC
*
* constant for auto_increment identifier
*
* @see self::ATTR_IDENTIFIER
*/
const IDENTIFIER_AUTOINC = 1;
/**
* IDENTIFIER_SEQUENCE
*
* constant for sequence identifier
*
* @see self::ATTR_IDENTIFIER
*/
const IDENTIFIER_SEQUENCE = 2;
/**
* IDENTIFIER_NATURAL
*
* constant for normal identifier
*
* @see self::ATTR_IDENTIFIER
*/
const IDENTIFIER_NATURAL = 3;
/**
* IDENTIFIER_COMPOSITE
*
* constant for composite identifier
* @see self::ATTR_IDENTIFIER
*/
const IDENTIFIER_COMPOSITE = 4;
/** /**
* MODEL_LOADING_AGGRESSIVE * MODEL_LOADING_AGGRESSIVE
* *
@ -572,33 +119,6 @@ final class Doctrine
*/ */
const MODEL_LOADING_CONSERVATIVE = 2; const MODEL_LOADING_CONSERVATIVE = 2;
/**
* INHERITANCE TYPE CONSTANTS.
*/
const INHERITANCE_TYPE_NONE = 0;
/**
* Constant for Single Table Inheritance.
*
* @see http://martinfowler.com/eaaCatalog/singleTableInheritance.html
*/
const INHERITANCE_TYPE_SINGLE_TABLE = 1;
/**
* Constant for Class Table Inheritance.
*
* @see http://martinfowler.com/eaaCatalog/classTableInheritance.html
*/
const INHERITANCE_TYPE_JOINED = 2;
/**
* Constant for Concrete Table Inheritance.
*
* @see http://martinfowler.com/eaaCatalog/concreteTableInheritance.html
*/
const INHERITANCE_TYPE_TABLE_PER_CLASS = 3;
/** /**
* Path * Path
* *
@ -614,7 +134,6 @@ final class Doctrine
* @var array * @var array
*/ */
private static $_loadedModelFiles = array(); private static $_loadedModelFiles = array();
private static $_pathModels = array(); private static $_pathModels = array();
/** /**
@ -1115,44 +634,6 @@ final class Doctrine
} }
} }
/**
* dump
*
* dumps a given variable
*
* @param mixed $var a variable of any type
* @param boolean $output whether to output the content
* @param string $indent indention string
* @return void|string
*/
public static function dump($var, $output = true, $indent = "")
{
$ret = array();
switch (gettype($var)) {
case 'array':
$ret[] = 'Array(';
$indent .= " ";
foreach ($var as $k => $v) {
$ret[] = $indent . $k . ' : ' . self::dump($v, false, $indent);
}
$indent = substr($indent,0, -4);
$ret[] = $indent . ")";
break;
case 'object':
$ret[] = 'Object(' . get_class($var) . ')';
break;
default:
$ret[] = var_export($var, true);
}
if ($output) {
print implode("\n", $ret);
}
return implode("\n", $ret);
}
/** /**
* tableize * tableize
* *

View File

@ -6,9 +6,10 @@
* Usage recommendation: * Usage recommendation:
* 1) Use only 1 class loader instance. * 1) Use only 1 class loader instance.
* 2) Prepend the base paths to your class libraries (including Doctrine's) to your include path. * 2) Prepend the base paths to your class libraries (including Doctrine's) to your include path.
* 3) DO NOT setCheckFileExists(true). Doing so is expensive. * 3) DO NOT setCheckFileExists(true). Doing so is expensive in terms of performance.
* *
* @since 2.0 * @since 2.0
* @author romanb <roman@code-factory.org>
*/ */
class Doctrine_Common_ClassLoader class Doctrine_Common_ClassLoader
{ {
@ -79,12 +80,13 @@ class Doctrine_Common_ClassLoader
/** /**
* Registers this class loader using spl_autoload_register(). * Registers this class loader using spl_autoload_register().
*
* @return void
*/ */
public function register() public function register()
{ {
spl_autoload_register(array($this, 'loadClass')); spl_autoload_register(array($this, 'loadClass'));
} }
} }

View File

@ -0,0 +1,139 @@
<?php
#namespace Doctrine\Common;
/**
* The ClassMetadata class represents a generic container for metadata of a class.
*
* @author robo
*/
class Doctrine_Common_ClassMetadata
{
/** The metadata that applies to the class. */
protected $_classMetadata = array();
/** The metadata that applies to properties of the class. */
protected $_fieldMetadata = array();
protected $_entityName;
/**
*
*
* @param <type> $className
*/
public function __construct($className)
{
$this->_entityName = $className;
}
/**
* Adds metadata to a property of the class.
*
* @param string $fieldName
* @param array $fieldMetadata
*/
public function addFieldMetadata($fieldName, array $fieldMetadata)
{
$this->_fieldMetadata[$fieldName] = array_merge(
isset($this->_fieldMetadata[$fieldName]) ? $this->_fieldMetadata[$fieldName] : array(),
$fieldMetadata);
}
/**
*
*
* @param <type> $fieldName
* @param <type> $metadata
*/
public function setFieldMetadata($fieldName, array $metadata)
{
$this->_fieldMetadata[$fieldName] = $metadata;
}
/**
*
* @param <type> $fieldName
* @param <type> $metadataKey
* @param <type> $value
*/
public function setFieldMetadataEntry($fieldName, $metadataKey, $value)
{
$this->_fieldMetadata[$fieldName][$metadataKey] = $value;
}
/**
* Gets metadata of a property of the class.
*
* @param string $fieldName
* @param string $metadataKey
* @return mixed
*/
public function getFieldMetadata($fieldName)
{
return $this->_fieldMetadata[$fieldName];
}
/**
*
* @param <type> $fieldName
* @param <type> $metadataKey
* @return <type>
*/
public function getFieldMetadataEntry($fieldName, $metadataKey)
{
return isset($this->_fieldMetadata[$fieldName][$metadataKey]) ?
$this->_fieldMetadata[$fieldName][$metadataKey] : null;
}
/**
* Gets metadata of the class.
*
* @param string $metadataKey
* @return mixed
*/
public function getClassMetadata()
{
return $this->_classMetadata;
}
/**
*
*
* @param <type> $metadataKey
*/
public function getClassMetadataEntry($metadataKey)
{
return isset($this->_classMetadata[$metadataKey]) ?
$this->_classMetadata[$metadataKey] : null;
}
/**
* Adds metadata to the class.
*
* @param array $classMetadata
*/
public function addClassMetadata(array $classMetadata)
{
$this->_classMetadata = array_merge($this->_classMetadata, $classMetadata);
}
/**
*
*
* @param <type> $metadata
*/
public function setClassMetadata(array $metadata)
{
$this->_classMetadata = $metadata;
}
/**
*
* @param <type> $metadataKey
* @param <type> $value
*/
public function setClassMetadataEntry($metadataKey, $value)
{
$this->_classMetadata[$metadataKey] = $value;
}
}
?>

View File

@ -0,0 +1,59 @@
<?php
#namespace Doctrine\Common;
/**
* Description of ClassMetadataFactory
*
* @author robo
*/
class Doctrine_Common_ClassMetadataFactory {
/** The factory driver. */
protected $_driver;
/** Map of the already loaded ClassMetadata instances, indexed by class name. */
protected $_loadedMetadata = array();
/**
* Creates a new factory instance that uses the given metadata driver implementation.
*
* @param $driver The metadata driver to use.
*/
public function __construct($driver)
{
$this->_driver = $driver;
}
/**
* Returns the metadata object for a class.
*
* @param string $className The name of the class.
* @return Doctrine_Metadata
*/
public function getMetadataFor($className)
{
if ( ! isset($this->_loadedMetadata[$className])) {
$this->_loadMetadata($className);
$this->_validateAndCompleteMetadata($className);
}
return $this->_loadedMetadata[$className];
}
/**
* Loads the metadata for the given class.
*
* @param <type> $className
* @return <type>
*/
protected function _loadMetadata($className)
{
$classMetadata = new Doctrine_Common_ClassMetadata();
$this->_driver->loadMetadataForClass($className, $classMetadata);
return $classMetadata;
}
/** Template method for subclasses */
protected function _validateAndCompleteMetadata($className)
{ /*empty*/ }
}
?>

View File

@ -0,0 +1,372 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
#namespace Doctrine\Common\Collections;
#use \Countable;
#use \IteratorAggregate;
#use \Serializable;
#use \ArrayAccess;
/**
* A Collection is a wrapper around a php array and just like a php array a
* collection instance can be a list, a map or a hashmap, depending on how it
* is used.
*
* @author robo
*/
class Doctrine_Common_Collection implements Countable, IteratorAggregate, Serializable, ArrayAccess {
/**
* An array containing the entries of this collection.
* This is the wrapped php array.
*
* @var array
*/
protected $_data = array();
/**
* Unwraps the array contained in the Collection instance.
*
* @return array The wrapped array.
*/
public function unwrap()
{
return $this->_data;
}
/**
* returns the first record in the collection
*
* @return mixed
*/
public function getFirst()
{
return reset($this->_data);
}
/**
* returns the last record in the collection
*
* @return mixed
*/
public function getLast()
{
return end($this->_data);
}
/**
* returns the last record in the collection
*
* @return mixed
*/
public function end()
{
return end($this->_data);
}
/**
* returns the current key
*
* @return mixed
*/
public function key()
{
return key($this->_data);
}
/**
* Removes an entry from the collection.
*
* @param mixed $key
* @return boolean
*/
public function remove($key)
{
$removed = $this->_data[$key];
unset($this->_data[$key]);
return $removed;
}
/**
* __isset()
*
* @param string $name
* @return boolean whether or not this object contains $name
*/
public function __isset($key)
{
return $this->containsKey($key);
}
/**
* __unset()
*
* @param string $name
* @since 1.0
* @return mixed
*/
public function __unset($key)
{
return $this->remove($key);
}
/**
* Check if an offsetExists.
*
* Part of the ArrayAccess implementation.
*
* @param mixed $offset
* @return boolean whether or not this object contains $offset
*/
public function offsetExists($offset)
{
return $this->containsKey($offset);
}
/**
* offsetGet an alias of get()
*
* Part of the ArrayAccess implementation.
*
* @see get, __get
* @param mixed $offset
* @return mixed
*/
public function offsetGet($offset)
{
return $this->get($offset);
}
/**
* Part of the ArrayAccess implementation.
*
* sets $offset to $value
* @see set, __set
* @param mixed $offset
* @param mixed $value
* @return void
*/
public function offsetSet($offset, $value)
{
if ( ! isset($offset)) {
return $this->add($value);
}
return $this->set($offset, $value);
}
/**
* Part of the ArrayAccess implementation.
*
* unset a given offset
* @see set, offsetSet, __set
* @param mixed $offset
*/
public function offsetUnset($offset)
{
return $this->remove($offset);
}
/**
* Checks whether the collection contains an entity.
*
* @param mixed $key the key of the element
* @return boolean
*/
public function containsKey($key)
{
return isset($this->_data[$key]);
}
/**
* Enter description here...
*
* @param unknown_type $entity
* @return unknown
*/
public function contains($entity)
{
return in_array($entity, $this->_data, true);
}
/**
* Enter description here...
*
* @param unknown_type $otherColl
* @todo Impl
*/
public function containsAll($otherColl)
{
//...
}
/**
*
*/
public function search(Doctrine_ORM_Entity $record)
{
return array_search($record, $this->_data, true);
}
/**
* returns a record for given key
*
* Collection also maps referential information to newly created records
*
* @param mixed $key the key of the element
* @return Doctrine_Entity return a specified record
*/
public function get($key)
{
if (isset($this->_data[$key])) {
return $this->_data[$key];
}
return null;
}
/**
* Gets all keys.
* (Map method)
*
* @return array
*/
public function getKeys()
{
return array_keys($this->_data);
}
/**
* Gets all values.
* (Map method)
*
* @return array
*/
public function getValues()
{
return array_values($this->_data);
}
/**
* Returns the number of records in this collection.
*
* Implementation of the Countable interface.
*
* @return integer The number of records in the collection.
*/
public function count()
{
return count($this->_data);
}
/**
* When the collection is a Map this is like put(key,value)/add(key,value).
* When the collection is a List this is like add(position,value).
*
* @param integer $key
* @param mixed $value
* @return void
*/
public function set($key, $value)
{
if ( ! $value instanceof Doctrine_ORM_Entity) {
throw new Doctrine_Collection_Exception('Value variable in set is not an instance of Doctrine_Entity');
}
$this->_data[$key] = $value;
//TODO: Register collection as dirty with the UoW if necessary
$this->_changed();
}
/**
* Adds an entry to the collection.
*
* @param mixed $value
* @param string $key
* @return boolean
*/
public function add($value, $key = null)
{
//TODO: really only allow entities?
if ( ! $value instanceof Doctrine_ORM_Entity) {
throw new Doctrine_Record_Exception('Value variable in collection is not an instance of Doctrine_Entity.');
}
// TODO: Really prohibit duplicates?
if (in_array($value, $this->_data, true)) {
return false;
}
if (isset($key)) {
if (isset($this->_data[$key])) {
return false;
}
$this->_data[$key] = $value;
} else {
$this->_data[] = $value;
}
if ($this->_hydrationFlag) {
if ($this->_backRefFieldName) {
// set back reference to owner
$value->_internalSetReference($this->_backRefFieldName, $this->_owner);
}
} else {
//TODO: Register collection as dirty with the UoW if necessary
$this->_changed();
}
return true;
}
/**
* Adds all entities of the other collection to this collection.
*
* @param unknown_type $otherCollection
* @todo Impl
*/
public function addAll($otherCollection)
{
}
/**
* Checks whether the collection is empty.
*
* @return boolean TRUE if the collection is empty, FALSE otherwise.
*/
public function isEmpty()
{
// Note: Little "trick". Empty arrays evaluate to FALSE. No need to count().
return ! (bool)$this->_data;
}
/**
* getIterator
*
* @return object ArrayIterator
*/
public function getIterator()
{
$data = $this->_data;
return new ArrayIterator($data);
}
/**
* returns a string representation of this object
*/
public function __toString()
{
return Doctrine_Lib::getCollectionAsString($this);
}
/**
* Clears the collection.
*
* @return void
*/
public function clear()
{
$this->_data = array();
}
}
?>

View File

@ -140,7 +140,7 @@ class Doctrine_Common_Configuration
public function setTypeOverrides(array $overrides) public function setTypeOverrides(array $overrides)
{ {
foreach ($override as $name => $typeClassName) { foreach ($override as $name => $typeClassName) {
Doctrine_DataType::overrideType($name, $typeClassName); Doctrine_DBAL_Types_Type::overrideType($name, $typeClassName);
} }
} }
} }

View File

@ -92,11 +92,7 @@ class Doctrine_Common_EventManager
public function addEventListener($events, $listener) public function addEventListener($events, $listener)
{ {
// TODO: maybe check for duplicate registrations? // TODO: maybe check for duplicate registrations?
if ( ! is_array($events)) { foreach ((array)$events as $event) {
$events = array($events);
}
foreach ($events as $event) {
$this->_listeners[$event] = $listener; $this->_listeners[$event] = $listener;
} }
} }

View File

@ -22,7 +22,7 @@
#namespace Doctrine::Common; #namespace Doctrine::Common;
/** /**
* An EventSubscriber knows itself what events it is interested in. * An EventSubscriber knows himself what events he is interested in.
* If an EventSubscriber is added to an EventManager, the manager invokes * If an EventSubscriber is added to an EventManager, the manager invokes
* getSubscribedEvents() and registers the subscriber as a listener for all * getSubscribedEvents() and registers the subscriber as a listener for all
* returned events. * returned events.

View File

@ -39,7 +39,6 @@ class Doctrine_Common_Events_Event
const postDelete = 'postDelete'; const postDelete = 'postDelete';
//...more //...more
protected $_type; protected $_type;
protected $_target; protected $_target;
protected $_defaultPrevented; protected $_defaultPrevented;

View File

@ -17,7 +17,7 @@ class Doctrine_Common_Exceptions_DoctrineException extends Exception
public static function notImplemented($method, $class) public static function notImplemented($method, $class)
{ {
return new self("The method '$method' is not implemented in the class '$class'."); return new self("The method '$method' is not implemented in class '$class'.");
} }
} }

View File

@ -0,0 +1,260 @@
<?php
#namespace Doctrine\Common;
#use \ArrayAccess;
/**
* Base class for classes that use the virtual property system.
*
* @author robo
*/
class Doctrine_Common_VirtualPropertyObject implements ArrayAccess
{
protected $_data = array();
protected $_entityName;
/**
* Initializes a new instance of a class derived from VirtualPropertyObject.
*/
public function __construct()
{
$this->_entityName = get_class($this);
if ( ! Doctrine_Common_VirtualPropertySystem::isInitialized($this->_entityName)) {
Doctrine_Common_VirtualPropertySystem::initialize($this->_entityName);
}
}
/**
* Generic getter for virtual properties.
*
* @param string $fieldName Name of the field.
* @return mixed
*/
final public function get($fieldName)
{
if ( ! Doctrine_Common_VirtualPropertySystem::hasProperty($this->_entityName, $fieldName)) {
throw new Doctrine_Exception("Access of undefined property '$fieldName'.");
}
$getter = $this->_getCustomAccessor($fieldName);
if ($getter) {
return $this->$getter();
}
return $this->_get($fieldName);
}
/**
* Generic setter for virtual properties.
*
* @param string $name The name of the field to set.
* @param mixed $value The value of the field.
*/
final public function set($fieldName, $value)
{
if ( ! Doctrine_Common_VirtualPropertySystem::hasProperty($this->_entityName, $fieldName)) {
throw new Doctrine_Exception("Access of undefined property '$fieldName'.");
}
if (Doctrine_Common_VirtualPropertySystem::isTypeCheckEnabled()) {
$this->_checkType($fieldName, $value);
}
$setter = $this->_getCustomMutator($fieldName);
if ($setter) {
return $this->$setter($value);
}
$this->_set($fieldName, $value);
}
/**
* Checks the type of a virtual property.
*
* @param <type> $fieldName
* @param <type> $value
*/
protected function _checkType($fieldName, $value)
{
$type = Doctrine_Common_VirtualPropertySystem::getType($this->_entityName, $fieldName);
if (Doctrine_Common_VirtualPropertySystem::isSimplePHPType($type)) {
$is_type = "is_$type";
if ( ! $is_type($value)) {
throw new Doctrine_Exception("'$value' is of an invalid type. Expected: $type.");
}
} else if ($type == 'array') {
if ( ! is_array($value)) {
throw new Doctrine_Exception("'$value' is of an invalid type. Expected: array.");
}
} else {
if ( ! $value instanceof $type) {
throw new Doctrine_Exception("'$value' is of an invalid type. Expected: $type.");
}
}
}
protected function _get($fieldName)
{
return isset($this->_data[$fieldName]) ? $this->_data[$fieldName] : null;
}
protected function _set($fieldName, $value)
{
$this->_data[$fieldName] = $value;
}
/**
* Gets the custom mutator method for a virtual property, if it exists.
*
* @param string $fieldName The field name.
* @return mixed The name of the custom mutator or FALSE, if the field does
* not have a custom mutator.
*/
private function _getCustomMutator($fieldName)
{
if (Doctrine_Common_VirtualPropertySystem::getMutator($this->_entityName, $fieldName) === null) {
if (Doctrine_Common_VirtualPropertySystem::isAutoAccessorOverride()) {
$setterMethod = 'set' . Doctrine::classify($fieldName);
if ( ! method_exists($this, $setterMethod)) {
$setterMethod = false;
}
Doctrine_Common_VirtualPropertySystem::setMutator(
$this->_entityName, $fieldName, $setterMethod);
} else {
Doctrine_Common_VirtualPropertySystem::setMutator(
$this->_entityName, $fieldName, false);
}
}
return Doctrine_Common_VirtualPropertySystem::getMutator($this->_entityName, $fieldName);
}
/**
* Gets the custom accessor method of a virtual property, if it exists.
*
* @param string $fieldName The field name.
* @return mixed The name of the custom accessor method, or FALSE if the
* field does not have a custom accessor.
*/
private function _getCustomAccessor($fieldName)
{
if (Doctrine_Common_VirtualPropertySystem::getAccessor($this->_entityName, $fieldName) === null) {
if (Doctrine_Common_VirtualPropertySystem::isAutoAccessorOverride()) {
$getterMethod = 'get' . Doctrine::classify($fieldName);
if ( ! method_exists($this, $getterMethod)) {
$getterMethod = false;
}
Doctrine_Common_VirtualPropertySystem::setAccessor(
$this->_entityName, $fieldName, $getterMethod);
} else {
Doctrine_Common_VirtualPropertySystem::setAccessor(
$this->_entityName, $fieldName, false);
}
}
return Doctrine_Common_VirtualPropertySystem::getAccessor($this->_entityName, $fieldName);
}
protected function _contains($fieldName)
{
return isset($this->_data[$fieldName]);
}
protected function _unset($fieldName)
{
unset($this->_data[$fieldName]);
}
/**
* Intercepts mutating calls for virtual properties.
*
* @see set, offsetSet
* @param $name
* @param $value
* @since 1.0
* @return void
*/
public function __set($name, $value)
{
$this->set($name, $value);
}
/**
* Intercepts accessing calls for virtual properties.
*
* @see get, offsetGet
* @param mixed $name
* @return mixed
*/
public function __get($name)
{
return $this->get($name);
}
/**
* Intercepts isset() calls for virtual properties.
*
* @param string $name
* @return boolean whether or not this object contains $name
*/
public function __isset($name)
{
return $this->_contains($name);
}
/**
* Intercepts unset() calls for virtual properties.
*
* @param string $name
* @return void
*/
public function __unset($name)
{
return $this->_unset($name);
}
/* ArrayAccess implementation */
/**
* Check if an offsetExists.
*
* @param mixed $offset
* @return boolean whether or not this object contains $offset
*/
public function offsetExists($offset)
{
return $this->_contains($offset);
}
/**
* offsetGet an alias of get()
*
* @see get, __get
* @param mixed $offset
* @return mixed
*/
public function offsetGet($offset)
{
return $this->get($offset);
}
/**
* sets $offset to $value
* @see set, __set
* @param mixed $offset
* @param mixed $value
* @return void
*/
public function offsetSet($offset, $value)
{
return $this->set($offset, $value);
}
/**
* unset a given offset
* @see set, offsetSet, __set
* @param mixed $offset
*/
public function offsetUnset($offset)
{
return $this->_unset($offset);
}
/* END of ArrayAccess implementation */
}
?>

View File

@ -0,0 +1,241 @@
<?php
/**
* The VirtualPropertySystem class is a class consisting solely of static methods and
* serves as a generic virtual property registry system.
* Classes register their (virtual) properties with the property system, optionally specifying
* property features/options. These can then be evaluated by other code.
*
* @author robo
* @since 2.0
*/
class Doctrine_Common_VirtualPropertySystem {
private static $_properties = array();
private static $_callback = 'construct';
private static $_checkTypes = false;
private static $_useAutoAccessorOverride = true;
private static $_simplePHPTypes = array(
'int' => true,
'string' => true,
'bool' => true,
'double' => true
);
/** Private constructor. This class cannot be instantiated. */
private function __construct() {}
/**
* Gets all properties of a class that are registered with the VirtualPropertySystem.
*
* @param string $class
* @return array
*/
public static function getProperties($class)
{
if ( ! self::isInitialized($class)) {
self::initialize($class);
}
return self::$_properties[$class];
}
/**
* Gets whether automatic accessor overrides are enabled.
*
* @return boolean
*/
public static function isAutoAccessorOverride()
{
return self::$_useAutoAccessorOverride;
}
/**
* Sets whether automatic accessor overrides are enabled.
*
* @param boolean $bool
*/
public static function setAutoAccessorOverride($bool)
{
self::$_useAutoAccessorOverride = (bool)$bool;
}
/**
* Prepopulates the property system.
*
* @param array $properties
*/
public static function populate(array $properties)
{
self::$_properties = $properties;
}
/**
* Checks whether the given type is a simple PHP type.
* Simple php types are: int, string, bool, double.
*
* @param string $type The type to check.
* @return boolean
*/
public static function isSimplePHPType($type)
{
return isset(self::$_simplePHPTypes[$type]);
}
/**
* Gets whether type checks are enabled.
*
* @return boolean
*/
public static function isTypeCheckEnabled()
{
return self::$_checkTypes;
}
/**
* Sets whether type checks are enabled.
*
* @param boolean $bool
*/
public static function setTypeCheckEnabled($bool)
{
self::$_checkTypes = (bool)$bool;
}
/**
* Sets the name of the callback method to use for initializing the virtual
* properties of a class. The callback must be static and public.
*
* @param string $callback
*/
public static function setCallback($callback)
{
self::$_callback = $callback;
}
/**
* Registers a virtual property for a class.
*
* @param string $class
* @param string $propName
* @param string $type
* @param string $accessor
* @param string $mutator
*/
public static function register($class, $propName, $type, $accessor = null, $mutator = null)
{
self::$_properties[$class][$propName] = array(
'type' => $type, 'accessor' => $accessor, 'mutator' => $mutator
);
}
/**
* Gets whether a class has already been initialized by the virtual property system.
*
* @param string $class
* @return boolean
*/
public static function isInitialized($class)
{
return isset(self::$_properties[$class]);
}
/**
* Initializes a class with the virtual property system.
*
* @param <type> $class
*/
public static function initialize($class)
{
if (method_exists($class, self::$_callback)) {
call_user_func(array($class, self::$_callback));
} else {
self::$_properties[$class] = false;
}
}
/**
* Gets whether a class has a virtual property with a certain name.
*
* @param string $class
* @param string $propName
* @return boolean
*/
public static function hasProperty($class, $propName)
{
return isset(self::$_properties[$class][$propName]);
}
/**
* Gets the accessor for a virtual property.
*
* @param string $class
* @param string $propName
* @return string|null
*/
public static function getAccessor($class, $propName)
{
return isset(self::$_properties[$class][$propName]['accessor']) ?
self::$_properties[$class][$propName]['accessor'] : null;
}
/**
* Sets the accessor method for a virtual property.
*
* @param <type> $class
* @param <type> $propName
* @param <type> $accessor
*/
public static function setAccessor($class, $propName, $accessor)
{
self::$_properties[$class][$propName]['accessor'] = $accessor;
}
/**
* Gets the mutator method for a virtual property.
*
* @param <type> $class
* @param <type> $propName
* @return <type>
*/
public static function getMutator($class, $propName)
{
return isset(self::$_properties[$class][$propName]['mutator']) ?
self::$_properties[$class][$propName]['mutator'] : null;
}
/**
* Sets the mutator method for a virtual property.
*
* @param <type> $class
* @param <type> $propName
* @param <type> $mutator
*/
public static function setMutator($class, $propName, $mutator)
{
self::$_properties[$class][$propName]['mutator'] = $mutator;
}
/**
* Gets the type of a virtual property.
*
* @param <type> $class
* @param <type> $propName
* @return <type>
*/
public static function getType($class, $propName)
{
return isset(self::$_properties[$class][$propName]['type']) ?
self::$_properties[$class][$propName]['type'] : null;
}
/**
* Sets the type of a virtual property.
*
* @param <type> $class
* @param <type> $propName
* @param <type> $type
*/
public static function setType($class, $propName, $type)
{
self::$_properties[$class][$propName]['type'] = $type;
}
}
?>

View File

@ -19,14 +19,14 @@
* <http://www.phpdoctrine.org>. * <http://www.phpdoctrine.org>.
*/ */
#namespace Doctrine::DBAL; #namespace Doctrine\DBAL;
#use Doctrine::Common::Configuration; #use Doctrine\Common\Configuration;
#use Doctrine::Common::EventManager; #use Doctrine\Common\EventManager;
#use Doctrine::DBAL::Exceptions::ConnectionException; #use Doctrine\DBAL\Exceptions\ConnectionException;
/** /**
* A wrapper around a Doctrine::DBAL::Driver::Connection that adds features like * A wrapper around a Doctrine\DBAL\Driver\Connection that adds features like
* events, transaction isolation levels, configuration, emulated transaction nesting, * events, transaction isolation levels, configuration, emulated transaction nesting,
* lazy connecting and more. * lazy connecting and more.
* *
@ -51,7 +51,7 @@
* 'masters' => array(...), * 'masters' => array(...),
* 'masterConnectionResolver' => new MyMasterConnectionResolver() * 'masterConnectionResolver' => new MyMasterConnectionResolver()
* *
* Doctrine::DBAL could ship with a simple standard broker that uses a primitive * Doctrine\DBAL could ship with a simple standard broker that uses a primitive
* round-robin approach to distribution. User can provide its own brokers. * round-robin approach to distribution. User can provide its own brokers.
*/ */
class Doctrine_DBAL_Connection class Doctrine_DBAL_Connection
@ -76,21 +76,21 @@ class Doctrine_DBAL_Connection
/** /**
* The wrapped driver connection. * The wrapped driver connection.
* *
* @var Doctrine::DBAL::Driver::Connection * @var Doctrine\DBAL\Driver\Connection
*/ */
protected $_conn; protected $_conn;
/** /**
* The Configuration. * The Configuration.
* *
* @var Doctrine::Common::Configuration * @var Doctrine\Common\Configuration
*/ */
protected $_config; protected $_config;
/** /**
* The EventManager. * The EventManager.
* *
* @var Doctrine::Commom::EventManager * @var Doctrine\Common\EventManager
*/ */
protected $_eventManager; protected $_eventManager;
@ -116,7 +116,7 @@ class Doctrine_DBAL_Connection
protected $_transactionIsolationLevel; protected $_transactionIsolationLevel;
/** /**
* The parameters used during creation of the Connection. * The parameters used during creation of the Connection instance.
* *
* @var array * @var array
*/ */
@ -133,32 +133,32 @@ class Doctrine_DBAL_Connection
* The DatabasePlatform object that provides information about the * The DatabasePlatform object that provides information about the
* database platform used by the connection. * database platform used by the connection.
* *
* @var Doctrine::DBAL::Platforms::DatabasePlatform * @var Doctrine\DBAL\Platforms\AbstractPlatform
*/ */
protected $_platform; protected $_platform;
/** /**
* The schema manager. * The schema manager.
* *
* @var Doctrine::DBAL::Schema::SchemaManager * @var Doctrine\DBAL\Schema\SchemaManager
*/ */
protected $_schemaManager; protected $_schemaManager;
/** /**
* The used DBAL driver. * The used DBAL driver.
* *
* @var Doctrine::DBAL::Driver * @var Doctrine\DBAL\Driver
*/ */
protected $_driver; protected $_driver;
/** /**
* Constructor. * Initializes a new instance of the Connection class.
* Creates a new Connection.
* *
* @param array $params The connection parameters. * @param array $params The connection parameters.
*/ */
public function __construct(array $params, Doctrine_DBAL_Driver $driver, public function __construct(array $params, Doctrine_DBAL_Driver $driver,
Doctrine_Common_Configuration $config = null, Doctrine_Common_EventManager $eventManager = null) Doctrine_Common_Configuration $config = null,
Doctrine_Common_EventManager $eventManager = null)
{ {
$this->_driver = $driver; $this->_driver = $driver;
$this->_params = $params; $this->_params = $params;
@ -193,7 +193,7 @@ class Doctrine_DBAL_Connection
/** /**
* Gets the EventManager used by the Connection. * Gets the EventManager used by the Connection.
* *
* @return Doctrine::Common::EventManager * @return Doctrine\Common\EventManager
*/ */
public function getEventManager() public function getEventManager()
{ {
@ -203,7 +203,7 @@ class Doctrine_DBAL_Connection
/** /**
* Gets the DatabasePlatform for the connection. * Gets the DatabasePlatform for the connection.
* *
* @return Doctrine::DBAL::Platforms::DatabasePlatform * @return Doctrine\DBAL\Platforms\AbstractPlatform
*/ */
public function getDatabasePlatform() public function getDatabasePlatform()
{ {
@ -842,7 +842,7 @@ class Doctrine_DBAL_Connection
/** /**
* Gets the wrapped driver connection. * Gets the wrapped driver connection.
* *
* @return Doctrine::DBAL::Driver::Connection * @return Doctrine\DBAL\Driver\Connection
*/ */
public function getWrappedConnection() public function getWrappedConnection()
{ {
@ -853,7 +853,7 @@ class Doctrine_DBAL_Connection
* Gets the SchemaManager that can be used to inspect or change the * Gets the SchemaManager that can be used to inspect or change the
* database schema through the connection. * database schema through the connection.
* *
* @return Doctrine::DBAL::Schema::SchemaManager * @return Doctrine\DBAL\Schema\SchemaManager
*/ */
public function getSchemaManager() public function getSchemaManager()
{ {

View File

@ -90,7 +90,8 @@ final class Doctrine_DBAL_DriverManager
* @param Doctrine::Common::EventManager The event manager to use. * @param Doctrine::Common::EventManager The event manager to use.
* @return Doctrine::DBAL::Connection * @return Doctrine::DBAL::Connection
*/ */
public static function getConnection(array $params, Doctrine_Common_Configuration $config = null, public static function getConnection(array $params,
Doctrine_Common_Configuration $config = null,
Doctrine_Common_EventManager $eventManager = null) Doctrine_Common_EventManager $eventManager = null)
{ {
// create default config and event manager, if not set // create default config and event manager, if not set

View File

@ -1,6 +1,8 @@
<?php <?php
#namespace Doctrine::DBAL::Types; #namespace Doctrine\DBAL\Types;
#use Doctrine\DBAL\Platforms\AbstractDatabasePlatform;
abstract class Doctrine_DBAL_Types_Type abstract class Doctrine_DBAL_Types_Type
{ {
@ -14,7 +16,7 @@ abstract class Doctrine_DBAL_Types_Type
'double' => 'Doctrine_DataType_DoubleType' 'double' => 'Doctrine_DataType_DoubleType'
); );
public function convertToDatabaseValue($value, Doctrine_DatabasePlatform $platform) public function convertToDatabaseValue($value, Doctrine_DBAL_Platforms_AbstractDatabasePlatform $platform)
{ {
return $value; return $value;
} }
@ -24,8 +26,8 @@ abstract class Doctrine_DBAL_Types_Type
return $value; return $value;
} }
abstract public function getDefaultLength(Doctrine_DatabasePlatform $platform); abstract public function getDefaultLength(Doctrine_DBAL_Platforms_AbstractDatabasePlatform $platform);
abstract public function getSqlDeclaration(array $fieldDeclaration, Doctrine_DatabasePlatform $platform); abstract public function getSqlDeclaration(array $fieldDeclaration, Doctrine_DBAL_Platforms_AbstractDatabasePlatform $platform);
abstract public function getName(); abstract public function getName();
/** /**

View File

@ -10,8 +10,45 @@
* *
* @since 2.0 * @since 2.0
*/ */
class Doctrine_ORM_ActiveEntity extends Doctrine_ORM_Entity class Doctrine_ORM_ActiveEntity extends Doctrine_Common_VirtualPropertyObject implements Doctrine_ORM_Entity
{ {
/**
* The class descriptor.
*
* @var Doctrine::ORM::ClassMetadata
*/
private $_class;
/**
* The changes that happened to fields of a managed entity.
* Keys are field names, values oldValue => newValue tuples.
*
* @var array
*/
private $_dataChangeSet = array();
/**
* The EntityManager that is responsible for the persistent state of the entity.
* Only managed entities have an associated EntityManager.
*
* @var Doctrine\ORM\EntityManager
*/
private $_em;
/**
* Initializes a new instance of a class derived from ActiveEntity.
*/
public function __construct() {
parent::__construct();
$this->_oid = self::$_index++;
$this->_em = Doctrine_ORM_EntityManager::getActiveEntityManager();
if (is_null($this->_em)) {
throw new Doctrine_Exception("No EntityManager found. ActiveEntity instances "
. "can only be instantiated within the context of an active EntityManager.");
}
$this->_class = $this->_em->getClassMetadata($this->_entityName);
}
/** /**
* Saves the current state of the entity into the database. * Saves the current state of the entity into the database.
* *
@ -419,6 +456,270 @@ class Doctrine_ORM_ActiveEntity extends Doctrine_ORM_Entity
$this->_data = array_merge($this->_data, $data); $this->_data = array_merge($this->_data, $data);
$this->_extractIdentifier(); $this->_extractIdentifier();
} }
/**
* Helps freeing the memory occupied by the entity.
* Cuts all references the entity has to other entities and removes the entity
* from the instance pool.
* Note: The entity is no longer useable after free() has been called. Any operations
* done with the entity afterwards can lead to unpredictable results.
*
* @param boolean $deep Whether to cascade the free() call to (loaded) associated entities.
*/
public function free($deep = false)
{
if ($this->_state != self::STATE_LOCKED) {
if ($this->_state == self::STATE_MANAGED) {
$this->_em->detach($this);
}
if ($deep) {
foreach ($this->_data as $name => $value) {
if ($value instanceof Doctrine_ORM_Entity || $value instanceof Doctrine_ORM_Collection) {
$value->free($deep);
}
}
}
$this->_data = array();
}
}
/**
* Returns a string representation of this object.
*/
public function __toString()
{
return (string)$this->_oid;
}
/**
* Checks whether the entity is new.
*
* @return boolean TRUE if the entity is new, FALSE otherwise.
*/
final public function isNew()
{
return $this->_state == self::STATE_NEW;
}
/**
* Checks whether the entity has been modified since it was last synchronized
* with the database.
*
* @return boolean TRUE if the object has been modified, FALSE otherwise.
*/
final public function isModified()
{
return count($this->_dataChangeSet) > 0;
}
/**
* Gets the ClassMetadata object that describes the entity class.
*
* @return Doctrine::ORM::Mapping::ClassMetadata
*/
final public function getClass()
{
return $this->_class;
}
/**
* Gets the EntityManager that is responsible for the persistence of
* this entity.
*
* @return Doctrine::ORM::EntityManager
*/
final public function getEntityManager()
{
return $this->_em;
}
/**
* Gets the EntityRepository of the Entity.
*
* @return Doctrine::ORM::EntityRepository
*/
final public function getRepository()
{
return $this->_em->getRepository($this->_entityName);
}
/**
* Checks whether a field is set (not null).
*
* @param string $name
* @return boolean
* @override
*/
final protected function _contains($fieldName)
{
if (isset($this->_data[$fieldName])) {
if ($this->_data[$fieldName] === Doctrine_ORM_Internal_Null::$INSTANCE) {
return false;
}
return true;
}
return false;
}
/**
* Clears the value of a field.
*
* @param string $name
* @return void
* @override
*/
final protected function _unset($fieldName)
{
if (isset($this->_data[$fieldName])) {
if ($this->_state == self::STATE_MANAGED && $this->_class->hasAssociation($fieldName)) {
$assoc = $this->_class->getAssociationMapping($fieldName);
if ($assoc->isOneToOne() && $assoc->shouldDeleteOrphans()) {
$this->_em->delete($this->_references[$fieldName]);
} else if ($assoc->isOneToMany() && $assoc->shouldDeleteOrphans()) {
foreach ($this->_references[$fieldName] as $entity) {
$this->_em->delete($entity);
}
}
}
$this->_data[$fieldName] = null;
}
}
/**
* Registers the entity as dirty with the UnitOfWork.
* Note: The Entity is only registered dirty if it is MANAGED and not yet
* registered as dirty.
*/
private function _registerDirty()
{
if ($this->_state == self::STATE_MANAGED &&
! $this->_em->getUnitOfWork()->isRegisteredDirty($this)) {
$this->_em->getUnitOfWork()->registerDirty($this);
}
}
/**
* Gets the entity class name.
*
* @return string
*/
final public function getClassName()
{
return $this->_entityName;
}
/**
* Gets the data of the Entity.
*
* @return array The fields and their values.
*/
final public function getData()
{
return $this->_data;
}
/**
* Gets the value of a field (regular field or reference).
*
* @param $name Name of the field.
* @return mixed Value of the field.
* @throws Doctrine::ORM::Exceptions::EntityException If trying to get an unknown field.
* @override
*/
final protected function _get($fieldName)
{
$nullObj = Doctrine_ORM_Internal_Null::$INSTANCE;
if (isset($this->_data[$fieldName])) {
return $this->_data[$fieldName] !== $nullObj ?
$this->_data[$fieldName] : null;
} else {
if ($this->_state == self::STATE_MANAGED && $this->_class->hasAssociation($fieldName)) {
$rel = $this->_class->getAssociationMapping($fieldName);
if ($rel->isLazilyFetched()) {
$this->_data[$fieldName] = $rel->lazyLoadFor($this);
return $this->_data[$fieldName] !== $nullObj ?
$this->_data[$fieldName] : null;
} else {
return null;
}
} else {
return null;
}
}
}
/**
* Sets the value of a field (regular field or reference).
*
* @param $fieldName The name of the field.
* @param $value The value of the field.
* @return void
* @throws Doctrine::ORM::Exceptions::EntityException
* @override
*/
final protected function _set($fieldName, $value)
{
$old = isset($this->_data[$fieldName]) ? $this->_data[$fieldName] : null;
if ( ! is_object($value)) {
// NOTE: Common case: $old != $value. Special case: null == 0 (TRUE), which
// is addressed by xor.
if ($old != $value || (is_null($old) xor is_null($value))) {
$this->_data[$fieldName] = $value;
$this->_dataChangeSet[$fieldName] = array($old => $value);
$this->_registerDirty();
}
} else {
if ($old !== $value) {
$this->_internalSetReference($fieldName, $value);
$this->_dataChangeSet[$fieldName] = array($old => $value);
$this->_registerDirty();
if ($this->_state == self::STATE_MANAGED) {
//TODO: Allow arrays in $value. Wrap them in a Collection transparently.
if ($old instanceof Doctrine_ORM_Collection) {
$this->_em->getUnitOfWork()->scheduleCollectionDeletion($old);
}
if ($value instanceof Doctrine_ORM_Collection) {
$this->_em->getUnitOfWork()->scheduleCollectionRecreation($value);
}
}
}
}
}
/* Serializable implementation */
/**
* Serializes the entity.
* This method is automatically called when the entity is serialized.
*
* Part of the implementation of the Serializable interface.
*
* @return string
* @todo Reimplement
*/
public function serialize()
{
return "";
}
/**
* Reconstructs the entity from it's serialized form.
* This method is automatically called everytime the entity is unserialized.
*
* @param string $serialized Doctrine_Entity as serialized string
* @throws Doctrine_Record_Exception if the cleanData operation fails somehow
* @return void
* @todo Reimplement.
*/
public function unserialize($serialized)
{
;
}
/* END of Serializable implementation */
} }
?> ?>

View File

@ -45,6 +45,7 @@
* @author Konsta Vesterinen <kvesteri@cc.hut.fi> * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @todo Add more typical Collection methods. * @todo Add more typical Collection methods.
* @todo Rename to PersistentCollection
*/ */
class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializable, ArrayAccess class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializable, ArrayAccess
{ {
@ -74,7 +75,7 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
/** /**
* This entity that owns this collection. * This entity that owns this collection.
* *
* @var Doctrine::ORM::Entity * @var Doctrine\ORM\Entity
*/ */
protected $_owner; protected $_owner;
@ -82,7 +83,7 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
* The association mapping the collection belongs to. * The association mapping the collection belongs to.
* This is currently either a OneToManyMapping or a ManyToManyMapping. * This is currently either a OneToManyMapping or a ManyToManyMapping.
* *
* @var Doctrine::ORM::Mapping::AssociationMapping * @var Doctrine\ORM\Mapping\AssociationMapping
*/ */
protected $_association; protected $_association;
@ -93,17 +94,10 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
*/ */
protected $_keyField; protected $_keyField;
/**
* Helper variable. Used for fast null value testing.
*
* @var Doctrine_Null
*/
//protected static $null;
/** /**
* The EntityManager that manages the persistence of the collection. * The EntityManager that manages the persistence of the collection.
* *
* @var Doctrine::ORM::EntityManager * @var Doctrine\ORM\EntityManager
*/ */
protected $_em; protected $_em;
@ -124,7 +118,6 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
protected $_hydrationFlag; protected $_hydrationFlag;
/** /**
* Constructor.
* Creates a new persistent collection. * Creates a new persistent collection.
*/ */
public function __construct($entityBaseType, $keyField = null) public function __construct($entityBaseType, $keyField = null)
@ -174,9 +167,9 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
} }
/** /**
* returns all the records as an array * Unwraps the array contained in the Collection instance.
* *
* @return array * @return array The wrapped array.
*/ */
public function unwrap() public function unwrap()
{ {
@ -229,7 +222,7 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
* *
* @return void * @return void
*/ */
public function _setOwner(Doctrine_ORM_Entity $entity, Doctrine_ORM_Mapping_AssociationMapping $relation) public function _setOwner($entity, Doctrine_ORM_Mapping_AssociationMapping $relation)
{ {
$this->_owner = $entity; $this->_owner = $entity;
$this->_association = $relation; $this->_association = $relation;
@ -248,9 +241,9 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
/** /**
* INTERNAL: * INTERNAL:
* getReference * Gets the collection owner.
* *
* @return mixed * @return Doctrine\ORM\Entity
*/ */
public function _getOwner() public function _getOwner()
{ {
@ -474,8 +467,7 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
*/ */
public function add($value, $key = null) public function add($value, $key = null)
{ {
//TODO: really only allow entities? if ( ! $value instanceof $this->_entityBaseType) {
if ( ! $value instanceof Doctrine_ORM_Entity) {
throw new Doctrine_Record_Exception('Value variable in collection is not an instance of Doctrine_Entity.'); throw new Doctrine_Record_Exception('Value variable in collection is not an instance of Doctrine_Entity.');
} }
@ -496,7 +488,8 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
if ($this->_hydrationFlag) { if ($this->_hydrationFlag) {
if ($this->_backRefFieldName) { if ($this->_backRefFieldName) {
// set back reference to owner // set back reference to owner
$value->_internalSetReference($this->_backRefFieldName, $this->_owner); $this->_em->getClassMetadata($this->_entityBaseType)->getReflectionProperty(
$this->_backRefFieldName)->setValue($value, $this->_owner);
} }
} else { } else {
//TODO: Register collection as dirty with the UoW if necessary //TODO: Register collection as dirty with the UoW if necessary
@ -721,7 +714,8 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
*/ */
public function isEmpty() public function isEmpty()
{ {
return $this->count() == 0; // Note: Little "trick". Empty arrays evaluate to FALSE. No need to count().
return ! (bool)$this->_data;
} }
/** /**
@ -765,40 +759,6 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
} }
} }
/**
* Export a Doctrine_Collection to one of the supported Doctrine_Parser formats
*
* @param string $type
* @param string $deep
* @return void
* @todo Move elsewhere.
*/
/*public function exportTo($type, $deep = false)
{
if ($type == 'array') {
return $this->toArray($deep);
} else {
return Doctrine_Parser::dump($this->toArray($deep, true), $type);
}
}*/
/**
* Import data to a Doctrine_Collection from one of the supported Doctrine_Parser formats
*
* @param string $type
* @param string $data
* @return void
* @todo Move elsewhere.
*/
/*public function importFrom($type, $data)
{
if ($type == 'array') {
return $this->fromArray($data);
} else {
return $this->fromArray(Doctrine_Parser::load($data, $type));
}
}*/
/** /**
* INTERNAL: getDeleteDiff * INTERNAL: getDeleteDiff
* *
@ -833,62 +793,9 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
} }
/** /**
* Saves all records of this collection and processes the
* difference of the last snapshot and the current data.
* *
* @param Doctrine_Connection $conn optional connection parameter * @param <type> $deep
* @return Doctrine_Collection
*/ */
/*public function save()
{
$conn = $this->_mapper->getConnection();
try {
$conn->beginInternalTransaction();
$conn->transaction->addCollection($this);
$this->processDiff();
foreach ($this->getData() as $key => $record) {
$record->save($conn);
}
$conn->commit();
} catch (Exception $e) {
$conn->rollback();
throw $e;
}
return $this;
}*/
/**
* Deletes all records from the collection.
* Shorthand for calling delete() for all entities in the collection.
*
* @return void
*/
/*public function delete()
{
$conn = $this->_mapper->getConnection();
try {
$conn->beginInternalTransaction();
$conn->transaction->addCollection($this);
foreach ($this as $key => $record) {
$record->delete($conn);
}
$conn->commit();
} catch (Exception $e) {
$conn->rollback();
throw $e;
}
$this->clear();
}*/
public function free($deep = false) public function free($deep = false)
{ {
foreach ($this->getData() as $key => $record) { foreach ($this->getData() as $key => $record) {

View File

@ -1,6 +1,6 @@
<?php <?php
/* /*
* $Id: Record.php 4342 2008-05-08 14:17:35Z romanb $ * $Id$
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@ -19,974 +19,18 @@
* <http://www.phpdoctrine.org>. * <http://www.phpdoctrine.org>.
*/ */
#namespace Doctrine::ORM; #namespace Doctrine\ORM;
#use \Serializable;
/** /**
* Base class for all Entities (objects with persistent state in a RDBMS that are * Entity marker interface.
* managed by Doctrine). Kind of a Layer Supertype.
* *
* NOTE: Methods that are intended for internal use only but must be public
* are marked INTERNAL: and begin with an underscore "_" to indicate that they
* ideally would not be public and to minimize naming collisions.
*
* The "final" modifiers on most methods prevent accidental overrides.
* It is not desirable that subclasses can override these methods.
* The persistence layer should stay in the background as much as possible.
*
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.org * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
* @version $Revision: 4342 $ * @version $Revision: 4342 $
*/ */
abstract class Doctrine_ORM_Entity implements ArrayAccess, Serializable interface Doctrine_ORM_Entity
{ {}
/**
* MANAGED
* An Entity is in managed state when it has a primary key/identifier and is
* managed by an EntityManager (registered in the identity map).
*/
const STATE_MANAGED = 1;
/**
* NEW
* An Entity is new if it does not yet have an identifier/primary key
* and is not (yet) managed by an EntityManager.
*/
const STATE_NEW = 2;
/**
* LOCKED STATE
* An Entity is temporarily locked during deletes and saves.
*
* This state is used internally to ensure that circular deletes
* and saves will not cause infinite loops.
* @todo Not sure this is a good idea. It is a problematic solution because
* it hides the original state while the locked state is active.
*/
const STATE_LOCKED = 6;
/**
* A detached Entity is an instance with a persistent identity that is not
* (or no longer) associated with an EntityManager (and a UnitOfWork).
* This means its no longer in the identity map.
*/
const STATE_DETACHED = 3;
/**
* A removed Entity instance is an instance with a persistent identity,
* associated with an EntityManager, whose persistent state has been
* deleted (or is scheduled for deletion).
*/
const STATE_DELETED = 4;
/**
* Index used for creating object identifiers (oid's).
*
* @var integer
*/
private static $_index = 1;
/**
* Boolean flag that indicates whether automatic accessor overriding is enabled.
*
* @var boolean
*/
private static $_useAutoAccessorOverride;
/**
* The accessor cache is used as a memory for the existance of custom accessors.
*
* @var array
*/
private static $_accessorCache = array();
/**
* The mutator cache is used as a memory for the existance of custom mutators.
*
* @var array
*/
private static $_mutatorCache = array();
/**
* The class descriptor.
*
* @var Doctrine::ORM::ClassMetadata
*/
private $_class;
/**
* The name of the Entity.
*
* @var string
*/
private $_entityName;
/**
* The values that make up the ID/primary key of the entity.
*
* @var array
*/
private $_id = array();
/**
* The entity data.
*
* @var array
*/
private $_data = array();
/**
* The state of the object.
*
* @var integer
*/
private $_state;
/**
* Name => Value map of join columns.
*
* @var array
* @todo Not yet clear if needed.
*/
//private $_joinColumns = array();
/**
* The changes that happened to fields of the entity.
* Keys are field names, values oldValue => newValue tuples.
*
* @var array
*/
private $_dataChangeSet = array();
/**
* The changes that happened to references of the entity to other entities.
* Keys are field names, values oldReference => newReference tuples.
*
* With one-one associations, a reference change means the reference has been
* swapped out / replaced by another one.
*
* With one-many, many-many associations, a reference change means the complete
* collection has been sweapped out / replaced by another one.
*
* @var array
*/
private $_referenceChangeSet = array();
/**
* The references for all associations of the entity to other entities.
* Keys are field names, values object references.
*
* @var array
*/
private $_references = array();
/**
* The EntityManager that is responsible for the persistent state of the entity.
*
* @var Doctrine::ORM::EntityManager
*/
private $_em;
/**
* The object identifier of the object. Each object has a unique identifier
* during script execution.
*
* @var integer
*/
private $_oid;
/**
* Constructor.
* Creates a new Entity instance.
*/
public function __construct()
{
$this->_entityName = get_class($this);
$this->_em = Doctrine_ORM_EntityManager::getActiveEntityManager();
$this->_class = $this->_em->getClassMetadata($this->_entityName);
$this->_oid = self::$_index++;
$this->_data = $this->_em->_getTmpEntityData();
if ($this->_data) {
$this->_extractIdentifier();
$this->_state = self::STATE_MANAGED;
} else {
$this->_state = self::STATE_NEW;
}
// @todo read from attribute the first time and move this initialization elsewhere.
self::$_useAutoAccessorOverride = true;
}
/**
* Returns the object identifier.
*
* @return integer
*/
final public function getOid()
{
return $this->_oid;
}
/**
* Copies the identifier names and values from _data into _id.
*/
private function _extractIdentifier()
{
if ( ! $this->_class->isIdentifierComposite()) {
// Single field identifier
$name = $this->_class->getIdentifier();
$name = $name[0];
if (isset($this->_data[$name]) && $this->_data[$name] !== Doctrine_ORM_Internal_Null::$INSTANCE) {
$this->_id[$name] = $this->_data[$name];
}
} else {
// Composite identifier
$names = $this->_class->getIdentifier();
foreach ($names as $name) {
if ($this->_data[$name] === Doctrine_Null::$INSTANCE) {
$this->_id[$name] = null;
} else {
$this->_id[$name] = $this->_data[$name];
}
}
}
}
/**
* Serializes the entity.
* This method is automatically called when the entity is serialized.
*
* Part of the implementation of the Serializable interface.
*
* @return string
*/
public function serialize()
{
//$this->_em->getEventManager()->dispatchEvent(Event::preSerialize);
//$this->_class->dispatchLifecycleEvent(Event::preSerialize, $this);
$vars = get_object_vars($this);
unset($vars['_references']);
unset($vars['_em']);
//$name = (array)$this->_table->getIdentifier();
$this->_data = array_merge($this->_data, $this->_id);
foreach ($this->_data as $k => $v) {
if ($v instanceof Doctrine_ORM_Entity && $this->_class->getTypeOfField($k) != 'object') {
unset($vars['_data'][$k]);
} else if ($v === Doctrine_Null::$INSTANCE) {
unset($vars['_data'][$k]);
} else {
switch ($this->_class->getTypeOfField($k)) {
case 'array':
case 'object':
$vars['_data'][$k] = serialize($vars['_data'][$k]);
break;
case 'gzip':
$vars['_data'][$k] = gzcompress($vars['_data'][$k]);
break;
case 'enum':
$vars['_data'][$k] = $this->_class->enumIndex($k, $vars['_data'][$k]);
break;
}
}
}
$str = serialize($vars);
//$this->postSerialize($event);
return $str;
}
/**
* Reconstructs the entity from it's serialized form.
* This method is automatically called everytime the entity is unserialized.
*
* @param string $serialized Doctrine_Entity as serialized string
* @throws Doctrine_Record_Exception if the cleanData operation fails somehow
* @return void
*/
public function unserialize($serialized)
{
//$event = new Doctrine_Event($this, Doctrine_Event::RECORD_UNSERIALIZE);
//$this->preUnserialize($event);
$this->_entityName = get_class($this);
$manager = Doctrine_EntityManagerFactory::getManager($this->_entityName);
$connection = $manager->getConnection();
$this->_oid = self::$_index;
self::$_index++;
$this->_em = $manager;
$array = unserialize($serialized);
foreach($array as $k => $v) {
$this->$k = $v;
}
$this->_class = $this->_em->getClassMetadata($this->_entityName);
foreach ($this->_data as $k => $v) {
switch ($this->_class->getTypeOfField($k)) {
case 'array':
case 'object':
$this->_data[$k] = unserialize($this->_data[$k]);
break;
case 'gzip':
$this->_data[$k] = gzuncompress($this->_data[$k]);
break;
case 'enum':
$this->_data[$k] = $this->_class->enumValue($k, $this->_data[$k]);
break;
}
}
$this->_extractIdentifier(!$this->isNew());
//$this->postUnserialize($event);
}
/**
* INTERNAL:
* Gets or sets the state of this Entity.
*
* @param integer|string $state if set, this method tries to set the record state to $state
* @see Doctrine_Entity::STATE_* constants
*
* @throws Doctrine_Record_State_Exception if trying to set an unknown state
* @return null|integer
*/
final public function _state($state = null)
{
if ($state == null) {
return $this->_state;
}
$this->_state = $state;
}
/**
* Gets the current field values.
*
* @return array The fields and their values.
*/
final public function getData()
{
return $this->_data;
}
/**
* Gets the value of a field (regular field or reference).
*
* @param $name Name of the field.
* @return mixed Value of the field.
* @throws Doctrine::ORM::Exceptions::EntityException If trying to get an unknown field.
*/
final protected function _get($fieldName)
{
$nullObj = Doctrine_ORM_Internal_Null::$INSTANCE;
if (isset($this->_data[$fieldName])) {
return $this->_data[$fieldName] !== $nullObj ?
$this->_data[$fieldName] : null;
} else if (isset($this->_references[$fieldName])) {
return $this->_references[$fieldName] !== $nullObj ?
$this->_references[$fieldName] : null;
} else {
if ($this->_class->hasField($fieldName)) {
return null;
} else if ($this->_class->hasAssociation($fieldName)) {
$rel = $this->_class->getAssociationMapping($fieldName);
if ($rel->isLazilyFetched()) {
$this->_references[$fieldName] = $rel->lazyLoadFor($this);
return $this->_references[$fieldName] !== $nullObj ?
$this->_references[$fieldName] : null;
} else {
return null;
}
} else {
throw Doctrine_Entity_Exception::invalidField($fieldName);
}
}
}
/**
* Sets the value of a field (regular field or reference).
*
* @param $fieldName The name of the field.
* @param $value The value of the field.
* @return void
* @throws Doctrine::ORM::Exceptions::EntityException
*/
final protected function _set($fieldName, $value)
{
if ($this->_class->hasField($fieldName)) {
$old = isset($this->_data[$fieldName]) ? $this->_data[$fieldName] : null;
// NOTE: Common case: $old != $value. Special case: null == 0 (TRUE), which
// is addressed by xor.
if ($old != $value || (is_null($old) xor is_null($value))) {
$this->_data[$fieldName] = $value;
$this->_dataChangeSet[$fieldName] = array($old => $value);
if ($this->isNew() && $this->_class->isIdentifier($fieldName)) {
$this->_id[$fieldName] = $value;
}
$this->_registerDirty();
}
} else if ($this->_class->hasAssociation($fieldName)) {
$old = isset($this->_references[$fieldName]) ? $this->_references[$fieldName] : null;
if ($old !== $value) {
$this->_internalSetReference($fieldName, $value);
$this->_referenceChangeSet[$fieldName] = array($old => $value);
$this->_registerDirty();
//TODO: Allow arrays in $value. Wrap them in a collection transparently.
if ($old instanceof Doctrine_Collection) {
$this->_em->getUnitOfWork()->scheduleCollectionDeletion($old);
}
if ($value instanceof Doctrine_Collection) {
$this->_em->getUnitOfWork()->scheduleCollectionRecreation($value);
}
}
} else {
throw Doctrine_ORM_Exceptions_EntityException::invalidField($fieldName);
}
}
/**
* Registers the entity as dirty with the UnitOfWork.
*/
private function _registerDirty()
{
if ($this->_state == self::STATE_MANAGED &&
! $this->_em->getUnitOfWork()->isRegisteredDirty($this)) {
$this->_em->getUnitOfWork()->registerDirty($this);
}
}
/**
* INTERNAL:
* Gets the value of a field.
*
* NOTE: Use of this method in userland code is strongly discouraged.
* This method does NOT check whether the field exists.
* _get() in extending classes should be preferred.
*
* @param string $fieldName
* @return mixed
*/
final public function _internalGetField($fieldName)
{
if ($this->_data[$fieldName] === Doctrine_ORM_Internal_Null::$INSTANCE) {
return null;
}
return $this->_data[$fieldName];
}
/**
* INTERNAL:
* Sets the value of a field.
*
* NOTE: Use of this method in userland code is strongly discouraged.
* This method does NOT check whether the field exists.
* _set() in extending classes should be preferred.
*
* @param string $fieldName
* @param mixed $value
*/
final public function _internalSetField($fieldName, $value)
{
$this->_data[$fieldName] = $value;
}
/**
* INTERNAL:
* Gets a reference to another Entity.
*
* NOTE: Use of this method in userland code is strongly discouraged.
* This method does NOT check whether the reference exists.
*
* @param string $fieldName
*/
final public function _internalGetReference($fieldName)
{
if ($this->_references[$fieldName] === Doctrine_ORM_Internal_Null::$INSTANCE) {
return null;
}
return $this->_references[$fieldName];
}
/**
* INTERNAL:
* Sets a reference to another entity or a collection of entities.
*
* NOTE: Use of this method in userland code is strongly discouraged.
*
* @param string $fieldName
* @param mixed $value
* @param boolean $completeBidirectional Whether to complete bidirectional associations
* (creating the back-reference). Should only
* be used by hydration.
*/
final public function _internalSetReference($name, $value, $completeBidirectional = false)
{
if (is_null($value) || $value === Doctrine_ORM_Internal_Null::$INSTANCE) {
$this->_references[$name] = $value;
return; // early exit!
}
$rel = $this->_class->getAssociationMapping($name);
if ($rel->isOneToOne() && ! $value instanceof Doctrine_ORM_Entity) {
throw Doctrine_Entity_Exception::invalidValueForOneToOneReference();
} else if (($rel->isOneToMany() || $rel->isManyToMany()) && ! $value instanceof Doctrine_ORM_Collection) {
throw Doctrine_Entity_Exception::invalidValueForOneToManyReference();
}
$this->_references[$name] = $value;
if ($completeBidirectional && $rel->isOneToOne()) {
if ($rel->isOwningSide()) {
// If there is an inverse mapping on the target class its bidirectional
$targetClass = $this->_em->getClassMetadata($rel->getTargetEntityName());
if ($targetClass->hasInverseAssociationMapping($name)) {
$value->_internalSetReference(
$targetClass->getInverseAssociationMapping($name)->getSourceFieldName(),
$this
);
}
} else {
// for sure bidirectional, as there is no inverse side in unidirectional
$value->_internalSetReference($rel->getMappedByFieldName(), $this);
}
}
}
/**
* Generic getter for all (persistent) fields of the entity.
*
* Invoked by Doctrine::ORM::Access#__get().
*
* @param string $fieldName Name of the field.
* @return mixed
* @override
*/
final public function get($fieldName)
{
if ($getter = $this->_getCustomAccessor($fieldName)) {
return $this->$getter();
}
return $this->_get($fieldName);
}
/**
* Gets the custom mutator method for a field, if it exists.
*
* @param string $fieldName The field name.
* @return mixed The name of the custom mutator or FALSE, if the field does
* not have a custom mutator.
*/
private function _getCustomMutator($fieldName)
{
if ( ! isset(self::$_mutatorCache[$this->_entityName][$fieldName])) {
if (self::$_useAutoAccessorOverride) {
$setterMethod = 'set' . Doctrine::classify($fieldName);
if (method_exists($this, $setterMethod)) {
self::$_mutatorCache[$this->_entityName][$fieldName] = $setterMethod;
} else {
self::$_mutatorCache[$this->_entityName][$fieldName] = false;
}
}
if ($setter = $this->_class->getCustomMutator($fieldName)) {
self::$_mutatorCache[$this->_entityName][$fieldName] = $setter;
} else if ( ! isset(self::$_mutatorCache[$this->_entityName][$fieldName])) {
self::$_mutatorCache[$this->_entityName][$fieldName] = false;
}
}
return self::$_mutatorCache[$this->_entityName][$fieldName];
}
/**
* Gets the custom accessor method of a field, if it exists.
*
* @param string $fieldName The field name.
* @return mixed The name of the custom accessor method, or FALSE if the
* field does not have a custom accessor.
*/
private function _getCustomAccessor($fieldName)
{
if ( ! isset(self::$_accessorCache[$this->_entityName][$fieldName])) {
if (self::$_useAutoAccessorOverride) {
$getterMethod = 'get' . Doctrine::classify($fieldName);
if (method_exists($this, $getterMethod)) {
self::$_accessorCache[$this->_entityName][$fieldName] = $getterMethod;
} else {
self::$_accessorCache[$this->_entityName][$fieldName] = false;
}
}
if ($getter = $this->_class->getCustomAccessor($fieldName)) {
self::$_accessorCache[$this->_entityName][$fieldName] = $getter;
} else if ( ! isset(self::$_accessorCache[$this->_entityName][$fieldName])) {
self::$_accessorCache[$this->_entityName][$fieldName] = false;
}
}
return self::$_accessorCache[$this->_entityName][$fieldName];
}
/**
* Gets the entity class name.
*
* @return string
*/
final public function getClassName()
{
return $this->_entityName;
}
/**
* Generic setter for (persistent) fields of the entity.
*
* Invoked by Doctrine::ORM::Access#__set().
*
* @param string $name The name of the field to set.
* @param mixed $value The value of the field.
* @override
*/
final public function set($fieldName, $value)
{
if ($setter = $this->_getCustomMutator($fieldName)) {
return $this->$setter($value);
}
$this->_set($fieldName, $value);
}
/**
* Checks whether a field is set (not null).
*
* NOTE: Invoked by Doctrine::ORM::Access#__isset().
*
* @param string $name
* @return boolean
*/
private function _contains($fieldName)
{
if (isset($this->_data[$fieldName])) {
if ($this->_data[$fieldName] === Doctrine_ORM_Internal_Null::$INSTANCE) {
return false;
}
return true;
}
if (isset($this->_id[$fieldName])) {
return true;
}
if (isset($this->_references[$fieldName]) &&
$this->_references[$fieldName] !== Doctrine_ORM_Internal_Null::$INSTANCE) {
return true;
}
return false;
}
/**
* Clears the value of a field.
*
* @param string $name
* @return void
*/
private function _unset($fieldName)
{
if (isset($this->_data[$fieldName])) {
$this->_data[$fieldName] = null;
} else if (isset($this->_references[$fieldName])) {
$assoc = $this->_class->getAssociationMapping($fieldName);
if ($assoc->isOneToOne() && $assoc->shouldDeleteOrphans()) {
$this->_em->delete($this->_references[$fieldName]);
} else if ($assoc->isOneToMany() && $assoc->shouldDeleteOrphans()) {
foreach ($this->_references[$fieldName] as $entity) {
$this->_em->delete($entity);
}
}
$this->_references[$fieldName] = null;
}
}
/**
* INTERNAL:
* Gets the changeset of the entities data.
*
* @return array
*/
final public function _getDataChangeSet()
{
return $this->_dataChangeSet;
}
/**
* INTERNAL:
* Gets the changeset of the entities references to other entities.
*
* @return array
*/
final public function _getReferenceChangeSet()
{
return $this->_referenceChangeSet;
}
/**
* Checks whether the entity already has a persistent state.
*
* @return boolean TRUE if the object is new, FALSE otherwise.
*/
final public function isNew()
{
return $this->_state == self::STATE_NEW;
}
/**
* Checks whether the entity has been modified since it was last synchronized
* with the database.
*
* @return boolean TRUE if the object has been modified, FALSE otherwise.
*/
final public function isModified()
{
return count($this->_fieldChangeSet) > 0;
}
/**
* INTERNAL:
* Assigns an identifier to the entity. This is only intended for use by
* the EntityPersisters or the UnitOfWork.
*
* @param mixed $id
*/
final public function _assignIdentifier($id)
{
if (is_array($id)) {
foreach ($id as $fieldName => $value) {
$this->_id[$fieldName] = $value;
$this->_data[$fieldName] = $value;
}
} else {
$name = $this->_class->getSingleIdentifierFieldName();
$this->_id[$name] = $id;
$this->_data[$name] = $id;
}
$this->_dataChangeSet = array();
$this->_referenceChangeSet = array();
}
/**
* @todo Not yet clear if needed.
*/
/*final public function _setJoinColumn($columnName, $value)
{
$this->_joinColumns[$columnName] = $value;
}*/
/**
* @todo Not yet clear if needed.
*/
/*final public function _getJoinColumn($columnName)
{
return $this->_joinColumns[$columnName];
}*/
/**
* INTERNAL:
* Returns the primary keys of the entity (field => value pairs).
*
* @return array
*/
final public function _identifier()
{
return $this->_id;
}
/**
* INTERNAL:
*
* getReferences
* @return array all references
*/
final public function _getReferences()
{
return $this->_references;
}
/**
* Gets the ClassMetadata object that describes the entity class.
*
* @return Doctrine::ORM::Mapping::ClassMetadata
*/
final public function getClass()
{
return $this->_class;
}
/**
* Gets the EntityManager that is responsible for the persistence of
* the entity.
*
* @return Doctrine::ORM::EntityManager
*/
final public function getEntityManager()
{
return $this->_em;
}
/**
* Gets the EntityRepository of the Entity.
*
* @return Doctrine::ORM::EntityRepository
*/
final public function getRepository()
{
return $this->_em->getRepository($this->_entityName);
}
/**
* Helps freeing the memory occupied by the entity.
* Cuts all references the entity has to other entities and removes the entity
* from the instance pool.
* Note: The entity is no longer useable after free() has been called. Any operations
* done with the entity afterwards can lead to unpredictable results.
*
* @param boolean $deep Whether to cascade the free() call to (loaded) associated entities.
*/
public function free($deep = false)
{
if ($this->_state != self::STATE_LOCKED) {
$this->_em->detach($this);
$this->_data = array();
$this->_id = array();
if ($deep) {
foreach ($this->_references as $name => $reference) {
if ( ! ($reference instanceof Doctrine_Null)) {
$reference->free($deep);
}
}
}
$this->_references = array();
}
}
/**
* Check if an offsetExists.
*
* Part of the ArrayAccess implementation.
*
* @param mixed $offset
* @return boolean whether or not this object contains $offset
*/
public function offsetExists($offset)
{
return $this->_contains($offset);
}
/**
* offsetGet an alias of get()
*
* Part of the ArrayAccess implementation.
*
* @see get, __get
* @param mixed $offset
* @return mixed
*/
public function offsetGet($offset)
{
return $this->get($offset);
}
/**
* Part of the ArrayAccess implementation.
*
* sets $offset to $value
* @see set, __set
* @param mixed $offset
* @param mixed $value
* @return void
*/
public function offsetSet($offset, $value)
{
return $this->set($offset, $value);
}
/**
* Part of the ArrayAccess implementation.
*
* unset a given offset
* @see set, offsetSet, __set
* @param mixed $offset
*/
public function offsetUnset($offset)
{
return $this->_unset($offset);
}
/**
* __set
*
* @see set, offsetSet
* @param $name
* @param $value
* @since 1.0
* @return void
*/
public function __set($name, $value)
{
$this->set($name, $value);
}
/**
* __get
*
* @see get, offsetGet
* @param mixed $name
* @return mixed
*/
public function __get($name)
{
return $this->get($name);
}
/**
* __isset()
*
* @param string $name
* @since 1.0
* @return boolean whether or not this object contains $name
*/
public function __isset($name)
{
return $this->_contains($name);
}
/**
* __unset()
*
* @param string $name
* @since 1.0
* @return void
*/
public function __unset($name)
{
return $this->_unset($name);
}
/**
* returns a string representation of this object
*/
public function __toString()
{
return (string)$this->_oid;
}
}

View File

@ -19,14 +19,14 @@
* <http://www.phpdoctrine.org>. * <http://www.phpdoctrine.org>.
*/ */
#namespace Doctrine::ORM; #namespace Doctrine\ORM;
#use Doctrine::Common::Configuration; #use Doctrine\Common\Configuration;
#use Doctrine::Common::EventManager; #use Doctrine\Common\EventManager;
#use Doctrine::DBAL::Connection; #use Doctrine\DBAL\Connection;
#use Doctrine::ORM::Exceptions::EntityManagerException; #use Doctrine\ORM\Exceptions\EntityManagerException;
#use Doctrine::ORM::Internal::UnitOfWork; #use Doctrine\ORM\Internal\UnitOfWork;
#use Doctrine::ORM::Mapping::ClassMetadata; #use Doctrine\ORM\Mapping\ClassMetadata;
/** /**
* The EntityManager is the central access point to ORM functionality. * The EntityManager is the central access point to ORM functionality.
@ -139,8 +139,12 @@ class Doctrine_ORM_EntityManager
*/ */
private $_tmpEntityData = array(); private $_tmpEntityData = array();
private $_idGenerators = array();
private $_closed = false; private $_closed = false;
private $_originalEntityData = array();
/** /**
* Creates a new EntityManager that operates on the given database connection. * Creates a new EntityManager that operates on the given database connection.
* *
@ -155,7 +159,8 @@ class Doctrine_ORM_EntityManager
$this->_config = $config; $this->_config = $config;
$this->_eventManager = $eventManager; $this->_eventManager = $eventManager;
$this->_metadataFactory = new Doctrine_ORM_Mapping_ClassMetadataFactory( $this->_metadataFactory = new Doctrine_ORM_Mapping_ClassMetadataFactory(
$this, new Doctrine_ORM_Mapping_Driver_CodeDriver()); new Doctrine_ORM_Mapping_Driver_CodeDriver(),
$this->_conn->getDatabasePlatform());
$this->_unitOfWork = new Doctrine_ORM_UnitOfWork($this); $this->_unitOfWork = new Doctrine_ORM_UnitOfWork($this);
$this->_nullObject = Doctrine_ORM_Internal_Null::$INSTANCE; $this->_nullObject = Doctrine_ORM_Internal_Null::$INSTANCE;
} }
@ -170,6 +175,14 @@ class Doctrine_ORM_EntityManager
return $this->_conn; return $this->_conn;
} }
/**
* Gets the metadata factory used to gather the metadata of classes.
*/
public function getMetadataFactory()
{
return $this->_metadataFactory;
}
/** /**
* Gets the name of the EntityManager. * Gets the name of the EntityManager.
* *
@ -180,16 +193,6 @@ class Doctrine_ORM_EntityManager
return $this->_name; return $this->_name;
} }
/**
* Gets the metadata for a class. Alias for getClassMetadata().
*
* @return Doctrine_Metadata
*/
public function getMetadata($className)
{
return $this->getClassMetadata($className);
}
/** /**
* Starts a database transaction. * Starts a database transaction.
*/ */
@ -224,18 +227,39 @@ class Doctrine_ORM_EntityManager
} }
/** /**
* Sets the driver that is used to obtain metadata mapping information * Gets an IdGenerator that can be used to generate identifiers for the specified
* about Entities. * class.
*
* @param $driver The driver to use.
*/ */
public function setClassMetadataDriver($driver) public function getIdGenerator($className)
{ {
$this->_metadataFactory->setDriver($driver); if (!isset($this->_idGenerators[$className])) {
$this->_idGenerators[$className] = $this->_createIdGenerator(
$this->getClassMetadata($className)->getIdGeneratorType());
}
return $this->_idGenerators[$className];
} }
/** /**
* Creates a new Doctrine_Query object that operates on this connection. * Used to lazily create the id generator.
*
* @param string $generatorType
* @return void
*/
protected function _createIdGenerator($generatorType)
{
if ($generatorType == Doctrine_ORM_Mapping_ClassMetadata::GENERATOR_TYPE_IDENTITY) {
return new Doctrine_ORM_Id_IdentityGenerator($this);
} else if ($generatorType == Doctrine_ORM_Mapping_ClassMetadata::GENERATOR_TYPE_SEQUENCE) {
return new Doctrine_ORM_Id_SequenceGenerator($this);
} else if ($generatorType == Doctrine_ORM_Mapping_ClassMetadata::GENERATOR_TYPE_TABLE) {
return new Doctrine_ORM_Id_TableGenerator($this);
} else {
return new Doctrine_ORM_Id_Assigned($this);
}
}
/**
* Creates a new Query object.
* *
* @param string The DQL string. * @param string The DQL string.
* @return Doctrine::ORM::Query * @return Doctrine::ORM::Query
@ -405,7 +429,7 @@ class Doctrine_ORM_EntityManager
/** /**
* Saves the given entity, persisting it's state. * Saves the given entity, persisting it's state.
* *
* @param Doctrine::ORM::Entity $entity * @param Doctrine\ORM\Entity $entity
* @return void * @return void
*/ */
public function save(Doctrine_ORM_Entity $entity) public function save(Doctrine_ORM_Entity $entity)
@ -487,9 +511,9 @@ class Doctrine_ORM_EntityManager
* *
* @param string $className The name of the entity class. * @param string $className The name of the entity class.
* @param array $data The data for the entity. * @param array $data The data for the entity.
* @return Doctrine::ORM::Entity * @return Doctrine\ORM\Entity
*/ */
public function createEntity($className, array $data) public function createEntity($className, array $data, Doctrine_Query $query = null)
{ {
$this->_errorIfNotActiveOrClosed(); $this->_errorIfNotActiveOrClosed();
@ -512,9 +536,9 @@ class Doctrine_ORM_EntityManager
$entity = new $className; $entity = new $className;
} else { } else {
$idHash = $this->_unitOfWork->getIdentifierHash($id); $idHash = $this->_unitOfWork->getIdentifierHash($id);
if ($entity = $this->_unitOfWork->tryGetByIdHash($idHash, $entity = $this->_unitOfWork->tryGetByIdHash($idHash, $classMetadata->getRootClassName());
$classMetadata->getRootClassName())) { if ($entity) {
$this->_mergeData($entity, $data); $this->_mergeData($entity, $data/*, $classMetadata, $query->getHint('doctrine.refresh')*/);
return $entity; return $entity;
} else { } else {
$entity = new $className; $entity = new $className;
@ -525,6 +549,8 @@ class Doctrine_ORM_EntityManager
$entity = new $className; $entity = new $className;
} }
//$this->_originalEntityData[$entity->getOid()] = $data;
return $entity; return $entity;
} }
@ -532,12 +558,12 @@ class Doctrine_ORM_EntityManager
* Merges the given data into the given entity, optionally overriding * Merges the given data into the given entity, optionally overriding
* local changes. * local changes.
* *
* @param Doctrine::ORM::Entity $entity * @param Doctrine\ORM\Entity $entity
* @param array $data * @param array $data
* @param boolean $overrideLocalChanges * @param boolean $overrideLocalChanges
* @return void * @return void
*/ */
private function _mergeData(Doctrine_ORM_Entity $entity, array $data, $overrideLocalChanges = false) { private function _mergeData(Doctrine_ORM_Entity $entity, /*$class,*/ array $data, $overrideLocalChanges = false) {
if ($overrideLocalChanges) { if ($overrideLocalChanges) {
foreach ($data as $field => $value) { foreach ($data as $field => $value) {
$entity->_internalSetField($field, $value); $entity->_internalSetField($field, $value);
@ -550,6 +576,21 @@ class Doctrine_ORM_EntityManager
} }
} }
} }
// NEW
/*if ($overrideLocalChanges) {
foreach ($data as $field => $value) {
$class->getReflectionProperty($field)->setValue($entity, $value);
}
} else {
foreach ($data as $field => $value) {
$currentValue = $class->getReflectionProperty($field)->getValue($entity);
if ( ! isset($this->_originalEntityData[$entity->getOid()]) ||
$currentValue == $this->_originalEntityData[$entity->getOid()]) {
$class->getReflectionProperty($field)->setValue($entity, $value);
}
}
}*/
} }
/** /**
@ -628,6 +669,8 @@ class Doctrine_ORM_EntityManager
/** /**
* Throws an exception if the EntityManager is closed or currently not active. * Throws an exception if the EntityManager is closed or currently not active.
*
* @throws EntityManagerException If the EntityManager is closed or not active.
*/ */
private function _errorIfNotActiveOrClosed() private function _errorIfNotActiveOrClosed()
{ {
@ -668,15 +711,16 @@ class Doctrine_ORM_EntityManager
/** /**
* Factory method to create EntityManager instances. * Factory method to create EntityManager instances.
*
* A newly created EntityManager is immediately activated, making it the * A newly created EntityManager is immediately activated, making it the
* currently active EntityManager. * currently active EntityManager.
* *
* @param mixed $conn An array with the connection parameters or an existing * @param mixed $conn An array with the connection parameters or an existing
* Doctrine::DBAL::Connection instance. * Connection instance.
* @param string $name * @param string $name The name of the EntityManager.
* @param Doctrine::Common::Configuration $config The Configuration instance to use. * @param Configuration $config The Configuration instance to use.
* @param Doctrine::Common::EventManager $eventManager The EventManager instance to use. * @param EventManager $eventManager The EventManager instance to use.
* @return Doctrine::ORM::EntityManager The created EntityManager. * @return EntityManager The created EntityManager.
*/ */
public static function create($conn, $name, Doctrine_Common_Configuration $config = null, public static function create($conn, $name, Doctrine_Common_Configuration $config = null,
Doctrine_Common_EventManager $eventManager = null) Doctrine_Common_EventManager $eventManager = null)
@ -702,8 +746,6 @@ class Doctrine_ORM_EntityManager
/** /**
* Static lookup to get the currently active EntityManager. * Static lookup to get the currently active EntityManager.
* This is used in the Entity constructor as well as unserialize() to connect
* the Entity with an EntityManager.
* *
* @return Doctrine::ORM::EntityManager * @return Doctrine::ORM::EntityManager
*/ */

View File

@ -18,7 +18,7 @@ abstract class Doctrine_ORM_Id_AbstractIdGenerator
$this->_em = $em; $this->_em = $em;
} }
abstract public function generate(Doctrine_ORM_Entity $entity); abstract public function generate($entity);
} }
?> ?>

View File

@ -14,11 +14,12 @@ class Doctrine_ORM_Id_Assigned extends Doctrine_ORM_Id_AbstractIdGenerator
* @return unknown * @return unknown
* @override * @override
*/ */
public function generate(Doctrine_ORM_Entity $entity) public function generate($entity)
{ {
if ( ! $entity->_identifier()) { if ( ! $entity->_identifier()) {
throw Doctrine_IdException::missingAssignedId($entity); throw new Doctrine_Exception("Entity '$entity' is missing an assigned Id");
} }
return $entity->_identifier();
} }
} }

View File

@ -9,7 +9,7 @@ class Doctrine_ORM_Id_IdentityGenerator extends Doctrine_ORM_Id_AbstractIdGenera
* @return unknown * @return unknown
* @override * @override
*/ */
public function generate(Doctrine_ORM_Entity $entity) public function generate($entity)
{ {
return self::POST_INSERT_INDICATOR; return self::POST_INSERT_INDICATOR;
} }

View File

@ -15,7 +15,7 @@ class Doctrine_ORM_Id_SequenceGenerator extends Doctrine_ORM_Id_AbstractIdGenera
* @param Doctrine_ORM_Entity $entity * @param Doctrine_ORM_Entity $entity
* @override * @override
*/ */
public function generate(Doctrine_ORM_Entity $entity) public function generate($entity)
{ {
$conn = $this->_em->getConnection(); $conn = $this->_em->getConnection();
$sql = $conn->getDatabasePlatform()->getSequenceNextValSql($this->_sequenceName); $sql = $conn->getDatabasePlatform()->getSequenceNextValSql($this->_sequenceName);

View File

@ -8,7 +8,7 @@
class Doctrine_ORM_Id_TableGenerator extends Doctrine_ORM_Id_AbstractIdGenerator class Doctrine_ORM_Id_TableGenerator extends Doctrine_ORM_Id_AbstractIdGenerator
{ {
public function generate(Doctrine_ORM_Entity $entity) public function generate($entity)
{ {
throw new Exception("Not implemented"); throw new Exception("Not implemented");
} }

View File

@ -129,6 +129,38 @@ class Doctrine_ORM_Internal_Hydration_ArrayDriver
return key($data); return key($data);
} }
/**
* Updates the result pointer for an Entity. The result pointers point to the
* last seen instance of each Entity type. This is used for graph construction.
*
* @param array $resultPointers The result pointers.
* @param array|Collection $coll The element.
* @param boolean|integer $index Index of the element in the collection.
* @param string $dqlAlias
* @param boolean $oneToOne Whether it is a single-valued association or not.
*/
public function updateResultPointer(&$resultPointers, &$coll, $index, $dqlAlias, $oneToOne)
{
if ($coll === null) {
unset($resultPointers[$dqlAlias]); // Ticket #1228
return;
}
if ($index !== false) {
$resultPointers[$dqlAlias] =& $coll[$index];
return;
}
if ($coll) {
if ($oneToOne) {
$resultPointers[$dqlAlias] =& $coll;
} else {
end($coll);
$resultPointers[$dqlAlias] =& $coll[key($coll)];
}
}
}
/** /**
* *
*/ */

View File

@ -19,7 +19,7 @@
* <http://www.phpdoctrine.org>. * <http://www.phpdoctrine.org>.
*/ */
#namespace Doctrine::ORM::Internal::Hydration; #namespace Doctrine\ORM\Internal\Hydration;
/** /**
* Hydration strategy used for creating graphs of entities. * Hydration strategy used for creating graphs of entities.
@ -41,6 +41,7 @@ class Doctrine_ORM_Internal_Hydration_ObjectDriver
private $_nullObject; private $_nullObject;
/** The EntityManager */ /** The EntityManager */
private $_em; private $_em;
private $_metadataMap = array();
public function __construct(Doctrine_ORM_EntityManager $em) public function __construct(Doctrine_ORM_EntityManager $em)
{ {
@ -52,32 +53,36 @@ class Doctrine_ORM_Internal_Hydration_ObjectDriver
{ {
$coll = new Doctrine_ORM_Collection($component); $coll = new Doctrine_ORM_Collection($component);
$this->_collections[] = $coll; $this->_collections[] = $coll;
return $coll; return $coll;
} }
public function getLastKey($coll) public function getLastKey($coll)
{ {
// check needed because of mixed results // check needed because of mixed results.
if (is_array($coll)) { // is_object instead of is_array because is_array is slow.
end($coll); if (is_object($coll)) {
return key($coll);
} else {
$coll->end(); $coll->end();
return $coll->key(); return $coll->key();
} else {
end($coll);
return key($coll);
} }
} }
public function initRelatedCollection(Doctrine_ORM_Entity $entity, $name) public function initRelatedCollection($entity, $name)
{ {
if ( ! isset($this->_initializedRelations[$entity->getOid()][$name])) { //$class = get_class($entity);
$relation = $entity->getClass()->getAssociationMapping($name); $oid = spl_object_id($entity);
$classMetadata = $this->_metadataMap[$oid];
//$classMetadata = $this->_em->getClassMetadata(get_class($entity));
if ( ! isset($this->_initializedRelations[$oid][$name])) {
$relation = $classMetadata->getAssociationMapping($name);
$relatedClass = $this->_em->getClassMetadata($relation->getTargetEntityName()); $relatedClass = $this->_em->getClassMetadata($relation->getTargetEntityName());
$coll = $this->getElementCollection($relatedClass->getClassName()); $coll = $this->getElementCollection($relatedClass->getClassName());
$coll->_setOwner($entity, $relation); $coll->_setOwner($entity, $relation);
$coll->_setHydrationFlag(true); $coll->_setHydrationFlag(true);
$entity->_internalSetReference($name, $coll, true); $classMetadata->getReflectionProperty($name)->setValue($entity, $coll);
$this->_initializedRelations[$entity->getOid()][$name] = true; $this->_initializedRelations[$oid][$name] = true;
} }
} }
@ -93,49 +98,91 @@ class Doctrine_ORM_Internal_Hydration_ObjectDriver
public function getElement(array $data, $className) public function getElement(array $data, $className)
{ {
return $this->_em->createEntity($className, $data); $entity = $this->_em->getUnitOfWork()->createEntity($className, $data);
$this->_metadataMap[spl_object_id($entity)] = $this->_em->getClassMetadata($className);
return $entity;
} }
public function addRelatedIndexedElement(Doctrine_ORM_Entity $entity1, $property, public function addRelatedIndexedElement($entity1, $property, $entity2, $indexField)
Doctrine_ORM_Entity $entity2, $indexField)
{ {
$entity1->_internalGetReference($property)->add($entity2, $entity2->_internalGetField($indexField)); $classMetadata1 = $this->_metadataMap[spl_object_id($entity1)];
$classMetadata2 = $this->_metadataMap[spl_object_id($entity2)];
//$classMetadata1 = $this->_em->getClassMetadata(get_class($entity1));
//$classMetadata2 = $this->_em->getClassMetadata(get_class($entity2));
$indexValue = $classMetadata2->getReflectionProperty($indexField)->getValue($entity2);
$classMetadata1->getReflectionProperty($property)->getValue($entity1)->add($entity2, $indexValue);
} }
public function addRelatedElement(Doctrine_ORM_Entity $entity1, $property, public function addRelatedElement($entity1, $property, $entity2)
Doctrine_ORM_Entity $entity2)
{ {
$entity1->_internalGetReference($property)->add($entity2); $classMetadata1 = $this->_metadataMap[spl_object_id($entity1)];
//$classMetadata1 = $this->_em->getClassMetadata(get_class($entity1));
$classMetadata1->getReflectionProperty($property)
->getValue($entity1)->add($entity2);
} }
public function setRelatedElement(Doctrine_ORM_Entity $entity1, $property, $entity2) public function setRelatedElement($entity1, $property, $entity2)
{ {
$entity1->_internalSetReference($property, $entity2, true); $classMetadata1 = $this->_metadataMap[spl_object_id($entity1)];
//$classMetadata1 = $this->_em->getClassMetadata(get_class($entity1));
$classMetadata1->getReflectionProperty($property)
->setValue($entity1, $entity2);
$relation = $classMetadata1->getAssociationMapping($property);
if ($relation->isOneToOne()) {
$targetClass = $this->_em->getClassMetadata($relation->getTargetEntityName());
if ($relation->isOwningSide()) {
// If there is an inverse mapping on the target class its bidirectional
if ($targetClass->hasInverseAssociationMapping($property)) {
$refProp = $targetClass->getReflectionProperty(
$targetClass->getInverseAssociationMapping($fieldName)
->getSourceFieldName());
$refProp->setValue($entity2, $entity1);
}
} else {
// for sure bidirectional, as there is no inverse side in unidirectional
$targetClass->getReflectionProperty($relation->getMappedByFieldName())
->setValue($entity2, $entity1);
}
}
} }
public function isIndexKeyInUse(Doctrine_ORM_Entity $entity, $assocField, $indexField) public function isIndexKeyInUse($entity, $assocField, $indexField)
{ {
return $entity->_internalGetReference($assocField)->contains($indexField); return $this->_metadataMap[spl_object_id($entity)]->getReflectionProperty($assocField)
->getValue($entity)->containsKey($indexField);
/*return $this->_em->getClassMetadata(get_class($entity))->getReflectionProperty($assocField)
->getValue($entity)->containsKey($indexField);*/
} }
public function isFieldSet(Doctrine_ORM_Entity $entity, $field) public function isFieldSet($entity, $field)
{ {
return $entity->contains($field); return $this->_metadataMap[spl_object_id($entity)]->getReflectionProperty($field)
->getValue($entity) !== null;
/*return $this->_em->getClassMetadata(get_class($entity))->getReflectionProperty($field)
->getValue($entity) !== null;*/
} }
public function getFieldValue(Doctrine_ORM_Entity $entity, $field) public function getFieldValue($entity, $field)
{ {
return $entity->_internalGetField($field); return $this->_metadataMap[spl_object_id($entity)]->getReflectionProperty($field)
->getValue($entity);
/*return $this->_em->getClassMetadata(get_class($entity))->getReflectionProperty($field)
->getValue($entity);*/
} }
public function getReferenceValue(Doctrine_ORM_Entity $entity, $field) public function getReferenceValue($entity, $field)
{ {
return $entity->_internalGetReference($field); return $this->_metadataMap[spl_object_id($entity)]->getReflectionProperty($field)
->getValue($entity);
/*return $this->_em->getClassMetadata(get_class($entity))->getReflectionProperty($field)
->getValue($entity);*/
} }
public function addElementToIndexedCollection($coll, $entity, $keyField) public function addElementToIndexedCollection($coll, $entity, $keyField)
{ {
$coll->add($entity, $entity->_internalGetField($keyField)); $coll->add($entity, $this->getFieldValue($entity, $keyField));
} }
public function addElementToCollection($coll, $entity) public function addElementToCollection($coll, $entity)
@ -143,6 +190,40 @@ class Doctrine_ORM_Internal_Hydration_ObjectDriver
$coll->add($entity); $coll->add($entity);
} }
/**
* Updates the result pointer for an Entity. The result pointers point to the
* last seen instance of each Entity type. This is used for graph construction.
*
* @param array $resultPointers The result pointers.
* @param array|Collection $coll The element.
* @param boolean|integer $index Index of the element in the collection.
* @param string $dqlAlias
* @param boolean $oneToOne Whether it is a single-valued association or not.
*/
public function updateResultPointer(&$resultPointers, &$coll, $index, $dqlAlias, $oneToOne)
{
if ($coll === $this->_nullObject) {
unset($resultPointers[$dqlAlias]); // Ticket #1228
return;
}
if ($index !== false) {
$resultPointers[$dqlAlias] = $coll[$index];
return;
}
if ( ! is_object($coll)) {
end($coll);
$resultPointers[$dqlAlias] =& $coll[key($coll)];
} else if ($coll instanceof Doctrine_ORM_Collection) {
if (count($coll) > 0) {
$resultPointers[$dqlAlias] = $coll->getLast();
}
} else {
$resultPointers[$dqlAlias] = $coll;
}
}
public function flush() public function flush()
{ {
// take snapshots from all initialized collections // take snapshots from all initialized collections
@ -152,6 +233,7 @@ class Doctrine_ORM_Internal_Hydration_ObjectDriver
} }
$this->_collections = array(); $this->_collections = array();
$this->_initializedRelations = array(); $this->_initializedRelations = array();
$this->_metadataMap = array();
} }
} }

View File

@ -19,7 +19,7 @@
* <http://www.phpdoctrine.org>. * <http://www.phpdoctrine.org>.
*/ */
#namespace Doctrine::ORM::Internal::Hydration; #namespace Doctrine\ORM\Internal\Hydration;
/** /**
* The hydrator has the tedious to process result sets returned by the database * The hydrator has the tedious to process result sets returned by the database
@ -104,7 +104,6 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
$s = microtime(true); $s = microtime(true);
// Used variables during hydration
reset($this->_queryComponents); reset($this->_queryComponents);
$rootAlias = key($this->_queryComponents); $rootAlias = key($this->_queryComponents);
$rootEntityName = $this->_queryComponents[$rootAlias]['metadata']->getClassName(); $rootEntityName = $this->_queryComponents[$rootAlias]['metadata']->getClassName();
@ -136,8 +135,7 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
// Initialize // Initialize
foreach ($this->_queryComponents as $dqlAlias => $component) { foreach ($this->_queryComponents as $dqlAlias => $component) {
// disable lazy-loading of related elements during hydration // disable lazy-loading of related elements during hydration
$component['metadata']->setAttribute('loadReferences', false); //$component['metadata']->setAttribute('loadReferences', false);
$entityName = $component['metadata']->getClassName();
$identifierMap[$dqlAlias] = array(); $identifierMap[$dqlAlias] = array();
$resultPointers[$dqlAlias] = array(); $resultPointers[$dqlAlias] = array();
$idTemplate[$dqlAlias] = ''; $idTemplate[$dqlAlias] = '';
@ -147,6 +145,7 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
// Evaluate HYDRATE_SINGLE_SCALAR // Evaluate HYDRATE_SINGLE_SCALAR
if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_SINGLE_SCALAR) { if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_SINGLE_SCALAR) {
$result = $stmt->fetchAll(PDO::FETCH_ASSOC); $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
//TODO: Let this exception be raised by Query as QueryException
if (count($result) > 1 || count($result[0]) > 1) { if (count($result) > 1 || count($result[0]) > 1) {
throw Doctrine_ORM_Exceptions_HydrationException::nonUniqueResult(); throw Doctrine_ORM_Exceptions_HydrationException::nonUniqueResult();
} }
@ -193,7 +192,7 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
} else { } else {
$index = $identifierMap[$rootAlias][$id[$rootAlias]]; $index = $identifierMap[$rootAlias][$id[$rootAlias]];
} }
$this->_updateResultPointer($resultPointers, $result, $index, $rootAlias, false); $driver->updateResultPointer($resultPointers, $result, $index, $rootAlias, false);
unset($rowData[$rootAlias]); unset($rowData[$rootAlias]);
// end hydrate data of the root component for the current row // end hydrate data of the root component for the current row
@ -211,10 +210,11 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
$entityName = $map['metadata']->getClassName(); $entityName = $map['metadata']->getClassName();
$parent = $map['parent']; $parent = $map['parent'];
$relation = $map['relation']; $relation = $map['relation'];
$relationAlias = $relation->getSourceFieldName();//$relation->getAlias(); $relationAlias = $relation->getSourceFieldName();
$path = $parent . '.' . $dqlAlias; $path = $parent . '.' . $dqlAlias;
// pick the right element that will get the associated element attached // Get a reference to the right element in the result tree.
// This element will get the associated element attached.
if ($parserResult->isMixedQuery() && $parent == $rootAlias) { if ($parserResult->isMixedQuery() && $parent == $rootAlias) {
$key = key(reset($resultPointers)); $key = key(reset($resultPointers));
// TODO: Exception if $key === null ? // TODO: Exception if $key === null ?
@ -228,13 +228,13 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
// check the type of the relation (many or single-valued) // check the type of the relation (many or single-valued)
if ( ! $relation->isOneToOne()) { if ( ! $relation->isOneToOne()) {
// x-many relation // x-to-many relation
$oneToOne = false; $oneToOne = false;
if (isset($nonemptyComponents[$dqlAlias])) { if (isset($nonemptyComponents[$dqlAlias])) {
$driver->initRelatedCollection($baseElement, $relationAlias); $driver->initRelatedCollection($baseElement, $relationAlias);
$indexExists = isset($identifierMap[$path][$id[$parent]][$id[$dqlAlias]]); $indexExists = isset($identifierMap[$path][$id[$parent]][$id[$dqlAlias]]);
$index = $indexExists ? $identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false; $index = $indexExists ? $identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false;
$indexIsValid = $index !== false ? isset($baseElement[$relationAlias][$index]) : false; $indexIsValid = $index !== false ? $driver->isIndexKeyInUse($baseElement, $relationAlias, $index) : false;
if ( ! $indexExists || ! $indexIsValid) { if ( ! $indexExists || ! $indexIsValid) {
$element = $driver->getElement($data, $entityName); $element = $driver->getElement($data, $entityName);
if ($field = $this->_getCustomIndexField($dqlAlias)) { if ($field = $this->_getCustomIndexField($dqlAlias)) {
@ -245,7 +245,7 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
$identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = $driver->getLastKey( $identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = $driver->getLastKey(
$driver->getReferenceValue($baseElement, $relationAlias)); $driver->getReferenceValue($baseElement, $relationAlias));
} }
} else if ( ! isset($baseElement[$relationAlias])) { } else if ( ! $driver->isFieldSet($baseElement, $relationAlias)) {
if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY) { if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY) {
$baseElement[$relationAlias] = array(); $baseElement[$relationAlias] = array();
} else { } else {
@ -254,7 +254,7 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
} }
} }
} else { } else {
// x-1 relation // x-to-one relation
$oneToOne = true; $oneToOne = true;
if ( ! isset($nonemptyComponents[$dqlAlias]) && if ( ! isset($nonemptyComponents[$dqlAlias]) &&
! $driver->isFieldSet($baseElement, $relationAlias)) { ! $driver->isFieldSet($baseElement, $relationAlias)) {
@ -269,15 +269,17 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY) { if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY) {
$coll =& $baseElement[$relationAlias]; $coll =& $baseElement[$relationAlias];
} else { } else {
$coll = $baseElement->_internalGetReference($relationAlias); $coll = $driver->getReferenceValue($baseElement, $relationAlias);
//$baseElement->_internalGetReference($relationAlias);
} }
if ($coll !== null) { if ($coll !== null) {
$this->_updateResultPointer($resultPointers, $coll, $index, $dqlAlias, $oneToOne); $driver->updateResultPointer($resultPointers, $coll, $index, $dqlAlias, $oneToOne);
} }
} }
// append scalar values to mixed result sets // Append scalar values to mixed result sets
//TODO: we dont need to count every time here, instead count with the loop
if (isset($scalars)) { if (isset($scalars)) {
$rowNumber = count($result) - 1; $rowNumber = count($result) - 1;
foreach ($scalars as $name => $value) { foreach ($scalars as $name => $value) {
@ -289,53 +291,17 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
$stmt->closeCursor(); $stmt->closeCursor();
$driver->flush(); $driver->flush();
// re-enable lazy loading /*// re-enable lazy loading
foreach ($this->_queryComponents as $dqlAlias => $data) { foreach ($this->_queryComponents as $dqlAlias => $data) {
$data['metadata']->setAttribute('loadReferences', true); $data['metadata']->setAttribute('loadReferences', true);
} }*/
$e = microtime(true); $e = microtime(true);
echo 'Hydration took: ' . ($e - $s) . ' for '.count($result).' records' . PHP_EOL; echo 'Hydration took: ' . ($e - $s) . PHP_EOL;
return $result; return $result;
} }
/**
* Updates the result pointer for an Entity. The result pointers point to the
* last seen instance of each Entity type. This is used for graph construction.
*
* @param array $resultPointers The result pointers.
* @param array|Collection $coll The element.
* @param boolean|integer $index Index of the element in the collection.
* @param string $dqlAlias
* @param boolean $oneToOne Whether it is a single-valued association or not.
*/
protected function _updateResultPointer(&$resultPointers, &$coll, $index, $dqlAlias, $oneToOne)
{
if ($coll === $this->_nullObject || $coll === null) {
unset($resultPointers[$dqlAlias]); // Ticket #1228
return;
}
if ($index !== false) {
$resultPointers[$dqlAlias] =& $coll[$index];
return;
}
if (is_array($coll) && $coll) {
if ($oneToOne) {
$resultPointers[$dqlAlias] =& $coll;
} else {
end($coll);
$resultPointers[$dqlAlias] =& $coll[key($coll)];
}
} else if ($coll instanceof Doctrine_ORM_Entity) {
$resultPointers[$dqlAlias] = $coll;
} else if (count($coll) > 0) {
$resultPointers[$dqlAlias] = $coll->getLast();
}
}
/** /**
* Processes a row of the result set. * Processes a row of the result set.
* Used for identity hydration (HYDRATE_IDENTITY_OBJECT and HYDRATE_IDENTITY_ARRAY). * Used for identity hydration (HYDRATE_IDENTITY_OBJECT and HYDRATE_IDENTITY_ARRAY).
@ -370,7 +336,7 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
$fieldName = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$columnName]; $fieldName = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$columnName];
$cache[$key]['isScalar'] = true; $cache[$key]['isScalar'] = true;
} else { } else {
$fieldName = $classMetadata->lookupFieldName($columnName); $fieldName = $this->_lookupFieldName($classMetadata, $columnName);
$cache[$key]['isScalar'] = false; $cache[$key]['isScalar'] = false;
} }
@ -455,7 +421,7 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
$fieldName = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$columnName]; $fieldName = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$columnName];
$cache[$key]['isScalar'] = true; $cache[$key]['isScalar'] = true;
} else { } else {
$fieldName = $classMetadata->lookupFieldName($columnName); $fieldName = $this->_lookupFieldName($classMetadata, $columnName);
$cache[$key]['isScalar'] = false; $cache[$key]['isScalar'] = false;
} }
@ -511,6 +477,42 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
return $name == 'doctrine_rownum'; return $name == 'doctrine_rownum';
} }
/**
* Looks up the field name for a (lowercased) column name.
*
* This is mostly used during hydration, because we want to make the
* conversion to field names while iterating over the result set for best
* performance. By doing this at that point, we can avoid re-iterating over
* the data just to convert the column names to field names.
*
* However, when this is happening, we don't know the real
* class name to instantiate yet (the row data may target a sub-type), hence
* this method looks up the field name in the subclass mappings if it's not
* found on this class mapping.
* This lookup on subclasses is costly but happens only *once* for a column
* during hydration because the hydrator caches effectively.
*
* @return string The field name.
* @throws Doctrine::ORM::Exceptions::ClassMetadataException If the field name could
* not be found.
*/
private function _lookupFieldName($class, $lcColumnName)
{
if ($class->hasLowerColumn($lcColumnName)) {
return $class->getFieldNameForLowerColumnName($lcColumnName);
}
foreach ($class->getSubclasses() as $subClass) {
$subClassMetadata = Doctrine_ORM_Mapping_ClassMetadataFactory::getInstance()
->getMetadataFor($subClass);
if ($subClassMetadata->hasLowerColumn($lcColumnName)) {
return $subClassMetadata->getFieldNameForLowerColumnName($lcColumnName);
}
}
throw new Doctrine_Exception("No field name found for column name '$lcColumnName' during hydration.");
}
/** /**
* prepareValue * prepareValue
* this method performs special data preparation depending on * this method performs special data preparation depending on

View File

@ -19,7 +19,7 @@
* <http://www.phpdoctrine.org>. * <http://www.phpdoctrine.org>.
*/ */
#namespace Doctrine::ORM::Mapping; #namespace Doctrine\ORM\Mapping;
/** /**
* Base class for association mappings. * Base class for association mappings.

View File

@ -19,9 +19,10 @@
* <http://www.phpdoctrine.org>. * <http://www.phpdoctrine.org>.
*/ */
#namespace Doctrine::ORM::Mapping; #namespace Doctrine\ORM\Mapping;
#use Doctrine::ORM::EntityManager; #use \Serializable;
#use Doctrine\Common\ClassMetadata;
/** /**
* A <tt>ClassMetadata</tt> instance holds all the information (metadata) of an entity and * A <tt>ClassMetadata</tt> instance holds all the information (metadata) of an entity and
@ -32,7 +33,8 @@
* @since 2.0 * @since 2.0
* @todo Rename to ClassDescriptor. * @todo Rename to ClassDescriptor.
*/ */
class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable, Serializable class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
implements Doctrine_Common_Configurable, Serializable
{ {
/* The inheritance mapping types */ /* The inheritance mapping types */
/** /**
@ -101,13 +103,6 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
*/ */
const ENTITY_TYPE_MAPPED_SUPERCLASS = 'mappedSuperclass'; const ENTITY_TYPE_MAPPED_SUPERCLASS = 'mappedSuperclass';
/**
* The name of the entity class.
*
* @var string
*/
protected $_entityName;
/** /**
* The name of the entity class that is at the root of the entity inheritance * The name of the entity class that is at the root of the entity inheritance
* hierarchy. If the entity is not part of an inheritance hierarchy this is the same * hierarchy. If the entity is not part of an inheritance hierarchy this is the same
@ -118,20 +113,13 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
protected $_rootEntityName; protected $_rootEntityName;
/** /**
* The name of the custom mapper class used for the entity class. * The name of the custom repository class used for the entity class.
* (Optional). * (Optional).
* *
* @var string * @var string
*/ */
protected $_customRepositoryClassName; protected $_customRepositoryClassName;
/**
* The EntityManager.
*
* @var Doctrine::ORM::EntityManager
*/
protected $_em;
/** /**
* The names of the parent classes (ancestors). * The names of the parent classes (ancestors).
* *
@ -168,13 +156,6 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
*/ */
protected $_generatorType = self::GENERATOR_TYPE_NONE; protected $_generatorType = self::GENERATOR_TYPE_NONE;
/**
* The Id generator.
*
* @var Doctrine::ORM::Id::IdGenerator
*/
protected $_idGenerator;
/** /**
* The field mappings of the class. * The field mappings of the class.
* Keys are field names and values are mapping definitions. * Keys are field names and values are mapping definitions.
@ -233,21 +214,6 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
*/ */
protected $_fieldMappings = array(); protected $_fieldMappings = array();
/**
* The mapped embedded values (value objects).
*
* @var array
* @TODO Implementation (Value Object support)
*/
//protected $_embeddedValueMappings = array();
/**
* Enter description here...
*
* @var array
*/
protected $_attributes = array('loadReferences' => true);
/** /**
* An array of field names. used to look up field names from column names. * An array of field names. used to look up field names from column names.
* Keys are column names and values are field names. * Keys are column names and values are field names.
@ -347,30 +313,40 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
*/ */
protected $_isIdentifierComposite = false; protected $_isIdentifierComposite = false;
protected $_customAssociationAccessors = array(); protected $_reflectionClass;
protected $_customAssociationMutators = array(); protected $_reflectionProperties;
/** /**
* Constructs a new ClassMetadata instance. * Constructs a new ClassMetadata instance.
* *
* @param string $entityName Name of the entity class the metadata info is used for. * @param string $entityName Name of the entity class the metadata info is used for.
* @param Doctrine::ORM::Entitymanager $em
*/ */
public function __construct($entityName, Doctrine_ORM_EntityManager $em) public function __construct($entityName)
{ {
$this->_entityName = $entityName; parent::__construct($entityName);
$this->_rootEntityName = $entityName; $this->_rootEntityName = $entityName;
$this->_em = $em; $this->_reflectionClass = new ReflectionClass($entityName);
$reflectionProps = $this->_reflectionClass->getProperties();
foreach ($reflectionProps as $prop) {
$prop->setAccessible(true);
$this->_reflectionProperties[$prop->getName()] = $prop;
}
//$this->_isVirtualPropertyObject = is_subclass_of($entityName, 'Doctrine\Common\VirtualPropertyObject');
} }
/** public function getReflectionClass()
* Gets the EntityManager that holds this ClassMetadata.
*
* @return Doctrine::ORM::EntityManager
*/
public function getEntityManager()
{ {
return $this->_em; return $this->_reflectionClass;
}
public function getReflectionProperties()
{
return $this->_reflectionProperties;
}
public function getReflectionProperty($name)
{
return $this->_reflectionProperties[$name];
} }
/** /**
@ -447,7 +423,6 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
public function isNotNull($fieldName) public function isNotNull($fieldName)
{ {
$mapping = $this->getFieldMapping($fieldName); $mapping = $this->getFieldMapping($fieldName);
if ($mapping !== false) { if ($mapping !== false) {
return isset($mapping['notnull']) && $mapping['notnull'] == true; return isset($mapping['notnull']) && $mapping['notnull'] == true;
} }
@ -455,34 +430,6 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
return false; return false;
} }
/**
* adds an index to this table
*
* @return void
* @deprecated
* @todo Should be done through setTableOption().
*/
public function addIndex($index, array $definition)
{
$this->_tableOptions['indexes'][$index] = $definition;
}
/**
* getIndex
*
* @return array|boolean array on success, FALSE on failure
* @todo Should be done through getTableOption().
* @deprecated
*/
public function getIndex($index)
{
if (isset($this->_tableOptions['indexes'][$index])) {
return $this->_tableOptions['indexes'][$index];
}
return false;
}
/** /**
* Sets a table option. * Sets a table option.
*/ */
@ -547,11 +494,6 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
return $this->_fieldMappings[$fieldName]; return $this->_fieldMappings[$fieldName];
} }
public function addFieldMapping($fieldName, array $mapping)
{
$this->_fieldMappings[$fieldName] = $mapping;
}
/** /**
* Gets the mapping of an association. * Gets the mapping of an association.
* *
@ -562,7 +504,7 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
public function getAssociationMapping($fieldName) public function getAssociationMapping($fieldName)
{ {
if ( ! isset($this->_associationMappings[$fieldName])) { if ( ! isset($this->_associationMappings[$fieldName])) {
throw Doctrine_MappingException::mappingNotFound($fieldName); throw new Doctrine_Exception("Mapping not found: $fieldName");
} }
return $this->_associationMappings[$fieldName]; return $this->_associationMappings[$fieldName];
@ -579,7 +521,6 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
if ( ! isset($this->_associationMappings[$fieldName])) { if ( ! isset($this->_associationMappings[$fieldName])) {
throw Doctrine_MappingException::mappingNotFound($fieldName); throw Doctrine_MappingException::mappingNotFound($fieldName);
} }
return $this->_inverseMappings[$mappedByFieldName]; return $this->_inverseMappings[$mappedByFieldName];
} }
@ -594,11 +535,6 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
return isset($this->_inverseMappings[$mappedByFieldName]); return isset($this->_inverseMappings[$mappedByFieldName]);
} }
public function addAssociationMapping($fieldName, Doctrine_Association $assoc)
{
$this->_associationMappings[$fieldName] = $assoc;
}
/** /**
* Gets all association mappings of the class. * Gets all association mappings of the class.
* *
@ -647,51 +583,16 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
$this->_lcColumnToFieldNames[$lcColumnName] : $lcColumnName; $this->_lcColumnToFieldNames[$lcColumnName] : $lcColumnName;
} }
/**
*
* @param <type> $lcColumnName
* @return <type>
*/
public function hasLowerColumn($lcColumnName) public function hasLowerColumn($lcColumnName)
{ {
return isset($this->_lcColumnToFieldNames[$lcColumnName]); return isset($this->_lcColumnToFieldNames[$lcColumnName]);
} }
/**
* Looks up the field name for a (lowercased) column name.
*
* This is mostly used during hydration, because we want to make the
* conversion to field names while iterating over the result set for best
* performance. By doing this at that point, we can avoid re-iterating over
* the data just to convert the column names to field names.
*
* However, when this is happening, we don't know the real
* class name to instantiate yet (the row data may target a sub-type), hence
* this method looks up the field name in the subclass mappings if it's not
* found on this class mapping.
* This lookup on subclasses is costly but happens only *once* for a column
* during hydration because the hydrator caches effectively.
*
* @return string The field name.
* @throws Doctrine::ORM::Exceptions::ClassMetadataException If the field name could
* not be found.
*/
public function lookupFieldName($lcColumnName)
{
if (isset($this->_lcColumnToFieldNames[$lcColumnName])) {
return $this->_lcColumnToFieldNames[$lcColumnName];
}/* else if (isset($this->_subclassFieldNames[$lcColumnName])) {
return $this->_subclassFieldNames[$lcColumnName];
}*/
foreach ($this->getSubclasses() as $subClass) {
$subClassMetadata = $this->_em->getClassMetadata($subClass);
if ($subClassMetadata->hasLowerColumn($lcColumnName)) {
/*$this->_subclassFieldNames[$lcColumnName] = $subClassMetadata->
getFieldNameForLowerColumnName($lcColumnName);
return $this->_subclassFieldNames[$lcColumnName];*/
return $subClassMetadata->getFieldNameForLowerColumnName($lcColumnName);
}
}
throw new Doctrine_ClassMetadata_Exception("No field name found for column name '$lcColumnName' during lookup.");
}
/** /**
* Adds a field mapping. * Adds a field mapping.
* *
@ -732,11 +633,6 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
$this->_fieldNames[$mapping['columnName']] = $mapping['fieldName']; $this->_fieldNames[$mapping['columnName']] = $mapping['fieldName'];
$this->_lcColumnToFieldNames[$lcColumnName] = $mapping['fieldName']; $this->_lcColumnToFieldNames[$lcColumnName] = $mapping['fieldName'];
// Complete length mapping
if ( ! isset($mapping['length'])) {
$mapping['length'] = $this->_getDefaultLength($mapping['type']);
}
// Complete id mapping // Complete id mapping
if (isset($mapping['id']) && $mapping['id'] === true) { if (isset($mapping['id']) && $mapping['id'] === true) {
if ( ! in_array($mapping['fieldName'], $this->_identifier)) { if ( ! in_array($mapping['fieldName'], $this->_identifier)) {
@ -783,58 +679,6 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
//... //...
} }
/**
* Used to lazily create the id generator.
*
* @param string $generatorType
* @return void
*/
protected function _createIdGenerator()
{
if ($this->_generatorType == self::GENERATOR_TYPE_IDENTITY) {
$this->_idGenerator = new Doctrine_ORM_Id_IdentityGenerator($this->_em);
} else if ($this->_generatorType == self::GENERATOR_TYPE_SEQUENCE) {
$this->_idGenerator = new Doctrine_ORM_Id_SequenceGenerator($this->_em);
} else if ($this->_generatorType == self::GENERATOR_TYPE_TABLE) {
$this->_idGenerator = new Doctrine_ORM_Id_TableGenerator($this->_em);
} else {
$this->_idGenerator = new Doctrine_ORM_Id_Assigned($this->_em);
}
}
/**
* Gets the default length for a column type.
*
* @param string $type
* @return mixed
*/
private function _getDefaultLength($type)
{
switch ($type) {
case 'string':
case 'clob':
case 'float':
case 'integer':
case 'array':
case 'object':
case 'blob':
case 'gzip':
// use php int max
return 2147483647;
case 'boolean':
return 1;
case 'date':
// YYYY-MM-DD ISO 8601
return 10;
case 'time':
// HH:NN:SS+00:00 ISO 8601
return 14;
case 'timestamp':
// YYYY-MM-DDTHH:MM:SS+00:00 ISO 8601
return 25;
}
}
/** /**
* Maps an embedded value object. * Maps an embedded value object.
* *
@ -845,6 +689,59 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
//... //...
} }
private $_entityIdentifiers = array();
/**
* Gets the identifier of an entity.
*
* @param object $entity
* @return array Map of identifier field names to values.
*/
public function getEntityIdentifier($entity)
{
$oid = spl_object_id($entity);
if ( ! isset($this->_entityIdentifiers[$oid])) {
if ( ! $this->isIdentifierComposite()) {
$idField = $this->_identifier[0];
$idValue = $this->_reflectionProperties[$idField]->getValue($entity);
if (isset($idValue)) {
//return array($idField => $idValue);
$this->_entityIdentifiers[$oid] = array($idField => $idValue);
} else {
return false;
}
//$this->_entityIdentifiers[$oid] = false;
} else {
$id = array();
foreach ($this->getIdentifierFieldNames() as $idFieldName) {
$idValue = $this->_reflectionProperties[$idFieldName]->getValue($entity);
if (isset($idValue)) {
$id[$idFieldName] = $idValue;
}
}
//return $id;
$this->_entityIdentifiers[$oid] = $id;
}
}
return $this->_entityIdentifiers[$oid];
}
/**
*
*
* @param <type> $entity
* @param <type> $identifier
*/
public function setEntityIdentifier($entity, $identifier)
{
if (is_array($identifier)) {
foreach ($identifier as $fieldName => $value) {
$this->_reflectionProperties[$fieldName]->setValue($entity, $value);
}
} else {
$this->_reflectionProperties[$this->_identifier[0]]->setValue($entity, $identifier);
}
}
/** /**
* Gets the identifier (primary key) field names of the class. * Gets the identifier (primary key) field names of the class.
* *
@ -896,38 +793,6 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
return isset($this->_columnNames[$fieldName]); return isset($this->_columnNames[$fieldName]);
} }
/**
* Gets the custom accessor of a field.
*
* @return string The name of the accessor (getter) method or NULL if the field does
* not have a custom accessor.
*/
public function getCustomAccessor($fieldName)
{
if (isset($this->_fieldMappings[$fieldName]['accessor'])) {
return $this->_fieldMappings[$fieldName]['accessor'];
} else if (isset($this->_customAssociationAccessors[$fieldName])) {
return $this->_customAssociationAccessors[$fieldName];
}
return null;
}
/**
* Gets the custom mutator of a field.
*
* @return string The name of the mutator (setter) method or NULL if the field does
* not have a custom mutator.
*/
public function getCustomMutator($fieldName)
{
if (isset($this->_fieldMappings[$fieldName]['mutator'])) {
return $this->_fieldMappings[$fieldName]['mutator'];
} else if (isset($this->_customAssociationMutators[$fieldName])) {
return $this->_customAssociationMutators[$fieldName];
}
return null;
}
/** /**
* Gets all field mappings. * Gets all field mappings.
* *
@ -986,6 +851,14 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
return $this->_generatorType; return $this->_generatorType;
} }
/**
* Sets the type of Id generator to use for this class.
*/
public function setIdGeneratorType($generatorType)
{
$this->_generatorType = $generatorType;
}
/** /**
* Checks whether the class uses an Id generator. * Checks whether the class uses an Id generator.
* *
@ -996,16 +869,43 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
return $this->_generatorType != self::GENERATOR_TYPE_NONE; return $this->_generatorType != self::GENERATOR_TYPE_NONE;
} }
/**
*
* @return <type>
*/
public function isInheritanceTypeNone()
{
return $this->_inheritanceType == self::INHERITANCE_TYPE_NONE;
}
/**
* Checks whether the mapped class uses the JOINED inheritance mapping strategy.
*
* @return boolean TRUE if the class participates in a JOINED inheritance mapping,
* FALSE otherwise.
*/
public function isInheritanceTypeJoined() public function isInheritanceTypeJoined()
{ {
return $this->_inheritanceType == self::INHERITANCE_TYPE_JOINED; return $this->_inheritanceType == self::INHERITANCE_TYPE_JOINED;
} }
/**
* Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy.
*
* @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping,
* FALSE otherwise.
*/
public function isInheritanceTypeSingleTable() public function isInheritanceTypeSingleTable()
{ {
return $this->_inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE; return $this->_inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE;
} }
/**
* Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy.
*
* @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping,
* FALSE otherwise.
*/
public function isInheritanceTypeTablePerClass() public function isInheritanceTypeTablePerClass()
{ {
return $this->_inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS; return $this->_inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
@ -1110,9 +1010,9 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
} }
/** /**
* Gets the inheritance type used by the class. * Gets the inheritance mapping type used by the class.
* *
* @return integer * @return string
*/ */
public function getInheritanceType() public function getInheritanceType()
{ {
@ -1141,29 +1041,6 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
return $this->_subClasses; return $this->_subClasses;
} }
/**
* Gets the name of the class in the entity hierarchy that owns the field with
* the given name. The owning class is the one that defines the field.
*
* @param string $fieldName
* @return string
* @todo Consider using 'inherited' => 'ClassName' to make the lookup simpler.
*/
public function getOwningClass($fieldName)
{
if ($this->_inheritanceType == self::INHERITANCE_TYPE_NONE) {
return $this;
} else {
foreach ($this->_parentClasses as $parentClass) {
if ( ! $this->_em->getClassMetadata($parentClass)->isInheritedField($fieldName)) {
return $parentClass;
}
}
}
throw new Doctrine_ClassMetadata_Exception("Unable to find defining class of field '$fieldName'.");
}
/** /**
* Checks whether the class has any persistent subclasses. * Checks whether the class has any persistent subclasses.
* *
@ -1259,9 +1136,9 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
*/ */
public function getInheritanceOption($name) public function getInheritanceOption($name)
{ {
if ( ! array_key_exists($name, $this->_inheritanceOptions)) { /*if ( ! array_key_exists($name, $this->_inheritanceOptions)) {
throw new Doctrine_ClassMetadata_Exception("Unknown inheritance option: '$name'."); throw new Doctrine_ClassMetadata_Exception("Unknown inheritance option: '$name'.");
} }*/
return $this->_inheritanceOptions[$name]; return $this->_inheritanceOptions[$name];
} }
@ -1317,7 +1194,6 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
*/ */
public function export() public function export()
{ {
//$this->_em->export->exportTable($this);
} }
/** /**
@ -1329,7 +1205,7 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
*/ */
public function getExportableFormat($parseForeignKeys = true) public function getExportableFormat($parseForeignKeys = true)
{ {
$columns = array(); /*$columns = array();
$primary = array(); $primary = array();
$allColumns = $this->getColumns(); $allColumns = $this->getColumns();
@ -1338,13 +1214,13 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
if ($this->_inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE) { if ($this->_inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE) {
$parents = $this->getParentClasses(); $parents = $this->getParentClasses();
if ($parents) { if ($parents) {
$rootClass = $this->_em->getClassMetadata(array_pop($parents)); $rootClass = $this->_classFactory->getClassMetadata(array_pop($parents));
} else { } else {
$rootClass = $this; $rootClass = $this;
} }
$subClasses = $rootClass->getSubclasses(); $subClasses = $rootClass->getSubclasses();
foreach ($subClasses as $subClass) { foreach ($subClasses as $subClass) {
$subClassMetadata = $this->_em->getClassMetadata($subClass); $subClassMetadata = $this->_classFactory->getClassMetadata($subClass);
$allColumns = array_merge($allColumns, $subClassMetadata->getColumns()); $allColumns = array_merge($allColumns, $subClassMetadata->getColumns());
} }
} else if ($this->_inheritanceType == self::INHERITANCE_TYPE_JOINED) { } else if ($this->_inheritanceType == self::INHERITANCE_TYPE_JOINED) {
@ -1440,6 +1316,8 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
return array('tableName' => $this->getTableOption('tableName'), return array('tableName' => $this->getTableOption('tableName'),
'columns' => $columns, 'columns' => $columns,
'options' => array_merge($options, $this->getTableOptions())); 'options' => array_merge($options, $this->getTableOptions()));
*/
} }
/** /**
@ -1545,23 +1423,6 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
return $mapping; return $mapping;
} }
/**
* Registers any custom accessors/mutators in the given association mapping in
* an internal cache for fast lookup.
*
* @param Doctrine_Association $assoc
* @param unknown_type $fieldName
*/
private function _registerCustomAssociationAccessors(Doctrine_ORM_Mapping_AssociationMapping $assoc, $fieldName)
{
if ($acc = $assoc->getCustomAccessor()) {
$this->_customAssociationAccessors[$fieldName] = $acc;
}
if ($mut = $assoc->getCustomMutator()) {
$this->_customAssociationMutators[$fieldName] = $mut;
}
}
/** /**
* Adds a one-to-one mapping. * Adds a one-to-one mapping.
* *
@ -1572,17 +1433,15 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
$mapping = $this->_completeAssociationMapping($mapping); $mapping = $this->_completeAssociationMapping($mapping);
$oneToOneMapping = new Doctrine_ORM_Mapping_OneToOneMapping($mapping); $oneToOneMapping = new Doctrine_ORM_Mapping_OneToOneMapping($mapping);
$this->_storeAssociationMapping($oneToOneMapping); $this->_storeAssociationMapping($oneToOneMapping);
/*if ($oneToOneMapping->isInverseSide()) {
//FIXME: infinite recursion possible?
// Alternative: Store inverse side mappings indexed by mappedBy fieldname
// ($this->_inverseMappings). Then look it up.
$owningClass = $this->_em->getClassMetadata($oneToOneMapping->getTargetEntityName());
$owningClass->getAssociationMapping($oneToOneMapping->getMappedByFieldName())
->setBidirectional($oneToOneMapping->getSourceFieldName());
}*/
} }
/**
* Registers the mapping as an inverse mapping, if it is a mapping on the
* inverse side of an association mapping.
*
* @param AssociationMapping The mapping to register as inverse if it is a mapping
* for the inverse side of an association.
*/
private function _registerMappingIfInverse(Doctrine_ORM_Mapping_AssociationMapping $assoc) private function _registerMappingIfInverse(Doctrine_ORM_Mapping_AssociationMapping $assoc)
{ {
if ($assoc->isInverseSide()) { if ($assoc->isInverseSide()) {
@ -1637,7 +1496,6 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
throw Doctrine_MappingException::duplicateFieldMapping(); throw Doctrine_MappingException::duplicateFieldMapping();
} }
$this->_associationMappings[$sourceFieldName] = $assocMapping; $this->_associationMappings[$sourceFieldName] = $assocMapping;
$this->_registerCustomAssociationAccessors($assocMapping, $sourceFieldName);
$this->_registerMappingIfInverse($assocMapping); $this->_registerMappingIfInverse($assocMapping);
} }
@ -1666,19 +1524,6 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
return $this->_customRepositoryClassName; return $this->_customRepositoryClassName;
} }
/**
* Gets the Id generator used by the class.
*
* @return Doctrine::ORM::Id::AbstractIdGenerator
*/
public function getIdGenerator()
{
if (is_null($this->_idGenerator)) {
$this->_createIdGenerator();
}
return $this->_idGenerator;
}
/** /**
* @todo Thoughts & Implementation. * @todo Thoughts & Implementation.
*/ */
@ -1746,7 +1591,7 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
} }
/** /**
* Adds a lifecycle listener for Entities this class. * Adds a lifecycle listener for Entities of this class.
* *
* Note: If the same listener class is registered more than once, the old * Note: If the same listener class is registered more than once, the old
* one will be overridden. * one will be overridden.
@ -1781,33 +1626,6 @@ class Doctrine_ORM_Mapping_ClassMetadata implements Doctrine_Common_Configurable
} }
} }
/**
* INTERNAL: Completes the identifier mapping of the class.
* NOTE: Should only be called by the ClassMetadataFactory!
*
* @return void
*/
public function completeIdentifierMapping()
{
if ($this->_generatorType == self::GENERATOR_TYPE_AUTO) {
$platform = $this->_em->getConnection()->getDatabasePlatform();
if ($platform === null) {
try {
throw new Exception();
} catch (Exception $e) {
echo $e->getTraceAsString();
}
}
if ($platform->prefersSequences()) {
$this->_generatorType = self::GENERATOR_TYPE_SEQUENCE;
} else if ($platform->prefersIdentityColumns()) {
$this->_generatorType = self::GENERATOR_TYPE_IDENTITY;
} else {
$this->_generatorType = self::GENERATOR_TYPE_TABLE;
}
}
}
/** /**
* @todo Implementation. Immutable entities can not be updated or deleted once * @todo Implementation. Immutable entities can not be updated or deleted once
* they are created. This means the entity can only be modified as long as it's * they are created. This means the entity can only be modified as long as it's

View File

@ -19,11 +19,14 @@
* <http://www.phpdoctrine.org>. * <http://www.phpdoctrine.org>.
*/ */
#namespace Doctrine::ORM::Internal; #namespace Doctrine\ORM\Mapping;
#use Doctrine\DBAL\Platforms\AbstractPlatform;
/** /**
* The metadata factory is used to create ClassMetadata objects that contain all the * The metadata factory is used to create ClassMetadata objects that contain all the
* metadata mapping informations of a class. * metadata mapping informations of a class which describes how a class should be mapped
* to a relational database.
* *
* @author Konsta Vesterinen <kvesteri@cc.hut.fi> * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
@ -33,44 +36,21 @@
* @since 2.0 * @since 2.0
* @todo Rename to ClassDescriptorFactory. * @todo Rename to ClassDescriptorFactory.
*/ */
class Doctrine_ORM_Mapping_ClassMetadataFactory class Doctrine_ORM_Mapping_ClassMetadataFactory extends Doctrine_Common_ClassMetadataFactory
{ {
protected $_em; /** The targeted database platform. */
protected $_driver; private $_targetPlatform;
/**
* The already loaded metadata objects.
*/
protected $_loadedMetadata = array();
/** /**
* Constructor. * Constructor.
* Creates a new factory instance that uses the given EntityManager and metadata driver * Creates a new factory instance that uses the given metadata driver implementation.
* implementations.
* *
* @param $conn The connection to use.
* @param $driver The metadata driver to use. * @param $driver The metadata driver to use.
*/ */
public function __construct(Doctrine_ORM_EntityManager $em, $driver) public function __construct($driver, Doctrine_DBAL_Platforms_AbstractPlatform $targetPlatform)
{ {
$this->_em = $em; parent::__construct($driver);
$this->_driver = $driver; $this->_targetPlatform = $targetPlatform;
}
/**
* Returns the metadata object for a class.
*
* @param string $className The name of the class.
* @return Doctrine_Metadata
*/
public function getMetadataFor($className)
{
if (isset($this->_loadedMetadata[$className])) {
return $this->_loadedMetadata[$className];
}
$this->_loadClasses($className, $this->_loadedMetadata);
return $this->_loadedMetadata[$className];
} }
/** /**
@ -79,17 +59,16 @@ class Doctrine_ORM_Mapping_ClassMetadataFactory
* *
* @param string $name The name of the class for which the metadata should get loaded. * @param string $name The name of the class for which the metadata should get loaded.
* @param array $tables The metadata collection to which the loaded metadata is added. * @param array $tables The metadata collection to which the loaded metadata is added.
* @override
*/ */
protected function _loadClasses($name, array &$classes) protected function _loadMetadata($name)
{ {
$parentClass = $name; $parentClass = $name;
$parentClasses = array(); $parentClasses = array();
$loadedParentClass = false; $loadedParentClass = false;
while ($parentClass = get_parent_class($parentClass)) { while ($parentClass = get_parent_class($parentClass)) {
if ($parentClass == 'Doctrine_ORM_Entity') { if (isset($this->_loadedMetadata[$parentClass])) {
break;
}
if (isset($classes[$parentClass])) {
$loadedParentClass = $parentClass; $loadedParentClass = $parentClass;
break; break;
} }
@ -99,12 +78,12 @@ class Doctrine_ORM_Mapping_ClassMetadataFactory
$parentClasses[] = $name; $parentClasses[] = $name;
if ($loadedParentClass) { if ($loadedParentClass) {
$class = $classes[$loadedParentClass]; $class = $this->_loadedMetadata[$loadedParentClass];
} else { } else {
$rootClassOfHierarchy = count($parentClasses) > 0 ? array_shift($parentClasses) : $name; $rootClassOfHierarchy = count($parentClasses) > 0 ? array_shift($parentClasses) : $name;
$class = new Doctrine_ORM_Mapping_ClassMetadata($rootClassOfHierarchy, $this->_em); $class = new Doctrine_ORM_Mapping_ClassMetadata($rootClassOfHierarchy);
$this->_loadMetadata($class, $rootClassOfHierarchy); $this->_loadClassMetadata($class, $rootClassOfHierarchy);
$classes[$rootClassOfHierarchy] = $class; $this->_loadedMetadata[$rootClassOfHierarchy] = $class;
} }
if (count($parentClasses) == 0) { if (count($parentClasses) == 0) {
@ -117,15 +96,15 @@ class Doctrine_ORM_Mapping_ClassMetadataFactory
// Move down the hierarchy of parent classes, starting from the topmost class // Move down the hierarchy of parent classes, starting from the topmost class
$parent = $class; $parent = $class;
foreach ($parentClasses as $subclassName) { foreach ($parentClasses as $subclassName) {
$subClass = new Doctrine_ORM_Mapping_ClassMetadata($subclassName, $this->_em); $subClass = new Doctrine_ORM_Mapping_ClassMetadata($subclassName);
$subClass->setInheritanceType($parent->getInheritanceType(), $parent->getInheritanceOptions()); $subClass->setInheritanceType($parent->getInheritanceType(), $parent->getInheritanceOptions());
$this->_addInheritedFields($subClass, $parent); $this->_addInheritedFields($subClass, $parent);
$this->_addInheritedRelations($subClass, $parent); $this->_addInheritedRelations($subClass, $parent);
$this->_loadMetadata($subClass, $subclassName); $this->_loadClassMetadata($subClass, $subclassName);
if ($parent->isInheritanceTypeSingleTable()) { if ($parent->isInheritanceTypeSingleTable()) {
$subClass->setTableName($parent->getTableName()); $subClass->setTableName($parent->getTableName());
} }
$classes[$subclassName] = $subClass; $this->_loadedMetadata[$subclassName] = $subClass;
$parent = $subClass; $parent = $subClass;
} }
} }
@ -137,7 +116,7 @@ class Doctrine_ORM_Mapping_ClassMetadataFactory
* @param Doctrine::ORM::Mapping::ClassMetadata $parentClass * @param Doctrine::ORM::Mapping::ClassMetadata $parentClass
* @return void * @return void
*/ */
protected function _addInheritedFields($subClass, $parentClass) private function _addInheritedFields($subClass, $parentClass)
{ {
foreach ($parentClass->getFieldMappings() as $fieldName => $mapping) { foreach ($parentClass->getFieldMappings() as $fieldName => $mapping) {
if ( ! isset($mapping['inherited'])) { if ( ! isset($mapping['inherited'])) {
@ -153,7 +132,7 @@ class Doctrine_ORM_Mapping_ClassMetadataFactory
* @param unknown_type $subClass * @param unknown_type $subClass
* @param unknown_type $parentClass * @param unknown_type $parentClass
*/ */
protected function _addInheritedRelations($subClass, $parentClass) private function _addInheritedRelations($subClass, $parentClass)
{ {
foreach ($parentClass->getAssociationMappings() as $fieldName => $mapping) { foreach ($parentClass->getAssociationMappings() as $fieldName => $mapping) {
$subClass->addAssociationMapping($name, $mapping); $subClass->addAssociationMapping($name, $mapping);
@ -166,7 +145,7 @@ class Doctrine_ORM_Mapping_ClassMetadataFactory
* @param Doctrine_ClassMetadata $class The container for the metadata. * @param Doctrine_ClassMetadata $class The container for the metadata.
* @param string $name The name of the class for which the metadata will be loaded. * @param string $name The name of the class for which the metadata will be loaded.
*/ */
protected function _loadMetadata(Doctrine_ORM_Mapping_ClassMetadata $class, $name) private function _loadClassMetadata(Doctrine_ORM_Mapping_ClassMetadata $class, $name)
{ {
if ( ! class_exists($name) || empty($name)) { if ( ! class_exists($name) || empty($name)) {
throw new Doctrine_Exception("Couldn't find class " . $name . "."); throw new Doctrine_Exception("Couldn't find class " . $name . ".");
@ -177,17 +156,15 @@ class Doctrine_ORM_Mapping_ClassMetadataFactory
// get parent classes // get parent classes
//TODO: Skip Entity types MappedSuperclass/Transient //TODO: Skip Entity types MappedSuperclass/Transient
do { do {
if ($className === 'Doctrine_ORM_Entity') { if ($className == $name) {
break;
} else if ($className == $name) {
continue; continue;
} }
$names[] = $className; $names[] = $className;
} while ($className = get_parent_class($className)); } while ($className = get_parent_class($className));
if ($className === false) { /*if ($className === false) {
throw new Doctrine_ClassMetadata_Factory_Exception("Unknown component '$className'."); throw new Doctrine_Exception("Unknown component '$className'.");
} }*/
// save parents // save parents
$class->setParentClasses($names); $class->setParentClasses($names);
@ -201,11 +178,20 @@ class Doctrine_ORM_Mapping_ClassMetadataFactory
$class->setTableName(Doctrine::tableize($class->getClassName())); $class->setTableName(Doctrine::tableize($class->getClassName()));
} }
$class->completeIdentifierMapping(); // Complete Id generator mapping. If AUTO is specified we choose the generator
// most appropriate for the target platform.
if ($class->getIdGeneratorType() == Doctrine_ORM_Mapping_ClassMetadata::GENERATOR_TYPE_AUTO) {
if ($this->_targetPlatform->prefersSequences()) {
$class->setIdGeneratorType(Doctrine_ORM_Mapping_ClassMetadata::GENERATOR_TYPE_SEQUENCE);
} else if ($this->_targetPlatform->prefersIdentityColumns()) {
$class->setIdGeneratorType(Doctrine_ORM_Mapping_ClassMetadata::GENERATOR_TYPE_IDENTITY);
} else {
$class->setIdGeneratorType(Doctrine_ORM_Mapping_ClassMetadata::GENERATOR_TYPE_TABLE);
}
}
return $class; return $class;
} }
} }

View File

@ -19,7 +19,7 @@
* <http://www.phpdoctrine.org>. * <http://www.phpdoctrine.org>.
*/ */
#namespace Doctrine::ORM::Internal; #namespace Doctrine\ORM\Mapping\Driver;
/** /**
* The code metadata driver loads the metadata of the classes through invoking * The code metadata driver loads the metadata of the classes through invoking
@ -39,17 +39,27 @@ class Doctrine_ORM_Mapping_Driver_CodeDriver
* *
* @todo We could make the name of the callback methods customizable for users. * @todo We could make the name of the callback methods customizable for users.
*/ */
const CALLBACK_METHOD = 'initMetadata'; private $_callback = 'initMetadata';
public function setCallback($callback)
{
$this->_callback = $callback;
}
public function getCallback()
{
return $this->_callback;
}
/** /**
* Loads the metadata for the specified class into the provided container. * Loads the metadata for the specified class into the provided container.
*/ */
public function loadMetadataForClass($className, Doctrine_ORM_Mapping_ClassMetadata $metadata) public function loadMetadataForClass($className, Doctrine_ORM_Mapping_ClassMetadata $metadata)
{ {
if ( ! method_exists($className, self::CALLBACK_METHOD)) { if ( ! method_exists($className, $this->_callback)) {
throw new Doctrine_ClassMetadata_Exception("Unable to load metadata for class" throw new Doctrine_Exception("Unable to load metadata for class"
. " '$className'. Callback method 'initMetadata' not found."); . " '$className'. Callback method 'initMetadata' not found.");
} }
call_user_func_array(array($className, 'initMetadata'), array($metadata)); call_user_func_array(array($className, $this->_callback), array($metadata));
} }
} }

View File

@ -19,7 +19,7 @@
* <http://www.phpdoctrine.org>. * <http://www.phpdoctrine.org>.
*/ */
#namespace Doctrine::ORM::Persisters; #namespace Doctrine\ORM\Persisters;
/** /**
* Base class for all EntityPersisters. * Base class for all EntityPersisters.
@ -89,60 +89,13 @@ abstract class Doctrine_ORM_Persisters_AbstractEntityPersister
* @param Doctrine::ORM::Entity $entity The entity to insert. * @param Doctrine::ORM::Entity $entity The entity to insert.
* @return void * @return void
*/ */
public function insert(Doctrine_ORM_Entity $entity) public function insert($entity)
{ {
$insertData = array(); $insertData = array();
$class = $entity->getClass();
$referenceChangeSet = $entity->_getReferenceChangeSet();
foreach ($referenceChangeSet as $field => $change) {
list($old, $new) = each($change);
$assocMapping = $class->getAssociationMapping($field);
if ( ! $assocMapping->isOneToOne() || $assocMapping->isInverseSide()) {
//echo "NOT TO-ONE OR INVERSE!";
continue;
}
foreach ($assocMapping->getSourceToTargetKeyColumns() as $sourceColumn => $targetColumn) {
//TODO: What if both join columns (local/foreign) are just db-only
// columns (no fields in models) ? Currently we assume the foreign column
// is mapped to a field in the foreign entity.
//TODO: throw exc if field not set
$insertData[$sourceColumn] = $new->_internalGetField(
$new->getClass()->getFieldName($targetColumn)
);
}
//...
}
$this->_prepareData($entity, $insertData, true); $this->_prepareData($entity, $insertData, true);
$this->_conn->insert($this->_classMetadata->getTableName(), $insertData);
//TODO: perform insert
$this->_conn->insert($class->getTableName(), $insertData);
} }
/*protected function _fillJoinColumns($entity, array &$data)
{
$referenceChangeSet = $entity->_getReferenceChangeSet();
foreach ($referenceChangeSet as $field => $change) {
list($old, $new) = each($change);
$assocMapping = $entity->getClass()->getAssociationMapping($field);
if ( ! $assocMapping->isOneToOne() || $assocMapping->isInverseSide()) {
//echo "NOT TO-ONE OR INVERSE!";
continue;
}
foreach ($assocMapping->getSourceToTargetKeyColumns() as $sourceColumn => $targetColumn) {
//TODO: What if both join columns (local/foreign) are just db-only
// columns (no fields in models) ? Currently we assume the foreign column
// is mapped to a field in the foreign entity.
$insertData[$sourceColumn] = $new->_internalGetField(
$new->getClass()->getFieldName($targetColumn)
);
}
}
}*/
/** /**
* Updates an entity. * Updates an entity.
* *
@ -231,11 +184,27 @@ abstract class Doctrine_ORM_Persisters_AbstractEntityPersister
} }
/** /**
* @todo Move to ClassMetadata? * Gets the name of the class in the entity hierarchy that owns the field with
* the given name. The owning class is the one that defines the field.
*
* @param string $fieldName
* @return string
* @todo Consider using 'inherited' => 'ClassName' to make the lookup simpler.
*/ */
public function getOwningClass($fieldName) public function getOwningClass($fieldName)
{ {
if ($this->_classMetadata->isInheritanceTypeNone()) {
return $this->_classMetadata; return $this->_classMetadata;
} else {
foreach ($this->_classMetadata->getParentClasses() as $parentClass) {
$parentClassMetadata = Doctrine_ORM_Mapping_ClassMetadataFactory::getInstance()
->getMetadataFor($parentClass);
if ( ! $parentClassMetadata->isInheritedField($fieldName)) {
return $parentClassMetadata;
}
}
}
throw new Doctrine_Exception("Unable to find defining class of field '$fieldName'.");
} }
/** /**
@ -278,20 +247,33 @@ abstract class Doctrine_ORM_Persisters_AbstractEntityPersister
* *
* @param array $array * @param array $array
* @return void * @return void
* @todo Move to EntityPersister. There call _getChangeSet() and apply this logic.
*/ */
protected function _prepareData($entity, array &$result, $isInsert = false) protected function _prepareData($entity, array &$result, $isInsert = false)
{ {
foreach ($entity->_getDataChangeSet() as $field => $change) { foreach ($this->_em->getUnitOfWork()->getDataChangeSet($entity) as $field => $change) {
list ($oldVal, $newVal) = each($change); list ($oldVal, $newVal) = each($change);
$type = $entity->getClass()->getTypeOfField($field); $type = $this->_classMetadata->getTypeOfField($field);
$columnName = $entity->getClass()->getColumnName($field); $columnName = $this->_classMetadata->getColumnName($field);
if ($newVal === Doctrine_ORM_Internal_Null::$INSTANCE) { if ($newVal === Doctrine_ORM_Internal_Null::$INSTANCE) {
$result[$columnName] = null; $result[$columnName] = null;
} else if (is_object($newVal)) {
$assocMapping = $this->_classMetadata->getAssociationMapping($field);
if ( ! $assocMapping->isOneToOne() || $assocMapping->isInverseSide()) {
//echo "NOT TO-ONE OR INVERSE!";
continue; continue;
} }
foreach ($assocMapping->getSourceToTargetKeyColumns() as $sourceColumn => $targetColumn) {
//TODO: What if both join columns (local/foreign) are just db-only
// columns (no fields in models) ? Currently we assume the foreign column
// is mapped to a field in the foreign entity.
//TODO: throw exc if field not set
$otherClass = $this->_em->getClassMetadata($assocMapping->getTargetEntityName());
$result[$sourceColumn] = $otherClass->getReflectionProperty(
$otherClass->getFieldName($targetColumn))->getValue($newVal);
}
} else {
switch ($type) { switch ($type) {
case 'array': case 'array':
case 'object': case 'object':
@ -306,16 +288,16 @@ abstract class Doctrine_ORM_Persisters_AbstractEntityPersister
default: default:
$result[$columnName] = $newVal; $result[$columnName] = $newVal;
} }
}
/*$result[$columnName] = $type->convertToDatabaseValue( /*$result[$columnName] = $type->convertToDatabaseValue(
$newVal, $this->_em->getConnection()->getDatabasePlatform());*/ $newVal, $this->_em->getConnection()->getDatabasePlatform());*/
} }
// @todo Cleanup
// populates the discriminator column on insert in Single & Class Table Inheritance // populates the discriminator column on insert in Single & Class Table Inheritance
if ($isInsert && ($entity->getClass()->isInheritanceTypeJoined() || if ($isInsert && ($this->_classMetadata->isInheritanceTypeJoined() ||
$entity->getClass()->isInheritanceTypeSingleTable())) { $this->_classMetadata->isInheritanceTypeSingleTable())) {
$discColumn = $entity->getClass()->getInheritanceOption('discriminatorColumn'); $discColumn = $this->_classMetadata->getInheritanceOption('discriminatorColumn');
$discMap = $entity->getClass()->getInheritanceOption('discriminatorMap'); $discMap = $this->_classMetadata->getInheritanceOption('discriminatorMap');
$result[$discColumn] = array_search($this->_entityName, $discMap); $result[$discColumn] = array_search($this->_entityName, $discMap);
} }
} }
@ -597,14 +579,14 @@ abstract class Doctrine_ORM_Persisters_AbstractEntityPersister
*/ */
protected function _insert(Doctrine_ORM_Entity $record) protected function _insert(Doctrine_ORM_Entity $record)
{ {
$record->preInsert(); //$record->preInsert();
$this->notifyEntityListeners($record, 'preInsert', Doctrine_Event::RECORD_INSERT); //$this->notifyEntityListeners($record, 'preInsert', Doctrine_Event::RECORD_INSERT);
$this->_doInsert($record); $this->_doInsert($record);
$this->addRecord($record); $this->addRecord($record);
$record->postInsert(); //$record->postInsert();
$this->notifyEntityListeners($record, 'postInsert', Doctrine_Event::RECORD_INSERT); //$this->notifyEntityListeners($record, 'postInsert', Doctrine_Event::RECORD_INSERT);
return true; return true;
} }

View File

@ -219,7 +219,7 @@ class Doctrine_ORM_Persisters_JoinedSubclassPersister extends Doctrine_ORM_Persi
* *
* @todo Looks like this better belongs into the ClassMetadata class. * @todo Looks like this better belongs into the ClassMetadata class.
*/ */
public function getOwningClass($fieldName) /*public function getOwningClass($fieldName)
{ {
$conn = $this->_conn; $conn = $this->_conn;
$classMetadata = $this->_classMetadata; $classMetadata = $this->_classMetadata;
@ -242,7 +242,7 @@ class Doctrine_ORM_Persisters_JoinedSubclassPersister extends Doctrine_ORM_Persi
} }
throw new Doctrine_Mapper_Exception("Unable to find defining class of field '$fieldName'."); throw new Doctrine_Mapper_Exception("Unable to find defining class of field '$fieldName'.");
} }*/
/** /**
* Analyzes the fields of the entity and creates a map in which the field names * Analyzes the fields of the entity and creates a map in which the field names

View File

@ -19,6 +19,10 @@
* <http://www.phpdoctrine.org>. * <http://www.phpdoctrine.org>.
*/ */
#namespace Doctrine\ORM\Persisters;
#use Doctrine\ORM\Entity;
/** /**
* The default persister strategy maps a single entity instance to a single database table, * The default persister strategy maps a single entity instance to a single database table,
* as is the case in Single Table Inheritance & Concrete Table Inheritance. * as is the case in Single Table Inheritance & Concrete Table Inheritance.

View File

@ -33,6 +33,7 @@
* @version $Revision: 3938 $ * @version $Revision: 3938 $
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Konsta Vesterinen <kvesteri@cc.hut.fi> * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
*/ */
class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
{ {
@ -74,9 +75,11 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
protected $_parserResult; protected $_parserResult;
/** /**
* @var string $_sql Cached SQL query. * A set of query hints.
*
* @var array
*/ */
protected $_sql; protected $_hints = array();
// Caching Stuff // Caching Stuff
@ -114,7 +117,11 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
// End of Caching Stuff // End of Caching Stuff
/**
* Initializes a new instance of the Query class.
*
* @param EntityManager $entityManager
*/
public function __construct(Doctrine_ORM_EntityManager $entityManager) public function __construct(Doctrine_ORM_EntityManager $entityManager)
{ {
$this->_entityManager = $entityManager; $this->_entityManager = $entityManager;
@ -123,21 +130,8 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
$this->free(); $this->free();
} }
/** /**
* Returns a new Doctrine_ORM_Query object * Retrieves the assocated EntityManager to this Doctrine_ORM_Query
*
* @param Doctrine_Connection $conn optional connection parameter
* @return Doctrine_ORM_Query
*/
public static function create($conn = null)
{
return new self($conn);
}
/**
* Retrieves the assocated Doctrine_EntityManager to this Doctrine_ORM_Query
* *
* @return Doctrine_EntityManager * @return Doctrine_EntityManager
*/ */
@ -146,7 +140,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this->_entityManager; return $this->_entityManager;
} }
/** /**
* Returns the hydrator associated with this query object * Returns the hydrator associated with this query object
* *
@ -157,7 +150,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this->_hydrator; return $this->_hydrator;
} }
/** /**
* Convenience method to execute using array fetching as hydration mode. * Convenience method to execute using array fetching as hydration mode.
* *
@ -168,7 +160,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this->execute($params, self::HYDRATE_ARRAY); return $this->execute($params, self::HYDRATE_ARRAY);
} }
/** /**
* Convenience method to execute the query and return the first item * Convenience method to execute the query and return the first item
* of the collection. * of the collection.
@ -194,7 +185,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return false; return false;
} }
/** /**
* Query the database with DQL (Doctrine Query Language). * Query the database with DQL (Doctrine Query Language).
* *
@ -210,7 +200,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this->execute($params, $hydrationMode); return $this->execute($params, $hydrationMode);
} }
/** /**
* Builds the sql query from the given parameters and applies things such as * Builds the sql query from the given parameters and applies things such as
* column aggregation inheritance and limit subqueries if needed * column aggregation inheritance and limit subqueries if needed
@ -222,7 +211,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this->parse()->getSqlExecutor()->getSqlStatements(); return $this->parse()->getSqlExecutor()->getSqlStatements();
} }
/** /**
* Parses the DQL query, if necessary, and stores the parser result. * Parses the DQL query, if necessary, and stores the parser result.
* *
@ -239,7 +227,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this->_parserResult; return $this->_parserResult;
} }
/** /**
* Executes the query and populates the data set. * Executes the query and populates the data set.
* *
@ -278,7 +265,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this->_execute($params, $hydrationMode); return $this->_execute($params, $hydrationMode);
} }
/** /**
* _execute * _execute
* *
@ -304,7 +290,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this->_hydrator->hydrateResultSet($stmt, $hydrationMode); return $this->_hydrator->hydrateResultSet($stmt, $hydrationMode);
} }
/** /**
* _execute2 * _execute2
* *
@ -356,7 +341,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $executor->execute($this->_conn, $params); return $executor->execute($this->_conn, $params);
} }
/** /**
* @nodoc * @nodoc
*/ */
@ -369,7 +353,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this->convertEnums($params); return $this->convertEnums($params);
} }
/** /**
* Defines a cache driver to be used for caching result sets. * Defines a cache driver to be used for caching result sets.
* *
@ -389,7 +372,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this; return $this;
} }
/** /**
* Returns the cache driver used for caching result sets. * Returns the cache driver used for caching result sets.
* *
@ -404,7 +386,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
} }
} }
/** /**
* Defines how long the result cache will be active before expire. * Defines how long the result cache will be active before expire.
* *
@ -422,7 +403,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this; return $this;
} }
/** /**
* Retrieves the lifetime of resultset cache. * Retrieves the lifetime of resultset cache.
* *
@ -433,7 +413,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this->_resultCacheTTL; return $this->_resultCacheTTL;
} }
/** /**
* Defines if the resultset cache is active or not. * Defines if the resultset cache is active or not.
* *
@ -447,7 +426,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this; return $this;
} }
/** /**
* Retrieves if the resultset cache is active or not. * Retrieves if the resultset cache is active or not.
* *
@ -458,7 +436,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this->_expireResultCache; return $this->_expireResultCache;
} }
/** /**
* Defines a cache driver to be used for caching queries. * Defines a cache driver to be used for caching queries.
* *
@ -478,7 +455,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this; return $this;
} }
/** /**
* Returns the cache driver used for caching queries. * Returns the cache driver used for caching queries.
* *
@ -493,7 +469,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
} }
} }
/** /**
* Defines how long the query cache will be active before expire. * Defines how long the query cache will be active before expire.
* *
@ -511,7 +486,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this; return $this;
} }
/** /**
* Retrieves the lifetime of resultset cache. * Retrieves the lifetime of resultset cache.
* *
@ -522,7 +496,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this->_queryCacheTTL; return $this->_queryCacheTTL;
} }
/** /**
* Defines if the query cache is active or not. * Defines if the query cache is active or not.
* *
@ -536,7 +509,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this; return $this;
} }
/** /**
* Retrieves if the query cache is active or not. * Retrieves if the query cache is active or not.
* *
@ -547,7 +519,6 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return $this->_expireQueryCache; return $this->_expireQueryCache;
} }
/** /**
* Defines the processing mode to be used during hydration process. * Defines the processing mode to be used during hydration process.
* *
@ -620,6 +591,29 @@ class Doctrine_ORM_Query extends Doctrine_ORM_Query_Abstract
return is_array($result) ? array_shift($result) : $result->getFirst(); return is_array($result) ? array_shift($result) : $result->getFirst();
} }
/**
* Sets an implementation-specific hint. If the hint name is not recognized,
* it is silently ignored.
*
* @param string $name The name of the hint.
* @param mixed $value The value of the hint.
*/
public function setHint($name, $value)
{
$this->_hints[$name] = $value;
}
/**
* Gets an implementation-specific hint. If the hint name is not recognized,
* FALSE is returned.
*
* @param string $name The name of the hint.
*/
public function getHint($name)
{
return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
}
/** /**
* This method is automatically called when this Doctrine_Hydrate is serialized. * This method is automatically called when this Doctrine_Hydrate is serialized.
* *

View File

@ -51,7 +51,7 @@ class Doctrine_ORM_Query_Parser_AbstractSchemaName extends Doctrine_ORM_Query_Pa
// Check if we are dealing with a real Doctrine_Entity or not // Check if we are dealing with a real Doctrine_Entity or not
if ( ! $this->_isDoctrineEntity($componentName)) { if ( ! $this->_isDoctrineEntity($componentName)) {
$this->_parser->semanticalError( $this->_parser->semanticalError(
"Defined entity '" . $companyName . "' is not a valid Doctrine_Entity." "Defined entity '" . $componentName . "' is not a valid entity."
); );
} }
@ -62,6 +62,6 @@ class Doctrine_ORM_Query_Parser_AbstractSchemaName extends Doctrine_ORM_Query_Pa
protected function _isDoctrineEntity($componentName) protected function _isDoctrineEntity($componentName)
{ {
return class_exists($componentName) && is_subclass_of($componentName, 'Doctrine_ORM_Entity'); return class_exists($componentName)/* && is_subclass_of($componentName, 'Doctrine_ORM_Entity')*/;
} }
} }

View File

@ -19,11 +19,10 @@
* <http://www.phpdoctrine.org>. * <http://www.phpdoctrine.org>.
*/ */
#namespace Doctrine::ORM; #namespace Doctrine\ORM;
#use Doctrine::ORM::Entity; #use Doctrine\ORM\EntityManager;
#use Doctrine::ORM::EntityManager; #use Doctrine\ORM\Exceptions\UnitOfWorkException;
#use Doctrine::ORM::Exceptions::UnitOfWorkException;
/** /**
* The UnitOfWork is responsible for tracking changes to objects during an * The UnitOfWork is responsible for tracking changes to objects during an
@ -41,6 +40,45 @@
*/ */
class Doctrine_ORM_UnitOfWork class Doctrine_ORM_UnitOfWork
{ {
/**
* An Entity is in managed state when it has a primary key/identifier (and
* therefore persistent state) and is managed by an EntityManager
* (registered in the identity map).
* In MANAGED state the entity is associated with an EntityManager that manages
* the persistent state of the Entity.
*/
const STATE_MANAGED = 1;
/**
* An Entity is new if it does not yet have an identifier/primary key
* and is not (yet) managed by an EntityManager.
*/
const STATE_NEW = 2;
/**
* An Entity is temporarily locked during deletes and saves.
*
* This state is used internally to ensure that circular deletes
* and saves will not cause infinite loops.
* @todo Not sure this is a good idea. It is a problematic solution because
* it hides the original state while the locked state is active.
*/
const STATE_LOCKED = 6;
/**
* A detached Entity is an instance with a persistent identity that is not
* (or no longer) associated with an EntityManager (and a UnitOfWork).
* This means its no longer in the identity map.
*/
const STATE_DETACHED = 3;
/**
* A removed Entity instance is an instance with a persistent identity,
* associated with an EntityManager, whose persistent state has been
* deleted (or is scheduled for deletion).
*/
const STATE_DELETED = 4;
/** /**
* The identity map that holds references to all managed entities that have * The identity map that holds references to all managed entities that have
* an identity. The entities are grouped by their class name. * an identity. The entities are grouped by their class name.
@ -51,6 +89,31 @@ class Doctrine_ORM_UnitOfWork
*/ */
protected $_identityMap = array(); protected $_identityMap = array();
/**
* Map of the original entity data of entities fetched from the database.
* Keys are object ids. This is used for calculating changesets at commit time.
* Note that PHPs "copy-on-write" behavior helps a lot with the potentially
* high memory usage.
*
* @var array
*/
protected $_originalEntityData = array();
/**
* Map of data changes. Keys are object ids.
* Filled at the beginning of a commit() of the UnitOfWork and cleaned at the end.
*
* @var array
*/
protected $_dataChangeSets = array();
/**
* The states of entities in this UnitOfWork.
*
* @var array
*/
protected $_entityStates = array();
/** /**
* A list of all new entities that need to be INSERTed. * A list of all new entities that need to be INSERTed.
* *
@ -61,7 +124,7 @@ class Doctrine_ORM_UnitOfWork
protected $_newEntities = array(); protected $_newEntities = array();
/** /**
* A list of all dirty entities. * A list of all dirty entities that need to be UPDATEd.
* *
* @var array * @var array
* @todo Rename to _updates? * @todo Rename to _updates?
@ -102,7 +165,7 @@ class Doctrine_ORM_UnitOfWork
/** /**
* The EntityManager the UnitOfWork belongs to. * The EntityManager the UnitOfWork belongs to.
* *
* @var Doctrine::ORM::EntityManager * @var Doctrine\ORM\EntityManager
*/ */
protected $_em; protected $_em;
@ -110,7 +173,7 @@ class Doctrine_ORM_UnitOfWork
* The calculator used to calculate the order in which changes to * The calculator used to calculate the order in which changes to
* entities need to be written to the database. * entities need to be written to the database.
* *
* @var Doctrine::ORM::Internal::CommitOrderCalculator * @var Doctrine\ORM\Internal\CommitOrderCalculator
*/ */
protected $_commitOrderCalculator; protected $_commitOrderCalculator;
@ -118,7 +181,7 @@ class Doctrine_ORM_UnitOfWork
* Constructor. * Constructor.
* Creates a new UnitOfWork. * Creates a new UnitOfWork.
* *
* @param Doctrine_EntityManager $em * @param Doctrine\ORM\EntityManager $em
*/ */
public function __construct(Doctrine_ORM_EntityManager $em) public function __construct(Doctrine_ORM_EntityManager $em)
{ {
@ -131,21 +194,12 @@ class Doctrine_ORM_UnitOfWork
* Commits the unit of work, executing all operations that have been postponed * Commits the unit of work, executing all operations that have been postponed
* up to this point. * up to this point.
* *
* @todo Impl * @return void
*/ */
public function commit() public function commit()
{ {
// Detect changes in managed entities (mark dirty) // Compute changes in managed entities
//TODO: Consider using registerDirty() in Entity#_set() instead if its $this->_computeDataChangeSet();
// more performant (SEE THERE).
/*foreach ($this->_identityMap as $entities) {
foreach ($entities as $entity) {
if ($entity->_state() == Doctrine_Entity::STATE_MANAGED
&& $entity->isModified()) {
$this->registerDirty($entity);
}
}
}*/
if (empty($this->_newEntities) && if (empty($this->_newEntities) &&
empty($this->_deletedEntities) && empty($this->_deletedEntities) &&
@ -176,10 +230,88 @@ class Doctrine_ORM_UnitOfWork
//TODO: commit transaction here? //TODO: commit transaction here?
// clear lists // clear up
$this->_newEntities = array(); $this->_newEntities = array();
$this->_dirtyEntities = array(); $this->_dirtyEntities = array();
$this->_deletedEntities = array(); $this->_deletedEntities = array();
$this->_dataChangeSets = array();
}
/**
* Gets the data changeset for an entity.
*
* @return array
*/
public function getDataChangeSet($entity)
{
$oid = spl_object_id($entity);
if (isset($this->_dataChangeSets[$oid])) {
return $this->_dataChangeSets[$oid];
}
return array();
}
/**
* Computes all the changes that have been done to entities in the identity map
* and stores these changes in _dataChangeSet temporarily for access by the
* peristers, until the UoW commit is finished.
*
* @param array $entities The entities for which to compute the changesets. If this
* parameter is not specified, the changesets of all entities in the identity
* map are computed.
* @return void
*/
private function _computeDataChangeSet(array $entities = null)
{
$entitySet = array();
if ( ! is_null($entities)) {
foreach ($entities as $entity) {
$className = get_class($entity);
if ( ! isset($entitySet[$className])) {
$entitySet[$className] = array();
}
$entitySet[$className][] = $entity;
}
} else {
$entitySet = $this->_identityMap;
}
foreach ($entitySet as $className => $entities) {
$class = $this->_em->getClassMetadata($className);
foreach ($entities as $entity) {
$oid = spl_object_id($entity);
if ($this->getEntityState($entity) == self::STATE_MANAGED) {
if ( ! $class->isInheritanceTypeNone()) {
$class = $this->_em->getClassMetadata(get_class($entity));
}
$actualData = array();
foreach ($class->getReflectionProperties() as $name => $refProp) {
$actualData[$name] = $refProp->getValue($entity);
}
if ( ! isset($this->_originalEntityData[$oid])) {
$this->_dataChangeSets[$oid] = $actualData;
} else {
$originalData = $this->_originalEntityData[$oid];
$changeSet = array();
foreach ($actualData as $propName => $actualValue) {
$orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
if (is_object($orgValue) && $orgValue !== $actualValue) {
$changeSet[$propName] = array($orgValue => $actualValue);
} else if ($orgValue != $actualValue || (is_null($orgValue) xor is_null($actualValue))) {
$changeSet[$propName] = array($orgValue => $actualValue);
}
}
$this->_dirtyEntities[$oid] = $entity;
$this->_dataChangeSets[$oid] = $changeSet;
}
}
if (isset($this->_dirtyEntities[$oid])) {
$this->_originalEntityData[$oid] = $actualData;
}
}
}
} }
/** /**
@ -196,10 +328,12 @@ class Doctrine_ORM_UnitOfWork
$className = $class->getClassName(); $className = $class->getClassName();
$persister = $this->_em->getEntityPersister($className); $persister = $this->_em->getEntityPersister($className);
foreach ($this->_newEntities as $entity) { foreach ($this->_newEntities as $entity) {
if ($entity->getClass()->getClassName() == $className) { if (get_class($entity) == $className) {
$persister->insert($entity); $persister->insert($entity);
if ($class->isIdGeneratorIdentity()) { if ($class->isIdGeneratorIdentity()) {
$entity->_assignIdentifier($class->getIdGenerator()->getPostInsertId()); $id = $this->_em->getIdGenerator($class->getIdGeneratorType());
$class->setEntityIdentifier($entity, $id);
$this->_entityStates[spl_object_id($oid)] = self::STATE_MANAGED;
} }
} }
} }
@ -265,18 +399,20 @@ class Doctrine_ORM_UnitOfWork
// commit order graph yet (dont have a node). // commit order graph yet (dont have a node).
$newNodes = array(); $newNodes = array();
foreach ($entityChangeSet as $entity) { foreach ($entityChangeSet as $entity) {
if ( ! $this->_commitOrderCalculator->hasNodeWithKey($entity->getClass()->getClassName())) { $className = get_class($entity);
if ( ! $this->_commitOrderCalculator->hasNodeWithKey($className)) {
$this->_commitOrderCalculator->addNodeWithItem( $this->_commitOrderCalculator->addNodeWithItem(
$entity->getClass()->getClassName(), // index/key $className, // index/key
$entity->getClass() // item $this->_em->getClassMetadata($className) // item
); );
$newNodes[] = $this->_commitOrderCalculator->getNodeForKey($entity->getClass()->getClassName()); $newNodes[] = $this->_commitOrderCalculator->getNodeForKey($className);
} }
} }
// Calculate dependencies for new nodes // Calculate dependencies for new nodes
foreach ($newNodes as $node) { foreach ($newNodes as $node) {
foreach ($node->getClass()->getAssociationMappings() as $assocMapping) { $class = $node->getClass();
foreach ($class->getAssociationMappings() as $assocMapping) {
//TODO: should skip target classes that are not in the changeset. //TODO: should skip target classes that are not in the changeset.
if ($assocMapping->isOwningSide()) { if ($assocMapping->isOwningSide()) {
$targetClass = $this->_em->getClassMetadata($assocMapping->getTargetEntityName()); $targetClass = $this->_em->getClassMetadata($assocMapping->getTargetEntityName());
@ -303,9 +439,9 @@ class Doctrine_ORM_UnitOfWork
* *
* @todo Rename to scheduleForInsert(). * @todo Rename to scheduleForInsert().
*/ */
public function registerNew(Doctrine_ORM_Entity $entity) public function registerNew($entity)
{ {
$oid = $entity->getOid(); $oid = spl_object_id($entity);
/*if ( ! $entity->_identifier()) { /*if ( ! $entity->_identifier()) {
throw new Doctrine_Connection_Exception("Entity without identity cant be registered as new."); throw new Doctrine_Connection_Exception("Entity without identity cant be registered as new.");
@ -321,7 +457,7 @@ class Doctrine_ORM_UnitOfWork
} }
$this->_newEntities[$oid] = $entity; $this->_newEntities[$oid] = $entity;
if ($entity->_identifier()) { if ($this->_em->getClassMetadata(get_class($entity))->getEntityIdentifier($entity)) {
$this->addToIdentityMap($entity); $this->addToIdentityMap($entity);
} }
} }
@ -333,18 +469,18 @@ class Doctrine_ORM_UnitOfWork
* @return boolean * @return boolean
* @todo Rename to isScheduledForInsert(). * @todo Rename to isScheduledForInsert().
*/ */
public function isRegisteredNew(Doctrine_ORM_Entity $entity) public function isRegisteredNew($entity)
{ {
return isset($this->_newEntities[$entity->getOid()]); return isset($this->_newEntities[spl_object_id($entity)]);
} }
/** /**
* Registers a clean entity. * Registers a clean entity.
* The entity is simply put into the identity map. * The entity is simply put into the identity map.
* *
* @param Doctrine::ORM::Entity $entity * @param object $entity
*/ */
public function registerClean(Doctrine_ORM_Entity $entity) public function registerClean($entity)
{ {
$this->addToIdentityMap($entity); $this->addToIdentityMap($entity);
} }
@ -355,15 +491,15 @@ class Doctrine_ORM_UnitOfWork
* @param Doctrine::ORM::Entity $entity * @param Doctrine::ORM::Entity $entity
* @todo Rename to scheduleForUpdate(). * @todo Rename to scheduleForUpdate().
*/ */
public function registerDirty(Doctrine_ORM_Entity $entity) public function registerDirty($entity)
{ {
$oid = $entity->getOid(); $oid = spl_object_id($entity);
if ( ! $entity->_identifier()) { if ( ! $entity->_identifier()) {
throw new Doctrine_Connection_Exception("Entity without identity " throw new Doctrine_Exception("Entity without identity "
. "can't be registered as dirty."); . "can't be registered as dirty.");
} }
if (isset($this->_deletedEntities[$oid])) { if (isset($this->_deletedEntities[$oid])) {
throw new Doctrine_Connection_Exception("Removed object can't be registered as dirty."); throw new Doctrine_Exception("Removed object can't be registered as dirty.");
} }
if ( ! isset($this->_dirtyEntities[$oid]) && ! isset($this->_newEntities[$oid])) { if ( ! isset($this->_dirtyEntities[$oid]) && ! isset($this->_newEntities[$oid])) {
@ -380,9 +516,9 @@ class Doctrine_ORM_UnitOfWork
* @return boolean * @return boolean
* @todo Rename to isScheduledForUpdate(). * @todo Rename to isScheduledForUpdate().
*/ */
public function isRegisteredDirty(Doctrine_ORM_Entity $entity) public function isRegisteredDirty($entity)
{ {
return isset($this->_dirtyEntities[$entity->getOid()]); return isset($this->_dirtyEntities[spl_object_id($entity)]);
} }
/** /**
@ -390,9 +526,9 @@ class Doctrine_ORM_UnitOfWork
* *
* @todo Rename to scheduleForDelete(). * @todo Rename to scheduleForDelete().
*/ */
public function registerDeleted(Doctrine_ORM_Entity $entity) public function registerDeleted($entity)
{ {
$oid = $entity->getOid(); $oid = spl_object_id($entity);
if ( ! $this->isInIdentityMap($entity)) { if ( ! $this->isInIdentityMap($entity)) {
return; return;
} }
@ -420,9 +556,9 @@ class Doctrine_ORM_UnitOfWork
* @return boolean * @return boolean
* @todo Rename to isScheduledForDelete(). * @todo Rename to isScheduledForDelete().
*/ */
public function isRegisteredRemoved(Doctrine_ORM_Entity $entity) public function isRegisteredRemoved($entity)
{ {
return isset($this->_deletedEntities[$entity->getOid()]); return isset($this->_deletedEntities[spl_object_id($entity)]);
} }
/** /**
@ -432,7 +568,7 @@ class Doctrine_ORM_UnitOfWork
* @param integer $oid object identifier * @param integer $oid object identifier
* @return boolean whether ot not the operation was successful * @return boolean whether ot not the operation was successful
*/ */
public function detach(Doctrine_ORM_Entity $entity) public function detach($entity)
{ {
if ($this->isInIdentityMap($entity)) { if ($this->isInIdentityMap($entity)) {
$this->removeFromIdentityMap($entity); $this->removeFromIdentityMap($entity);
@ -446,9 +582,9 @@ class Doctrine_ORM_UnitOfWork
* @return unknown * @return unknown
* @todo Rename to isScheduled() * @todo Rename to isScheduled()
*/ */
public function isEntityRegistered(Doctrine_ORM_Entity $entity) public function isEntityRegistered($entity)
{ {
$oid = $entity->getOid(); $oid = spl_object_id($entity);
return isset($this->_newEntities[$oid]) || return isset($this->_newEntities[$oid]) ||
isset($this->_dirtyEntities[$oid]) || isset($this->_dirtyEntities[$oid]) ||
isset($this->_deletedEntities[$oid]) || isset($this->_deletedEntities[$oid]) ||
@ -484,40 +620,54 @@ class Doctrine_ORM_UnitOfWork
* Note that entities in a hierarchy are registered with the class name of * Note that entities in a hierarchy are registered with the class name of
* the root entity. * the root entity.
* *
* @param Doctrine::ORM::Entity $entity The entity to register. * @param Doctrine\ORM\Entity $entity The entity to register.
* @return boolean TRUE if the registration was successful, FALSE if the identity of * @return boolean TRUE if the registration was successful, FALSE if the identity of
* the entity in question is already managed. * the entity in question is already managed.
*/ */
public function addToIdentityMap(Doctrine_ORM_Entity $entity) public function addToIdentityMap($entity)
{ {
$idHash = $this->getIdentifierHash($entity->_identifier()); $classMetadata = $this->_em->getClassMetadata(get_class($entity));
$idHash = $this->getIdentifierHash($classMetadata->getEntityIdentifier($entity));
if ($idHash === '') { if ($idHash === '') {
throw new Doctrine_Connection_Exception("Entity with oid '" . $entity->getOid() throw new Doctrine_Exception("Entity with oid '" . spl_object_id($entity)
. "' has no identity and therefore can't be added to the identity map."); . "' has no identity and therefore can't be added to the identity map.");
} }
$className = $entity->getClass()->getRootClassName(); $className = $classMetadata->getRootClassName();
if (isset($this->_identityMap[$className][$idHash])) { if (isset($this->_identityMap[$className][$idHash])) {
return false; return false;
} }
$this->_identityMap[$className][$idHash] = $entity; $this->_identityMap[$className][$idHash] = $entity;
$entity->_state(Doctrine_ORM_Entity::STATE_MANAGED);
return true; return true;
} }
/**
* Gets the state of an entity within the current unit of work.
*
* @param Doctrine\ORM\Entity $entity
* @return int
*/
public function getEntityState($entity)
{
$oid = spl_object_id($entity);
return isset($this->_entityStates[$oid]) ? $this->_entityStates[$oid] :
self::STATE_NEW;
}
/** /**
* Removes an entity from the identity map. * Removes an entity from the identity map.
* *
* @param Doctrine_ORM_Entity $entity * @param Doctrine_ORM_Entity $entity
* @return unknown * @return unknown
*/ */
public function removeFromIdentityMap(Doctrine_ORM_Entity $entity) public function removeFromIdentityMap($entity)
{ {
$idHash = $this->getIdentifierHash($entity->_identifier()); $classMetadata = $this->_em->getClassMetadata(get_class($entity));
$idHash = $this->getIdentifierHash($classMetadata->getEntityIdentifier($entity));
if ($idHash === '') { if ($idHash === '') {
throw new Doctrine_Connection_Exception("Entity with oid '" . $entity->getOid() throw new Doctrine_Exception("Entity with oid '" . spl_object_id($entity)
. "' has no identity and therefore can't be removed from the identity map."); . "' has no identity and therefore can't be removed from the identity map.");
} }
$className = $entity->getClass()->getRootClassName(); $className = $classMetadata->getRootClassName();
if (isset($this->_identityMap[$className][$idHash])) { if (isset($this->_identityMap[$className][$idHash])) {
unset($this->_identityMap[$className][$idHash]); unset($this->_identityMap[$className][$idHash]);
return true; return true;
@ -537,6 +687,15 @@ class Doctrine_ORM_UnitOfWork
{ {
return $this->_identityMap[$rootClassName][$idHash]; return $this->_identityMap[$rootClassName][$idHash];
} }
/**
* Tries to get an entity by its identifier hash. If no entity is found for
* the given hash, FALSE is returned.
*
* @param <type> $idHash
* @param <type> $rootClassName
* @return mixed The found entity or FALSE.
*/
public function tryGetByIdHash($idHash, $rootClassName) public function tryGetByIdHash($idHash, $rootClassName)
{ {
if ($this->containsIdHash($idHash, $rootClassName)) { if ($this->containsIdHash($idHash, $rootClassName)) {
@ -565,18 +724,19 @@ class Doctrine_ORM_UnitOfWork
* Checks whether an entity is registered in the identity map of the * Checks whether an entity is registered in the identity map of the
* UnitOfWork. * UnitOfWork.
* *
* @param Doctrine_ORM_Entity $entity * @param object $entity
* @return boolean * @return boolean
*/ */
public function isInIdentityMap(Doctrine_ORM_Entity $entity) public function isInIdentityMap($entity)
{ {
$idHash = $this->getIdentifierHash($entity->_identifier()); $classMetadata = $this->_em->getClassMetadata(get_class($entity));
$idHash = $this->getIdentifierHash($classMetadata->getEntityIdentifier($entity));
if ($idHash === '') { if ($idHash === '') {
return false; return false;
} }
return isset($this->_identityMap return isset($this->_identityMap
[$entity->getClass()->getRootClassName()] [$classMetadata->getRootClassName()]
[$idHash] [$idHash]
); );
} }
@ -598,7 +758,7 @@ class Doctrine_ORM_UnitOfWork
* *
* @param Doctrine_ORM_Entity $entity The entity to save. * @param Doctrine_ORM_Entity $entity The entity to save.
*/ */
public function save(Doctrine_ORM_Entity $entity) public function save($entity)
{ {
$insertNow = array(); $insertNow = array();
$visited = array(); $visited = array();
@ -606,12 +766,14 @@ class Doctrine_ORM_UnitOfWork
if ( ! empty($insertNow)) { if ( ! empty($insertNow)) {
// We have no choice. This means that there are new entities // We have no choice. This means that there are new entities
// with an IDENTITY column key generation. // with an IDENTITY column key generation.
$this->_computeDataChangeSet($insertNow);
$commitOrder = $this->_getCommitOrder($insertNow); $commitOrder = $this->_getCommitOrder($insertNow);
foreach ($commitOrder as $class) { foreach ($commitOrder as $class) {
$this->_executeInserts($class); $this->_executeInserts($class);
} }
// remove them from _newEntities // remove them from _newEntities
$this->_newEntities = array_diff_key($this->_newEntities, $insertNow); $this->_newEntities = array_diff_key($this->_newEntities, $insertNow);
$this->_dataChangeSets = array();
} }
} }
@ -623,37 +785,39 @@ class Doctrine_ORM_UnitOfWork
* @param Doctrine_ORM_Entity $entity The entity to save. * @param Doctrine_ORM_Entity $entity The entity to save.
* @param array $visited The already visited entities. * @param array $visited The already visited entities.
*/ */
private function _doSave(Doctrine_ORM_Entity $entity, array &$visited, array &$insertNow) private function _doSave($entity, array &$visited, array &$insertNow)
{ {
if (isset($visited[$entity->getOid()])) { $oid = spl_object_id($entity);
if (isset($visited[$oid])) {
return; // Prevent infinite recursion return; // Prevent infinite recursion
} }
$visited[$entity->getOid()] = $entity; // mark visited $visited[$oid] = $entity; // mark visited
$class = $entity->getClass(); $class = $this->_em->getClassMetadata(get_class($entity));
switch ($entity->_state()) { switch ($this->getEntityState($entity)) {
case Doctrine_ORM_Entity::STATE_MANAGED: case self::STATE_MANAGED:
// nothing to do for $entity // nothing to do
break; break;
case Doctrine_ORM_Entity::STATE_NEW: case self::STATE_NEW:
$result = $class->getIdGenerator()->generate($entity); $result = $this->_em->getIdGenerator($class->getClassName())->generate($entity);
if ($result == Doctrine_ORM_Id_AbstractIdGenerator::POST_INSERT_INDICATOR) { if ($result == Doctrine_ORM_Id_AbstractIdGenerator::POST_INSERT_INDICATOR) {
$insertNow[$entity->getOid()] = $entity; $insertNow[$oid] = $entity;
} else { } else {
$entity->_assignIdentifier($result); $class->setEntityIdentifier($entity, $result);
$this->_entityStates[$oid] = self::STATE_MANAGED;
} }
$this->registerNew($entity); $this->registerNew($entity);
break; break;
case Doctrine_ORM_Entity::STATE_DETACHED: case self::STATE_DETACHED:
//exception? //exception?
throw new Doctrine_Exception("Behavior of save() for a detached entity " throw new Doctrine_Exception("Behavior of save() for a detached entity "
. "is not yet defined."); . "is not yet defined.");
case Doctrine_ORM_Entity::STATE_DELETED: case self::STATE_DELETED:
// $entity becomes managed again // $entity becomes managed again
if ($this->isRegisteredRemoved($entity)) { if ($this->isRegisteredRemoved($entity)) {
//TODO: better a method for this? //TODO: better a method for this?
unset($this->_deletedEntities[$entity->getOid()]); unset($this->_deletedEntities[$oid]);
} else { } else {
//FIXME: There's more to think of here... //FIXME: There's more to think of here...
$this->registerNew($entity); $this->registerNew($entity);
@ -672,7 +836,7 @@ class Doctrine_ORM_UnitOfWork
* *
* @param Doctrine_ORM_Entity $entity * @param Doctrine_ORM_Entity $entity
*/ */
public function delete(Doctrine_ORM_Entity $entity) public function delete($entity)
{ {
$this->_doDelete($entity, array()); $this->_doDelete($entity, array());
} }
@ -683,24 +847,25 @@ class Doctrine_ORM_UnitOfWork
* @param Doctrine_ORM_Entity $entity * @param Doctrine_ORM_Entity $entity
* @param array $visited * @param array $visited
*/ */
private function _doDelete(Doctrine_ORM_Entity $entity, array &$visited) private function _doDelete($entity, array &$visited)
{ {
if (isset($visited[$entity->getOid()])) { $oid = spl_object_id($entity);
if (isset($visited[$oid])) {
return; // Prevent infinite recursion return; // Prevent infinite recursion
} }
$visited[$entity->getOid()] = $entity; // mark visited $visited[$oid] = $entity; // mark visited
$class = $entity->getClass(); //$class = $entity->getClass();
switch ($entity->_state()) { switch ($this->getEntityState($entity)) {
case Doctrine_ORM_Entity::STATE_NEW: case self::STATE_NEW:
case Doctrine_ORM_Entity::STATE_DELETED: case self::STATE_DELETED:
// nothing to do for $entity // nothing to do for $entity
break; break;
case Doctrine_ORM_Entity::STATE_MANAGED: case self::STATE_MANAGED:
$this->registerDeleted($entity); $this->registerDeleted($entity);
break; break;
case Doctrine_ORM_Entity::STATE_DETACHED: case self::STATE_DETACHED:
//exception? //exception?
throw new Doctrine_Exception("A detached entity can't be deleted."); throw new Doctrine_Exception("A detached entity can't be deleted.");
default: default:
@ -714,28 +879,30 @@ class Doctrine_ORM_UnitOfWork
/** /**
* Cascades the save operation to associated entities. * Cascades the save operation to associated entities.
* *
* @param Doctrine_ORM_Entity $entity * @param Doctrine\ORM\Entity $entity
* @param array $visited * @param array $visited
*/ */
private function _cascadeSave(Doctrine_ORM_Entity $entity, array &$visited, array &$insertNow) private function _cascadeSave($entity, array &$visited, array &$insertNow)
{ {
foreach ($entity->getClass()->getAssociationMappings() as $assocMapping) { $class = $this->_em->getClassMetadata(get_class($entity));
foreach ($class->getAssociationMappings() as $assocMapping) {
if ( ! $assocMapping->isCascadeSave()) { if ( ! $assocMapping->isCascadeSave()) {
continue; continue;
} }
$relatedEntities = $entity->get($assocMapping->getSourceFieldName()); $relatedEntities = $class->getReflectionProperty($assocMapping->getSourceFieldName())
if ($relatedEntities instanceof Doctrine_ORM_Entity) { ->getValue($entity);
$this->_doSave($relatedEntities, $visited, $insertNow); if ($relatedEntities instanceof Doctrine_ORM_Collection &&
} else if ($relatedEntities instanceof Doctrine_Collection &&
count($relatedEntities) > 0) { count($relatedEntities) > 0) {
foreach ($relatedEntities as $relatedEntity) { foreach ($relatedEntities as $relatedEntity) {
$this->_doSave($relatedEntity, $visited, $insertNow); $this->_doSave($relatedEntity, $visited, $insertNow);
} }
} else if (is_object($relatedEntities)) {
$this->_doSave($relatedEntities, $visited, $insertNow);
} }
} }
} }
private function _cascadeDelete(Doctrine_ORM_Entity $entity) private function _cascadeDelete($entity)
{ {
} }
@ -783,192 +950,110 @@ class Doctrine_ORM_UnitOfWork
//... //...
} }
// Stuff from 0.11/1.0 that we will need later (need to modify it though)
/** /**
* Collects all records that need to be deleted by applying defined * Creates an entity. Used for reconstitution as well as initial creation.
* application-level delete cascades.
* *
* @param array $deletions Map of the records to delete. Keys=Oids Values=Records. * @param string $className The name of the entity class.
* @param array $data The data for the entity.
* @return Doctrine\ORM\Entity
*/ */
/*private function _collectDeletions(Doctrine_Record $record, array &$deletions) public function createEntity($className, array $data, Doctrine_Query $query = null)
{ {
if ( ! $record->exists()) { $className = $this->_inferCorrectClassName($data, $className);
return; $classMetadata = $this->_em->getClassMetadata($className);
if ( ! empty($data)) {
$identifierFieldNames = $classMetadata->getIdentifier();
$isNew = false;
foreach ($identifierFieldNames as $fieldName) {
if ( ! isset($data[$fieldName])) {
// id field not found return new entity
$isNew = true;
break;
}
$id[] = $data[$fieldName];
} }
$deletions[$record->getOid()] = $record; if ($isNew) {
$this->_cascadeDelete($record, $deletions); $entity = new $className;
}*/ } else {
$idHash = $this->getIdentifierHash($id);
$entity = $this->tryGetByIdHash($idHash, $classMetadata->getRootClassName());
if ($entity) {
$this->_mergeData($entity, $data, $classMetadata/*, $query->getHint('doctrine.refresh')*/);
return $entity;
} else {
$entity = new $className;
$this->_mergeData($entity, $data, $classMetadata, true);
$this->addToIdentityMap($entity);
}
}
} else {
$entity = new $className;
}
$this->_originalEntityData[spl_object_id($entity)] = $data;
return $entity;
}
/** /**
* Cascades an ongoing delete operation to related objects. Applies only on relations * Merges the given data into the given entity, optionally overriding
* that have 'delete' in their cascade options. * local changes.
* This is an application-level cascade. Related objects that participate in the
* cascade and are not yet loaded are fetched from the database.
* Exception: many-valued relations are always (re-)fetched from the database to
* make sure we have all of them.
* *
* @param Doctrine_Record The record for which the delete operation will be cascaded. * @param Doctrine\ORM\Entity $entity
* @throws PDOException If something went wrong at database level * @param array $data
* @param boolean $overrideLocalChanges
* @return void * @return void
*/ */
/*protected function _cascadeDelete(Doctrine_Record $record, array &$deletions) private function _mergeData($entity, array $data, $class, $overrideLocalChanges = false) {
{ if ($overrideLocalChanges) {
foreach ($record->getTable()->getRelations() as $relation) { foreach ($data as $field => $value) {
if ($relation->isCascadeDelete()) { $class->getReflectionProperty($field)->setValue($entity, $value);
$fieldName = $relation->getAlias();
// if it's a xToOne relation and the related object is already loaded
// we don't need to refresh.
if ( ! ($relation->getType() == Doctrine_Relation::ONE && isset($record->$fieldName))) {
$record->refreshRelated($relation->getAlias());
} }
$relatedObjects = $record->get($relation->getAlias());
if ($relatedObjects instanceof Doctrine_Record && $relatedObjects->exists()
&& ! isset($deletions[$relatedObjects->getOid()])) {
$this->_collectDeletions($relatedObjects, $deletions);
} else if ($relatedObjects instanceof Doctrine_Collection && count($relatedObjects) > 0) {
// cascade the delete to the other objects
foreach ($relatedObjects as $object) {
if ( ! isset($deletions[$object->getOid()])) {
$this->_collectDeletions($object, $deletions);
}
}
}
}
}
}*/
/**
* Executes the deletions for all collected records during a delete operation
* (usually triggered through $record->delete()).
*
* @param array $deletions Map of the records to delete. Keys=Oids Values=Records.
*/
/*private function _executeDeletions(array $deletions)
{
// collect class names
$classNames = array();
foreach ($deletions as $record) {
$classNames[] = $record->getTable()->getComponentName();
}
$classNames = array_unique($classNames);
// order deletes
$executionOrder = $this->buildFlushTree($classNames);
// execute
try {
$this->conn->beginInternalTransaction();
for ($i = count($executionOrder) - 1; $i >= 0; $i--) {
$className = $executionOrder[$i];
$table = $this->conn->getTable($className);
// collect identifiers
$identifierMaps = array();
$deletedRecords = array();
foreach ($deletions as $oid => $record) {
if ($record->getTable()->getComponentName() == $className) {
$veto = $this->_preDelete($record);
if ( ! $veto) {
$identifierMaps[] = $record->identifier();
$deletedRecords[] = $record;
unset($deletions[$oid]);
}
}
}
if (count($deletedRecords) < 1) {
continue;
}
// extract query parameters (only the identifier values are of interest)
$params = array();
$columnNames = array();
foreach ($identifierMaps as $idMap) {
while (list($fieldName, $value) = each($idMap)) {
$params[] = $value;
$columnNames[] = $table->getColumnName($fieldName);
}
}
$columnNames = array_unique($columnNames);
// delete
$tableName = $table->getTableName();
$sql = "DELETE FROM " . $this->conn->quoteIdentifier($tableName) . " WHERE ";
if ($table->isIdentifierComposite()) {
$sql .= $this->_buildSqlCompositeKeyCondition($columnNames, count($identifierMaps));
$this->conn->exec($sql, $params);
} else { } else {
$sql .= $this->_buildSqlSingleKeyCondition($columnNames, count($params)); $oid = spl_object_id($entity);
$this->conn->exec($sql, $params); foreach ($data as $field => $value) {
} $currentValue = $class->getReflectionProperty($field)->getValue($entity);
if ( ! isset($this->_originalEntityData[$oid]) ||
// adjust state, remove from identity map and inform postDelete listeners $currentValue == $this->_originalEntityData[$oid]) {
foreach ($deletedRecords as $record) { $class->getReflectionProperty($field)->setValue($entity, $value);
// currently just for bc!
$this->_deleteCTIParents($table, $record);
//--
$record->state(Doctrine_Record::STATE_TCLEAN);
$record->getTable()->removeRecord($record);
$this->_postDelete($record);
} }
} }
$this->conn->commit();
// trigger postDelete for records skipped during the deletion (veto!)
foreach ($deletions as $skippedRecord) {
$this->_postDelete($skippedRecord);
} }
return true;
} catch (Exception $e) {
$this->conn->rollback();
throw $e;
} }
}*/
/** /**
* Builds the SQL condition to target multiple records who have a single-column * Check the dataset for a discriminator column to determine the correct
* primary key. * class to instantiate. If no discriminator column is found, the given
* classname will be returned.
* *
* @param Doctrine_Table $table The table from which the records are going to be deleted. * @param array $data
* @param integer $numRecords The number of records that are going to be deleted. * @param string $className
* @return string The SQL condition "pk = ? OR pk = ? OR pk = ? ..." * @return string The name of the class to instantiate.
*/ */
/*private function _buildSqlSingleKeyCondition($columnNames, $numRecords) private function _inferCorrectClassName(array $data, $className)
{ {
$idColumn = $this->conn->quoteIdentifier($columnNames[0]); $class = $this->_em->getClassMetadata($className);
return implode(' OR ', array_fill(0, $numRecords, "$idColumn = ?"));
}*/
/** $discCol = $class->getInheritanceOption('discriminatorColumn');
* Builds the SQL condition to target multiple records who have a composite primary key. if ( ! $discCol) {
* return $className;
* @param Doctrine_Table $table The table from which the records are going to be deleted. }
* @param integer $numRecords The number of records that are going to be deleted.
* @return string The SQL condition "(pk1 = ? AND pk2 = ?) OR (pk1 = ? AND pk2 = ?) ..." $discMap = $class->getInheritanceOption('discriminatorMap');
*/
/*private function _buildSqlCompositeKeyCondition($columnNames, $numRecords) if (isset($data[$discCol], $discMap[$data[$discCol]])) {
{ return $discMap[$data[$discCol]];
$singleCondition = "";
foreach ($columnNames as $columnName) {
$columnName = $this->conn->quoteIdentifier($columnName);
if ($singleCondition === "") {
$singleCondition .= "($columnName = ?";
} else { } else {
$singleCondition .= " AND $columnName = ?"; return $className;
} }
} }
$singleCondition .= ")";
$fullCondition = implode(' OR ', array_fill(0, $numRecords, $singleCondition));
return $fullCondition;
}*/
/**
* Gets the identity map of the UnitOfWork.
*
* @return array
*/
public function getIdentityMap() public function getIdentityMap()
{ {
return $this->_identityMap; return $this->_identityMap;

View File

@ -18,8 +18,14 @@ class Orm_Entity_AccessorTest extends Doctrine_OrmTestCase
/* Local test classes */ /* Local test classes */
class CustomAccessorMutatorTestEntity extends Doctrine_ORM_Entity class CustomAccessorMutatorTestEntity extends Doctrine_Common_VirtualPropertyObject
{ {
static function construct() {
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'id', 'int');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__,
'username', 'string', 'getUsernameCustom', 'setUsernameCustom');
}
public static function initMetadata($mapping) public static function initMetadata($mapping)
{ {
$mapping->mapField(array( $mapping->mapField(array(
@ -31,9 +37,7 @@ class CustomAccessorMutatorTestEntity extends Doctrine_ORM_Entity
$mapping->mapField(array( $mapping->mapField(array(
'fieldName' => 'username', 'fieldName' => 'username',
'type' => 'string', 'type' => 'string',
'length' => 50, 'length' => 50
'accessor' => 'getUsernameCustom',
'mutator' => 'setUsernameCustom'
)); ));
} }
@ -48,8 +52,13 @@ class CustomAccessorMutatorTestEntity extends Doctrine_ORM_Entity
} }
} }
class MagicAccessorMutatorTestEntity extends Doctrine_ORM_Entity class MagicAccessorMutatorTestEntity extends Doctrine_Common_VirtualPropertyObject
{ {
static function construct() {
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'id', 'int');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'username', 'string');
}
public static function initMetadata($mapping) public static function initMetadata($mapping)
{ {
$mapping->mapField(array( $mapping->mapField(array(

View File

@ -20,7 +20,7 @@ class Orm_Entity_AllTests
{ {
$suite = new Doctrine_TestSuite('Doctrine Orm Entity Tests'); $suite = new Doctrine_TestSuite('Doctrine Orm Entity Tests');
$suite->addTestSuite('Orm_Entity_AccessorTest'); //$suite->addTestSuite('Orm_Entity_AccessorTest');
$suite->addTestSuite('Orm_Entity_ConstructorTest'); $suite->addTestSuite('Orm_Entity_ConstructorTest');
return $suite; return $suite;

View File

@ -6,17 +6,18 @@ class Orm_Entity_ConstructorTest extends Doctrine_OrmTestCase
public function testFieldInitializationInConstructor() public function testFieldInitializationInConstructor()
{ {
$entity = new ConstructorTestEntity1("romanb"); $entity = new ConstructorTestEntity1("romanb");
$this->assertTrue($entity->isNew());
$this->assertEquals("romanb", $entity->username); $this->assertEquals("romanb", $entity->username);
} }
} }
class ConstructorTestEntity1 extends Doctrine_ORM_Entity class ConstructorTestEntity1
{ {
public $id;
public $username;
public function __construct($username = null) public function __construct($username = null)
{ {
parent::__construct(); if ($username !== null) {
if ($this->isNew()) {
$this->username = $username; $this->username = $username;
} }
} }
@ -24,10 +25,16 @@ class ConstructorTestEntity1 extends Doctrine_ORM_Entity
/* The mapping definition */ /* The mapping definition */
public static function initMetadata($mapping) public static function initMetadata($mapping)
{ {
/*
$mapping->addFieldMetadata('id', array(
'doctrine.id' => true,
'doctrine.validator.constraints' => array('notnull', 'unique')
));
*/
$mapping->mapField(array( $mapping->mapField(array(
'fieldName' => 'id', 'fieldName' => 'id',
'type' => 'integer', 'type' => 'integer',
'length' => 4,
'id' => true 'id' => true
)); ));
$mapping->mapField(array( $mapping->mapField(array(

View File

@ -3,50 +3,57 @@ require_once 'lib/DoctrineTestInit.php';
require_once 'lib/mocks/Doctrine_EntityManagerMock.php'; require_once 'lib/mocks/Doctrine_EntityManagerMock.php';
require_once 'lib/mocks/Doctrine_ConnectionMock.php'; require_once 'lib/mocks/Doctrine_ConnectionMock.php';
require_once 'lib/mocks/Doctrine_ClassMetadataMock.php'; require_once 'lib/mocks/Doctrine_ClassMetadataMock.php';
require_once 'lib/mocks/Doctrine_UnitOfWorkMock.php';
/** /**
* EntityPersister tests. * EntityPersister tests.
*/ */
class Orm_EntityPersisterTest extends Doctrine_OrmTestCase class Orm_EntityPersisterTest extends Doctrine_OrmTestCase
{ {
private $_persister; // SUT
private $_connMock; private $_connMock;
private $_emMock; private $_emMock;
private $_idGenMock; private $_idGenMock;
private $classMetadataMock; private $_uowMock;
protected function setUp() { protected function setUp() {
parent::setUp(); parent::setUp();
$this->_connMock = new Doctrine_ConnectionMock(array()); $this->_connMock = new Doctrine_ConnectionMock(array());
$this->_emMock = Doctrine_EntityManagerMock::create($this->_connMock, 'persisterMockEM'); $this->_emMock = Doctrine_EntityManagerMock::create($this->_connMock, 'persisterMockEM');
$this->_uowMock = new Doctrine_UnitOfWorkMock($this->_emMock);
$this->_emMock->setUnitOfWork($this->_uowMock);
$this->_idGenMock = new Doctrine_SequenceMock($this->_emMock); $this->_idGenMock = new Doctrine_SequenceMock($this->_emMock);
$this->_classMetadataMock = new Doctrine_ClassMetadataMock("ForumUser", $this->_emMock); $this->_emMock->setIdGenerator('ForumUser', $this->_idGenMock);
$this->_classMetadataMock->setIdGenerator($this->_idGenMock);
$this->_connMock->setDatabasePlatform(new Doctrine_DatabasePlatformMock());
$this->_persister = new Doctrine_ORM_Persisters_StandardEntityPersister(
$this->_emMock, $this->_emMock->getClassMetadata("ForumUser"));
$this->_emMock->activate(); $this->_emMock->activate();
} }
public function testInsert() { public function testSimpleInsert() {
$userPersister = new Doctrine_ORM_Persisters_StandardEntityPersister(
$this->_emMock, $this->_emMock->getClassMetadata("ForumUser"));
$avatarPersister = new Doctrine_ORM_Persisters_StandardEntityPersister(
$this->_emMock, $this->_emMock->getClassMetadata("ForumAvatar"));
$user = new ForumUser(); $user = new ForumUser();
$user->username = "romanb"; $user->username = "romanb";
$user->avatar = new ForumAvatar(); $user->avatar = new ForumAvatar();
$this->_uowMock->setDataChangeSet($user, array(
'username' => array('' => 'romanb'),
'avatar' => array('' => $user->avatar)));
//insert //insert
$this->_persister->insert($user->avatar); $avatarPersister->insert($user->avatar);
$inserts = $this->_connMock->getInserts(); $inserts = $this->_connMock->getInserts();
//check //check
$this->assertEquals(1, count($inserts)); $this->assertEquals(1, count($inserts));
$this->assertEquals(null, $user->avatar->id);
$user->avatar->id = 0; // fake we got id
$this->assertTrue(isset($inserts['forum_avatar'])); $this->assertTrue(isset($inserts['forum_avatar']));
$this->assertEquals(1, count($inserts['forum_avatar'])); $this->assertEquals(1, count($inserts['forum_avatar']));
$this->assertTrue(empty($inserts['forum_avatar'][0])); $this->assertEquals(null, $user->avatar->id);
$user->avatar->id = 0; // Fake that we got an id
//insert //insert
$this->_persister->insert($user); $userPersister->insert($user);
$inserts = $this->_connMock->getInserts(); $inserts = $this->_connMock->getInserts();
//check //check
$this->assertEquals(2, count($inserts)); $this->assertEquals(2, count($inserts));

View File

@ -1,6 +1,6 @@
<?php <?php
#namespace Doctrine::Tests::ORM::Hydration; #namespace Doctrine\Tests\ORM\Hydration;
require_once 'lib/DoctrineTestInit.php'; require_once 'lib/DoctrineTestInit.php';
require_once 'lib/mocks/Doctrine_HydratorMockStatement.php'; require_once 'lib/mocks/Doctrine_HydratorMockStatement.php';
@ -78,18 +78,22 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY || $hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) { if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY || $hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) {
$this->assertEquals(2, count($result)); $this->assertEquals(2, count($result));
$this->assertEquals(1, $result[0]['id']);
$this->assertEquals('romanb', $result[0]['name']);
$this->assertEquals(2, $result[1]['id']);
$this->assertEquals('jwage', $result[1]['name']);
} }
if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) { if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) {
$this->assertTrue($result instanceof Doctrine_ORM_Collection); $this->assertTrue($result instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0] instanceof Doctrine_ORM_Entity); $this->assertTrue($result[0] instanceof CmsUser);
$this->assertTrue($result[1] instanceof Doctrine_ORM_Entity); $this->assertTrue($result[1] instanceof CmsUser);
$this->assertEquals(1, $result[0]->id);
$this->assertEquals('romanb', $result[0]->name);
$this->assertEquals(2, $result[1]->id);
$this->assertEquals('jwage', $result[1]->name);
} else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY) { } else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY) {
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
$this->assertEquals(1, $result[0]['id']);
$this->assertEquals('romanb', $result[0]['name']);
$this->assertEquals(2, $result[1]['id']);
$this->assertEquals('jwage', $result[1]['name']);
} else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_SCALAR) { } else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_SCALAR) {
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
$this->assertEquals(2, count($result)); $this->assertEquals(2, count($result));
@ -172,7 +176,27 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0])); $this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1])); $this->assertTrue(is_array($result[1]));
}
if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) {
$this->assertTrue($result[0][0] instanceof CmsUser);
$this->assertTrue($result[0][0]->phonenumbers instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]->phonenumbers[0] instanceof CmsPhonenumber);
$this->assertTrue($result[0][0]->phonenumbers[1] instanceof CmsPhonenumber);
$this->assertTrue($result[1][0] instanceof CmsUser);
$this->assertTrue($result[1][0]->phonenumbers instanceof Doctrine_ORM_Collection);
// first user => 2 phonenumbers
$this->assertEquals(2, count($result[0][0]->phonenumbers));
$this->assertEquals('ROMANB', $result[0]['nameUpper']);
// second user => 1 phonenumber
$this->assertEquals(1, count($result[1][0]->phonenumbers));
$this->assertEquals('JWAGE', $result[1]['nameUpper']);
$this->assertEquals(42, $result[0][0]->phonenumbers[0]->phonenumber);
$this->assertEquals(43, $result[0][0]->phonenumbers[1]->phonenumber);
$this->assertEquals(91, $result[1][0]->phonenumbers[0]->phonenumber);
} else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY) {
// first user => 2 phonenumbers // first user => 2 phonenumbers
$this->assertEquals(2, count($result[0][0]['phonenumbers'])); $this->assertEquals(2, count($result[0][0]['phonenumbers']));
$this->assertEquals('ROMANB', $result[0]['nameUpper']); $this->assertEquals('ROMANB', $result[0]['nameUpper']);
@ -183,15 +207,6 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
$this->assertEquals(42, $result[0][0]['phonenumbers'][0]['phonenumber']); $this->assertEquals(42, $result[0][0]['phonenumbers'][0]['phonenumber']);
$this->assertEquals(43, $result[0][0]['phonenumbers'][1]['phonenumber']); $this->assertEquals(43, $result[0][0]['phonenumbers'][1]['phonenumber']);
$this->assertEquals(91, $result[1][0]['phonenumbers'][0]['phonenumber']); $this->assertEquals(91, $result[1][0]['phonenumbers'][0]['phonenumber']);
}
if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) {
$this->assertTrue($result[0][0] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[0][0]['phonenumbers'] instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]['phonenumbers'][0] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[0][0]['phonenumbers'][1] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[1][0] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[1][0]['phonenumbers'] instanceof Doctrine_ORM_Collection);
} else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_SCALAR) { } else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_SCALAR) {
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
$this->assertEquals(3, count($result)); $this->assertEquals(3, count($result));
@ -265,7 +280,6 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0])); $this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1])); $this->assertTrue(is_array($result[1]));
// first user => 2 phonenumbers // first user => 2 phonenumbers
$this->assertEquals(2, $result[0]['numPhones']); $this->assertEquals(2, $result[0]['numPhones']);
// second user => 1 phonenumber // second user => 1 phonenumber
@ -273,8 +287,8 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
} }
if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) { if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) {
$this->assertTrue($result[0][0] instanceof Doctrine_ORM_Entity); $this->assertTrue($result[0][0] instanceof CmsUser);
$this->assertTrue($result[1][0] instanceof Doctrine_ORM_Entity); $this->assertTrue($result[1][0] instanceof CmsUser);
} else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_SCALAR) { } else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_SCALAR) {
$this->assertEquals(2, count($result)); $this->assertEquals(2, count($result));
@ -351,7 +365,7 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
$result = $hydrator->hydrateResultSet($this->_createParserResult( $result = $hydrator->hydrateResultSet($this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, $hydrationMode, true)); $stmt, $queryComponents, $tableAliasMap, $hydrationMode, true));
if ($hydrationMode == Doctrine::HYDRATE_ARRAY) { if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY) {
//var_dump($result); //var_dump($result);
} }
@ -361,26 +375,32 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
$this->assertTrue(is_array($result[0])); $this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1])); $this->assertTrue(is_array($result[1]));
// first user => 2 phonenumbers. notice the custom indexing by user id
$this->assertEquals(2, count($result[0]['1']['phonenumbers']));
// second user => 1 phonenumber. notice the custom indexing by user id
$this->assertEquals(1, count($result[1]['2']['phonenumbers']));
// test the custom indexing of the phonenumbers
$this->assertTrue(isset($result[0]['1']['phonenumbers']['42']));
$this->assertTrue(isset($result[0]['1']['phonenumbers']['43']));
$this->assertTrue(isset($result[1]['2']['phonenumbers']['91']));
// test the scalar values // test the scalar values
$this->assertEquals('ROMANB', $result[0]['nameUpper']); $this->assertEquals('ROMANB', $result[0]['nameUpper']);
$this->assertEquals('JWAGE', $result[1]['nameUpper']); $this->assertEquals('JWAGE', $result[1]['nameUpper']);
} }
if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) { if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) {
$this->assertTrue($result[0]['1'] instanceof Doctrine_ORM_Entity); $this->assertTrue($result[0]['1'] instanceof CmsUser);
$this->assertTrue($result[1]['2'] instanceof Doctrine_ORM_Entity); $this->assertTrue($result[1]['2'] instanceof CmsUser);
$this->assertTrue($result[0]['1']['phonenumbers'] instanceof Doctrine_ORM_Collection); $this->assertTrue($result[0]['1']->phonenumbers instanceof Doctrine_ORM_Collection);
// first user => 2 phonenumbers. notice the custom indexing by user id
$this->assertEquals(2, count($result[0]['1']->phonenumbers));
// second user => 1 phonenumber. notice the custom indexing by user id
$this->assertEquals(1, count($result[1]['2']->phonenumbers));
// test the custom indexing of the phonenumbers
$this->assertTrue(isset($result[0]['1']->phonenumbers['42']));
$this->assertTrue(isset($result[0]['1']->phonenumbers['43']));
$this->assertTrue(isset($result[1]['2']->phonenumbers['91']));
} else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY) {
// first user => 2 phonenumbers. notice the custom indexing by user id
$this->assertEquals(2, count($result[0]['1']['phonenumbers'])); $this->assertEquals(2, count($result[0]['1']['phonenumbers']));
// second user => 1 phonenumber. notice the custom indexing by user id
$this->assertEquals(1, count($result[1]['2']['phonenumbers']));
// test the custom indexing of the phonenumbers
$this->assertTrue(isset($result[0]['1']['phonenumbers']['42']));
$this->assertTrue(isset($result[0]['1']['phonenumbers']['43']));
$this->assertTrue(isset($result[1]['2']['phonenumbers']['91']));
} else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_SCALAR) { } else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_SCALAR) {
// NOTE: Indexing has no effect with HYDRATE_SCALAR // NOTE: Indexing has no effect with HYDRATE_SCALAR
//... asserts to come //... asserts to come
@ -500,7 +520,22 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0])); $this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1])); $this->assertTrue(is_array($result[1]));
}
if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) {
$this->assertTrue($result[0][0] instanceof CmsUser);
$this->assertTrue($result[0][0]->phonenumbers instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]->phonenumbers[0] instanceof CmsPhonenumber);
$this->assertTrue($result[0][0]->phonenumbers[1] instanceof CmsPhonenumber);
$this->assertTrue($result[0][0]->articles instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]->articles[0] instanceof CmsArticle);
$this->assertTrue($result[0][0]->articles[1] instanceof CmsArticle);
$this->assertTrue($result[1][0] instanceof CmsUser);
$this->assertTrue($result[1][0]->phonenumbers instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[1][0]->phonenumbers[0] instanceof CmsPhonenumber);
$this->assertTrue($result[1][0]->articles[0] instanceof CmsArticle);
$this->assertTrue($result[1][0]->articles[1] instanceof CmsArticle);
} else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY) {
// first user => 2 phonenumbers, 2 articles // first user => 2 phonenumbers, 2 articles
$this->assertEquals(2, count($result[0][0]['phonenumbers'])); $this->assertEquals(2, count($result[0][0]['phonenumbers']));
$this->assertEquals(2, count($result[0][0]['articles'])); $this->assertEquals(2, count($result[0][0]['articles']));
@ -518,21 +553,6 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
$this->assertEquals('ZendCon', $result[0][0]['articles'][1]['topic']); $this->assertEquals('ZendCon', $result[0][0]['articles'][1]['topic']);
$this->assertEquals('LINQ', $result[1][0]['articles'][0]['topic']); $this->assertEquals('LINQ', $result[1][0]['articles'][0]['topic']);
$this->assertEquals('PHP6', $result[1][0]['articles'][1]['topic']); $this->assertEquals('PHP6', $result[1][0]['articles'][1]['topic']);
}
if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) {
$this->assertTrue($result[0][0] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[0][0]['phonenumbers'] instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]['phonenumbers'][0] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[0][0]['phonenumbers'][1] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[0][0]['articles'] instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]['articles'][0] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[0][0]['articles'][1] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[1][0] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[1][0]['phonenumbers'] instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[1][0]['phonenumbers'][0] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[1][0]['articles'][0] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[1][0]['articles'][1] instanceof Doctrine_ORM_Entity);
} else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_SCALAR) { } else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_SCALAR) {
//... //...
$this->assertEquals(6, count($result)); $this->assertEquals(6, count($result));
@ -675,7 +695,36 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0])); $this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1])); $this->assertTrue(is_array($result[1]));
}
if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) {
$this->assertTrue($result[0][0] instanceof CmsUser);
$this->assertTrue($result[1][0] instanceof CmsUser);
// phonenumbers
$this->assertTrue($result[0][0]->phonenumbers instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]->phonenumbers[0] instanceof CmsPhonenumber);
$this->assertTrue($result[0][0]->phonenumbers[1] instanceof CmsPhonenumber);
$this->assertTrue($result[1][0]->phonenumbers instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[1][0]->phonenumbers[0] instanceof CmsPhonenumber);
// articles
$this->assertTrue($result[0][0]->articles instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]->articles[0] instanceof CmsArticle);
$this->assertTrue($result[0][0]->articles[1] instanceof CmsArticle);
$this->assertTrue($result[1][0]->articles[0] instanceof CmsArticle);
$this->assertTrue($result[1][0]->articles[1] instanceof CmsArticle);
// article comments
$this->assertTrue($result[0][0]->articles[0]->comments instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]->articles[0]->comments[0] instanceof CmsComment);
// empty comment collections
$this->assertTrue($result[0][0]->articles[1]->comments instanceof Doctrine_ORM_Collection);
$this->assertEquals(0, count($result[0][0]->articles[1]->comments));
$this->assertTrue($result[1][0]->articles[0]->comments instanceof Doctrine_ORM_Collection);
$this->assertEquals(0, count($result[1][0]->articles[0]->comments));
$this->assertTrue($result[1][0]->articles[1]->comments instanceof Doctrine_ORM_Collection);
$this->assertEquals(0, count($result[1][0]->articles[1]->comments));
} else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_SCALAR) {
//...
} else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY) {
// first user => 2 phonenumbers, 2 articles, 1 comment on first article // first user => 2 phonenumbers, 2 articles, 1 comment on first article
$this->assertEquals(2, count($result[0][0]['phonenumbers'])); $this->assertEquals(2, count($result[0][0]['phonenumbers']));
$this->assertEquals(2, count($result[0][0]['articles'])); $this->assertEquals(2, count($result[0][0]['articles']));
@ -698,41 +747,6 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
$this->assertEquals('First!', $result[0][0]['articles'][0]['comments'][0]['topic']); $this->assertEquals('First!', $result[0][0]['articles'][0]['comments'][0]['topic']);
$this->assertTrue(isset($result[0][0]['articles'][0]['comments'])); $this->assertTrue(isset($result[0][0]['articles'][0]['comments']));
// empty collections/arrays
$this->assertTrue(isset($result[0][0]['articles'][1]['comments']));
$this->assertTrue(isset($result[1][0]['articles'][0]['comments']));
$this->assertTrue(isset($result[1][0]['articles'][1]['comments']));
}
if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) {
$this->assertTrue($result[0][0] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[1][0] instanceof Doctrine_ORM_Entity);
// phonenumbers
$this->assertTrue($result[0][0]['phonenumbers'] instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]['phonenumbers'][0] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[0][0]['phonenumbers'][1] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[1][0]['phonenumbers'] instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[1][0]['phonenumbers'][0] instanceof Doctrine_ORM_Entity);
// articles
$this->assertTrue($result[0][0]['articles'] instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]['articles'][0] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[0][0]['articles'][1] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[1][0]['articles'][0] instanceof Doctrine_ORM_Entity);
$this->assertTrue($result[1][0]['articles'][1] instanceof Doctrine_ORM_Entity);
// article comments
$this->assertTrue($result[0][0]['articles'][0]['comments'] instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0][0]['articles'][0]['comments'][0] instanceof Doctrine_ORM_Entity);
// empty comment collections
$this->assertTrue($result[0][0]['articles'][1]['comments'] instanceof Doctrine_ORM_Collection);
$this->assertEquals(0, count($result[0][0]['articles'][1]['comments']));
$this->assertTrue($result[1][0]['articles'][0]['comments'] instanceof Doctrine_ORM_Collection);
$this->assertEquals(0, count($result[1][0]['articles'][0]['comments']));
$this->assertTrue($result[1][0]['articles'][1]['comments'] instanceof Doctrine_ORM_Collection);
$this->assertEquals(0, count($result[1][0]['articles'][1]['comments']));
} else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_SCALAR) {
//...
} else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY) {
//...
// empty comment collections // empty comment collections
$this->assertTrue(is_array($result[0][0]['articles'][1]['comments'])); $this->assertTrue(is_array($result[0][0]['articles'][1]['comments']));
@ -798,7 +812,7 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
'c__name' => 'First', 'c__name' => 'First',
'b__id' => '1', 'b__id' => '1',
'b__position' => '0', 'b__position' => '0',
'b__category_id' => '1' //'b__category_id' => '1'
), ),
array( array(
'c__id' => '2', 'c__id' => '2',
@ -806,7 +820,7 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
'c__name' => 'Second', 'c__name' => 'Second',
'b__id' => '2', 'b__id' => '2',
'b__position' => '0', 'b__position' => '0',
'b__category_id' => '2' //'b__category_id' => '2'
), ),
array( array(
'c__id' => '1', 'c__id' => '1',
@ -814,7 +828,7 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
'c__name' => 'First', 'c__name' => 'First',
'b__id' => '3', 'b__id' => '3',
'b__position' => '1', 'b__position' => '1',
'b__category_id' => '1' //'b__category_id' => '1'
), ),
array( array(
'c__id' => '1', 'c__id' => '1',
@ -822,7 +836,7 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
'c__name' => 'First', 'c__name' => 'First',
'b__id' => '4', 'b__id' => '4',
'b__position' => '2', 'b__position' => '2',
'b__category_id' => '1' //'b__category_id' => '1'
) )
); );
@ -837,20 +851,26 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY || $hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) { if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY || $hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) {
$this->assertEquals(2, count($result)); $this->assertEquals(2, count($result));
$this->assertTrue(isset($result[0]['boards']));
$this->assertEquals(3, count($result[0]['boards']));
$this->assertTrue(isset($result[1]['boards']));
$this->assertEquals(1, count($result[1]['boards']));
} }
if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY) { if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_ARRAY) {
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0])); $this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1])); $this->assertTrue(is_array($result[1]));
$this->assertTrue(isset($result[0]['boards']));
$this->assertEquals(3, count($result[0]['boards']));
$this->assertTrue(isset($result[1]['boards']));
$this->assertEquals(1, count($result[1]['boards']));
} else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) { } else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_OBJECT) {
$this->assertTrue($result instanceof Doctrine_ORM_Collection); $this->assertTrue($result instanceof Doctrine_ORM_Collection);
$this->assertTrue($result[0] instanceof Doctrine_ORM_Entity); $this->assertTrue($result[0] instanceof ForumCategory);
$this->assertTrue($result[1] instanceof Doctrine_ORM_Entity); $this->assertTrue($result[1] instanceof ForumCategory);
$this->assertEquals(1, $result[0]->getId());
$this->assertEquals(2, $result[1]->getId());
$this->assertTrue(isset($result[0]->boards));
$this->assertEquals(3, count($result[0]->boards));
$this->assertTrue(isset($result[1]->boards));
$this->assertEquals(1, count($result[1]->boards));
} else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_SCALAR) { } else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_SCALAR) {
//... //...
} }
@ -938,4 +958,77 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
} }
/**
* select u.id, u.status, p.phonenumber, upper(u.name) nameUpper from User u
* join u.phonenumbers p
* =
* select u.id, u.status, p.phonenumber, upper(u.name) as u__0 from USERS u
* INNER JOIN PHONENUMBERS p ON u.id = p.user_id
*
* @dataProvider hydrationModeProvider
*/
/*public function testNewHydrationMixedQueryFetchJoinPerformance($hydrationMode)
{
// Faked query components
$queryComponents = array(
'u' => array(
'metadata' => $this->_em->getClassMetadata('CmsUser'),
'parent' => null,
'relation' => null,
'map' => null,
'agg' => array('0' => 'nameUpper')
),
'p' => array(
'metadata' => $this->_em->getClassMetadata('CmsPhonenumber'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('phonenumbers'),
'map' => null
)
);
// Faked table alias map
$tableAliasMap = array(
'u' => 'u',
'p' => 'p'
);
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '42',
),
array(
'u__id' => '1',
'u__status' => 'developer',
'u__0' => 'ROMANB',
'p__phonenumber' => '43',
),
array(
'u__id' => '2',
'u__status' => 'developer',
'u__0' => 'JWAGE',
'p__phonenumber' => '91'
)
);
for ($i=4; $i<5000; $i++) {
$resultSet[] = array(
'u__id' => $i,
'u__status' => 'developer',
'u__0' => 'JWAGE' . $i,
'p__phonenumber' => '91'
);
}
$stmt = new Doctrine_HydratorMockStatement($resultSet);
$hydrator = new Doctrine_ORM_Internal_Hydration_StandardHydrator($this->_em);
$result = $hydrator->hydrateResultSet($this->_createParserResult(
$stmt, $queryComponents, $tableAliasMap, $hydrationMode, true));
}*/
} }

View File

@ -24,21 +24,15 @@ class Orm_UnitOfWorkTest extends Doctrine_OrmTestCase
private $_persisterMock; private $_persisterMock;
// The EntityManager mock that provides the mock persister // The EntityManager mock that provides the mock persister
private $_emMock; private $_emMock;
private $_platformMock;
private $_classMetadataMock;
protected function setUp() { protected function setUp() {
parent::setUp(); parent::setUp();
$this->_connectionMock = new Doctrine_ConnectionMock(array()); $this->_connectionMock = new Doctrine_ConnectionMock(array());
$this->_platformMock = new Doctrine_DatabasePlatformMock();
$this->_platformMock->setPrefersIdentityColumns(true);
$this->_emMock = Doctrine_EntityManagerMock::create($this->_connectionMock, "uowMockEm"); $this->_emMock = Doctrine_EntityManagerMock::create($this->_connectionMock, "uowMockEm");
$this->_idGeneratorMock = new Doctrine_SequenceMock($this->_emMock); $this->_idGeneratorMock = new Doctrine_SequenceMock($this->_emMock);
$this->_connectionMock->setDatabasePlatform($this->_platformMock); $this->_emMock->setIdGenerator('ForumUser', $this->_idGeneratorMock);
$this->_emMock->setIdGenerator('ForumAvatar', $this->_idGeneratorMock);
$this->_classMetadataMock = new Doctrine_ClassMetadataMock("ForumUser", $this->_emMock);
$this->_classMetadataMock->setIdGenerator($this->_idGeneratorMock);
$this->_persisterMock = new Doctrine_EntityPersisterMock( $this->_persisterMock = new Doctrine_EntityPersisterMock(
$this->_emMock, $this->_emMock->getClassMetadata("ForumUser")); $this->_emMock, $this->_emMock->getClassMetadata("ForumUser"));
@ -55,7 +49,7 @@ class Orm_UnitOfWorkTest extends Doctrine_OrmTestCase
} }
protected function tearDown() { protected function tearDown() {
$this->_user->free(); //$this->_user->free();
} }
/* Basic registration tests */ /* Basic registration tests */
@ -83,16 +77,6 @@ class Orm_UnitOfWorkTest extends Doctrine_OrmTestCase
echo $e - $s . " seconds" . PHP_EOL; echo $e - $s . " seconds" . PHP_EOL;
}*/ }*/
public function testRegisterDirty()
{
$this->assertEquals(Doctrine_ORM_Entity::STATE_NEW, $this->_user->_state());
$this->assertFalse($this->_unitOfWork->isInIdentityMap($this->_user));
$this->_unitOfWork->registerDirty($this->_user);
$this->assertTrue($this->_unitOfWork->isRegisteredDirty($this->_user));
$this->assertFalse($this->_unitOfWork->isRegisteredNew($this->_user));
$this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user));
}
public function testRegisterRemovedOnNewEntityIsIgnored() public function testRegisterRemovedOnNewEntityIsIgnored()
{ {
$this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user)); $this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user));
@ -105,8 +89,6 @@ class Orm_UnitOfWorkTest extends Doctrine_OrmTestCase
public function testSavingSingleEntityWithIdentityColumnForcesInsert() public function testSavingSingleEntityWithIdentityColumnForcesInsert()
{ {
$this->assertEquals(Doctrine_ORM_Entity::STATE_NEW, $this->_user->_state());
$this->_unitOfWork->save($this->_user); $this->_unitOfWork->save($this->_user);
$this->assertEquals(1, count($this->_persisterMock->getInserts())); // insert forced $this->assertEquals(1, count($this->_persisterMock->getInserts())); // insert forced
@ -114,7 +96,6 @@ class Orm_UnitOfWorkTest extends Doctrine_OrmTestCase
$this->assertEquals(0, count($this->_persisterMock->getDeletes())); $this->assertEquals(0, count($this->_persisterMock->getDeletes()));
$this->assertTrue($this->_unitOfWork->isInIdentityMap($this->_user)); $this->assertTrue($this->_unitOfWork->isInIdentityMap($this->_user));
$this->assertEquals(Doctrine_ORM_Entity::STATE_MANAGED, $this->_user->_state());
// should no longer be scheduled for insert // should no longer be scheduled for insert
$this->assertFalse($this->_unitOfWork->isRegisteredNew($this->_user)); $this->assertFalse($this->_unitOfWork->isRegisteredNew($this->_user));
@ -149,7 +130,7 @@ class Orm_UnitOfWorkTest extends Doctrine_OrmTestCase
//... //...
} }
/*
public function testSavingSingleEntityWithSequenceIdGeneratorSchedulesInsert() public function testSavingSingleEntityWithSequenceIdGeneratorSchedulesInsert()
{ {
//... //...
@ -189,5 +170,5 @@ class Orm_UnitOfWorkTest extends Doctrine_OrmTestCase
{ {
//... //...
} }
*/
} }

View File

@ -8,11 +8,6 @@ class Doctrine_DbalTestCase extends Doctrine_TestCase
/** /**
* setUp() * setUp()
*
* Note: This setUp() and the one of OrmTestCase currently look identical. However,
* please dont pull this method up. In the future with a separation of Dbal/Orm
* this setUp() will take care of a DBAL connection and the ORM setUp() will take care
* of an ORM connection/session/manager.
*/ */
protected function setUp() protected function setUp()
{ {

View File

@ -22,24 +22,6 @@ class Doctrine_OrmFunctionalTestCase extends Doctrine_OrmTestCase
*/ */
private static $_exportedTables = array(); private static $_exportedTables = array();
/**
* setUp()
*
* Note: This setUp() and the one of DbalTestCase currently look identical. However,
* please dont pull this method up. In the future with a separation of Dbal/Orm
* this setUp() will take care of a ORM connection/session/manager initialization
* and the DBAL setUp() will take care of just a DBAL connection.
*/
protected function setUp()
{
// Setup a db connection if there is none, yet. This makes it possible
// to run tests that use a connection standalone.
if ( ! isset($this->sharedFixture['em'])) {
$this->sharedFixture['em'] = new Doctrine_EntityManager(
Doctrine_TestUtil::getConnection());
}
}
/** /**
* Loads a data fixture into the database. This method must only be called * Loads a data fixture into the database. This method must only be called
* from within the setUp() method of testcases. The database will then be * from within the setUp() method of testcases. The database will then be
@ -78,7 +60,7 @@ class Doctrine_OrmFunctionalTestCase extends Doctrine_OrmTestCase
$tableName = $classMetadata->getTableName(); $tableName = $classMetadata->getTableName();
if ( ! in_array($tableName, self::$_exportedTables)) { if ( ! in_array($tableName, self::$_exportedTables)) {
$em->getConnection()->export->exportClasses(array($fixture['model'])); $em->getConnection()->getSchemaManager()->exportClasses(array($fixture['model']));
self::$_exportedTables[] = $tableName; self::$_exportedTables[] = $tableName;
} }

View File

@ -10,8 +10,6 @@ class Doctrine_OrmFunctionalTestSuite extends Doctrine_OrmTestSuite
{ {
protected function setUp() protected function setUp()
{ {
$this->sharedFixture['em'] = new Doctrine_EntityManager(
Doctrine_TestUtil::getConnection());
} }
protected function tearDown() protected function tearDown()

View File

@ -10,7 +10,6 @@ require_once 'lib/mocks/Doctrine_ConnectionMock.php';
class Doctrine_OrmTestCase extends Doctrine_TestCase class Doctrine_OrmTestCase extends Doctrine_TestCase
{ {
protected $_em; protected $_em;
protected $_emf;
protected function setUp() { protected function setUp() {
if (isset($this->sharedFixture['em'])) { if (isset($this->sharedFixture['em'])) {

View File

@ -13,8 +13,6 @@ class Doctrine_TestUtil
'host' => $GLOBALS['db_host'], 'host' => $GLOBALS['db_host'],
'database' => $GLOBALS['db_name'] 'database' => $GLOBALS['db_name']
); );
//$dsn = "{$GLOBALS['db_type']}://{$GLOBALS['db_username']}:{$GLOBALS['db_password']}@{$GLOBALS['db_host']}/{$GLOBALS['db_name']}";
//return Doctrine_Manager::connection($dsn, 'testconn');
} else { } else {
$params = array( $params = array(
'driver' => 'pdo_sqlite', 'driver' => 'pdo_sqlite',
@ -24,13 +22,4 @@ class Doctrine_TestUtil
return Doctrine_DBAL_DriverManager::getConnection($params); return Doctrine_DBAL_DriverManager::getConnection($params);
} }
/*
public static function autoloadModel($className)
{
$modelDir = dirname(__CLASS__) . '/../models/';
$fileName = $modelDir . str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
if (file_exists($fileName)) {
require $fileName;
}
}*/
} }

View File

@ -2,12 +2,10 @@
class Doctrine_ClassMetadataMock extends Doctrine_ORM_Mapping_ClassMetadata class Doctrine_ClassMetadataMock extends Doctrine_ORM_Mapping_ClassMetadata
{ {
/* Mock API */ /* Mock API */
public function setIdGenerator(Doctrine_ORM_Id_AbstractIdGenerator $g) { public function setIdGeneratorType($type) {
$this->_idGenerator = $g; $this->_generatorType = $type;
} }
} }

View File

@ -5,20 +5,33 @@ require_once 'lib/mocks/Doctrine_EntityPersisterMock.php';
class Doctrine_EntityManagerMock extends Doctrine_ORM_EntityManager class Doctrine_EntityManagerMock extends Doctrine_ORM_EntityManager
{ {
private $_persisterMock; private $_persisterMock;
private $_uowMock;
private $_idGenerators = array();
/** /**
* Enter description here...
*
* @param unknown_type $entityName
* @override * @override
*/ */
public function getEntityPersister($entityName) public function getEntityPersister($entityName)
{ {
return $this->_persisterMock; return isset($this->_persisterMock) ? $this->_persisterMock :
parent::getEntityPersister($entityName);
}
/**
* @override
*/
public function getUnitOfWork()
{
return isset($this->_uowMock) ? $this->_uowMock : parent::getUnitOfWork();
} }
/* Mock API */ /* Mock API */
public function setUnitOfWork($uow)
{
$this->_uowMock = $uow;
}
public function setEntityPersister($persister) public function setEntityPersister($persister)
{ {
$this->_persisterMock = $persister; $this->_persisterMock = $persister;
@ -45,6 +58,19 @@ class Doctrine_EntityManagerMock extends Doctrine_ORM_EntityManager
return new Doctrine_EntityManagerMock($conn, $name, $config, $eventManager); return new Doctrine_EntityManagerMock($conn, $name, $config, $eventManager);
} }
public function setIdGenerator($className, $generator)
{
$this->_idGenerators[$className] = $generator;
}
public function getIdGenerator($className)
{
if (isset($this->_idGenerators[$className])) {
return $this->_idGenerators[$className];
}
return parent::getIdGenerator($className);
}
} }
?> ?>

View File

@ -8,10 +8,11 @@ class Doctrine_EntityPersisterMock extends Doctrine_ORM_Persisters_StandardEntit
private $_identityColumnValueCounter = 0; private $_identityColumnValueCounter = 0;
public function insert(Doctrine_ORM_Entity $entity) public function insert($entity)
{ {
if ($entity->getClass()->isIdGeneratorIdentity()) { $class = $this->_em->getClassMetadata(get_class($entity));
$entity->_assignIdentifier($this->_identityColumnValueCounter++); if ($class->isIdGeneratorIdentity()) {
$class->setEntityIdentifier($entity, $this->_identityColumnValueCounter++);
$this->_em->getUnitOfWork()->addToIdentityMap($entity); $this->_em->getUnitOfWork()->addToIdentityMap($entity);
} }

View File

@ -10,7 +10,7 @@ class Doctrine_SequenceMock extends Doctrine_ORM_Id_SequenceGenerator
* @param Doctrine_Entity $entity * @param Doctrine_Entity $entity
* @override * @override
*/ */
public function generate(Doctrine_ORM_Entity $entity) public function generate($entity)
{ {
return $this->_sequenceNumber++; return $this->_sequenceNumber++;
} }

View File

@ -0,0 +1,31 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/**
* Description of Doctrine_UnitOfWorkMock
*
* @author robo
*/
class Doctrine_UnitOfWorkMock extends Doctrine_ORM_UnitOfWork {
private $_mockDataChangeSets = array();
/**
* @param <type> $entity
* @override
*/
public function getDataChangeSet($entity) {
$oid = spl_object_id($entity);
return isset($this->_mockDataChangeSets[$oid]) ?
$this->_mockDataChangeSets[$oid] : parent::getDataChangeSet($entity);
}
/* MOCK API */
public function setDataChangeSet($entity, array $mockChangeSet) {
$this->_mockDataChangeSets[spl_object_id($entity)] = $mockChangeSet;
}
}

View File

@ -4,19 +4,28 @@
#use Doctrine::ORM::Entity; #use Doctrine::ORM::Entity;
class CmsArticle extends Doctrine_ORM_Entity class CmsArticle
{ {
#protected $id; public $id;
#protected $topic; public $topic;
#protected $text; public $text;
#protected $user_id; public $user;
public $comments;
/*static function construct() {
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'id', 'int');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'topic', 'string');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'text', 'string');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'user_id', 'int');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'user', 'CmsUser');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'comments', 'collection');
}*/
public static function initMetadata($mapping) public static function initMetadata($mapping)
{ {
$mapping->mapField(array( $mapping->mapField(array(
'fieldName' => 'id', 'fieldName' => 'id',
'type' => 'integer', 'type' => 'integer',
'length' => 4,
'id' => true, 'id' => true,
'idGenerator' => 'auto' 'idGenerator' => 'auto'
)); ));
@ -31,11 +40,9 @@ class CmsArticle extends Doctrine_ORM_Entity
)); ));
$mapping->mapField(array( $mapping->mapField(array(
'fieldName' => 'user_id', 'fieldName' => 'user_id',
'type' => 'integer', 'type' => 'integer'
'length' => 4
)); ));
$mapping->mapOneToMany(array( $mapping->mapOneToMany(array(
'fieldName' => 'comments', 'fieldName' => 'comments',
'targetEntity' => 'CmsComment', 'targetEntity' => 'CmsComment',

View File

@ -4,19 +4,26 @@
#use Doctrine::ORM::Entity; #use Doctrine::ORM::Entity;
class CmsComment extends Doctrine_ORM_Entity class CmsComment
{ {
#protected $id; public $id;
#protected $topic; public $topic;
#protected $text; public $text;
#protected $article_id; public $article;
/*static function construct() {
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'id', 'int');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'topic', 'string');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'text', 'string');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'article_id', 'int');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'article', 'CmsArticle');
}*/
public static function initMetadata($mapping) public static function initMetadata($mapping)
{ {
$mapping->mapField(array( $mapping->mapField(array(
'fieldName' => 'id', 'fieldName' => 'id',
'type' => 'integer', 'type' => 'integer',
'length' => 4,
'id' => true, 'id' => true,
'generatorType' => 'auto' 'generatorType' => 'auto'
)); ));
@ -31,8 +38,7 @@ class CmsComment extends Doctrine_ORM_Entity
)); ));
$mapping->mapField(array( $mapping->mapField(array(
'fieldName' => 'article_id', 'fieldName' => 'article_id',
'type' => 'integer', 'type' => 'integer'
'length' => 4
)); ));
$mapping->mapManyToOne(array( $mapping->mapManyToOne(array(

View File

@ -1,15 +1,20 @@
<?php <?php
class CmsPhonenumber extends Doctrine_ORM_Entity class CmsPhonenumber
{ {
#protected $user_id; public $phonenumber;
#protected $phonenumber; public $user;
/*static function construct() {
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'user_id', 'int');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'phonenumber', 'string');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'user', 'CmsUser');
}*/
public static function initMetadata($mapping) public static function initMetadata($mapping)
{ {
$mapping->mapField(array( $mapping->mapField(array(
'fieldName' => 'user_id', 'fieldName' => 'user_id',
'type' => 'integer', 'type' => 'integer'
'length' => 4
)); ));
$mapping->mapField(array( $mapping->mapField(array(
'fieldName' => 'phonenumber', 'fieldName' => 'phonenumber',

View File

@ -1,22 +1,48 @@
<?php <?php
#namespace Doctrine::Test::ORM::Models; #namespace Doctrine\Tests\ORM\Models\Cms;
#use Doctrine::ORM::Entity; #use Doctrine\ORM\Entity;
#use Doctrine\Common\VirtualPropertySystem;
class CmsUser extends Doctrine_ORM_Entity class CmsUser
{ {
#protected $id; public $id;
#protected $status; public $status;
#protected $username; public $username;
#protected $name; public $name;
public $phonenumbers;
public $articles;
/*static function construct() {
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'id', 'int');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'status', 'int');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'username', 'string');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'name', 'string');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'phonenumbers', 'CmsPhonenumber');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'articles', 'CmsArticle');
}*/
public static function initMetadata($mapping) public static function initMetadata($mapping)
{ {
/* NEW
$mapping->addFieldMetadata('id', array(
'doctrine.id' => true, 'doctrine.idGenerator' => 'auto'
));
$mapping->addFieldMetadata('status', array(
'doctrine.length' => 50
));
$mapping->addFieldMetadata('phonenumbers', array(
'doctrine.oneToMany' => array('mappedBy' => 'user')
));
$mapping->addFieldMetadata('articles', array(
'doctrine.oneToMany' => array('mappedBy' => 'user')
));
*/
$mapping->mapField(array( $mapping->mapField(array(
'fieldName' => 'id', 'fieldName' => 'id',
'type' => 'integer', 'type' => 'integer',
'length' => 4,
'id' => true, 'id' => true,
'idGenerator' => 'auto' 'idGenerator' => 'auto'
)); ));

View File

@ -1,6 +1,6 @@
<?php <?php
class CompanyEmployee extends Doctrine_ORM_Entity class CompanyEmployee
{ {
#protected $id; #protected $id;
#protected $salary; #protected $salary;

View File

@ -2,15 +2,14 @@
class ForumAdministrator extends ForumUser class ForumAdministrator extends ForumUser
{ {
public $accessLevel;
public static function initMetadata($mapping) public static function initMetadata($mapping)
{ {
$mapping->mapField(array( $mapping->mapField(array(
'fieldName' => 'accessLevel', 'fieldName' => 'accessLevel',
'columnName' => 'access_level', 'columnName' => 'access_level',
'type' => 'integer', 'type' => 'integer'
'length' => 1
)); ));
} }
public function banUser(ForumUser $user) {}
} }

View File

@ -1,19 +1,27 @@
<?php <?php
class ForumAvatar extends Doctrine_ORM_Entity #namespace Doctrine\Tests\ORM\Models\Forum;
#use Doctrine\ORM\Entity;
#use Doctrine\Common\VirtualPropertySystem;
class ForumAvatar
{ {
public $id;
/*static function construct() {
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'id', 'int');
}*/
public static function initMetadata($mapping) public static function initMetadata($mapping)
{ {
$mapping->mapField(array( $mapping->mapField(array(
'fieldName' => 'id', 'fieldName' => 'id',
'type' => 'integer', 'type' => 'integer',
'length' => 4,
'id' => true, 'id' => true,
'idGenerator' => 'auto' 'idGenerator' => 'auto'
)); ));
} }
} }

View File

@ -1,19 +1,15 @@
<?php <?php
class ForumBoard extends Doctrine_ORM_Entity class ForumBoard
{ {
public $id;
public $position;
public $category;
public static function initMetadata($mapping) public static function initMetadata($mapping)
{ {
/*$metadata->mapField(array(
'fieldName' => 'id',
'id' => true,
'type' => 'integer',
'length' => 4
));
*/
$mapping->mapField(array( $mapping->mapField(array(
'fieldName' => 'id', 'fieldName' => 'id',
'type' => 'integer', 'type' => 'integer',
'length' => 4,
'id' => true 'id' => true
)); ));
$mapping->mapField(array( $mapping->mapField(array(

View File

@ -1,13 +1,20 @@
<?php <?php
class ForumCategory extends Doctrine_ORM_Entity class ForumCategory
{ {
private $id;
public $position;
public $name;
public $boards;
public function getId() {
return $this->id;
}
public static function initMetadata($mapping) public static function initMetadata($mapping)
{ {
$mapping->mapField(array( $mapping->mapField(array(
'fieldName' => 'id', 'fieldName' => 'id',
'type' => 'integer', 'type' => 'integer',
'length' => 4,
'id' => true 'id' => true
)); ));
$mapping->mapField(array( $mapping->mapField(array(

View File

@ -6,15 +6,19 @@
class ForumEntry extends Doctrine_ORM_Entity class ForumEntry extends Doctrine_ORM_Entity
{ {
#protected $id; public $id;
#protected $topic; public $topic;
static function construct() {
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'id', 'int');
Doctrine_Common_VirtualPropertySystem::register(__CLASS__, 'topic', 'string');
}
public static function initMetadata($mapping) public static function initMetadata($mapping)
{ {
$mapping->mapField(array( $mapping->mapField(array(
'fieldName' => 'id', 'fieldName' => 'id',
'type' => 'integer', 'type' => 'integer',
'length' => 4,
'id' => true, 'id' => true,
'idGenerator' => 'auto' 'idGenerator' => 'auto'
)); ));

View File

@ -1,14 +1,15 @@
<?php <?php
#namespace Doctrine::Tests::ORM::Models::Forum; #namespace Doctrine\Tests\ORM\Models\Forum;
#use Doctrine::ORM::Entity; #use Doctrine\ORM\Entity;
#use Doctrine\Common\VirtualPropertySystem;
class ForumUser extends Doctrine_ORM_Entity class ForumUser
{ {
#protected $id; public $id;
#protected $username; public $username;
#protected $avatar; public $avatar;
public static function initMetadata($mapping) public static function initMetadata($mapping)
{ {
@ -26,7 +27,6 @@ class ForumUser extends Doctrine_ORM_Entity
$mapping->mapField(array( $mapping->mapField(array(
'fieldName' => 'id', 'fieldName' => 'id',
'type' => 'integer', 'type' => 'integer',
'length' => 4,
'id' => true, 'id' => true,
'idGenerator' => 'auto' 'idGenerator' => 'auto'
)); ));
@ -42,7 +42,5 @@ class ForumUser extends Doctrine_ORM_Entity
'joinColumns' => array('avatar_id' => 'id'), 'joinColumns' => array('avatar_id' => 'id'),
'cascade' => array('save') 'cascade' => array('save')
)); ));
} }
} }