diff --git a/lib/Doctrine/ClassMetadata.php b/lib/Doctrine/ClassMetadata.php
index 14b865cc8..f1bbba4a3 100644
--- a/lib/Doctrine/ClassMetadata.php
+++ b/lib/Doctrine/ClassMetadata.php
@@ -24,9 +24,8 @@
/**
* A ClassMetadata instance holds all the information (metadata) of an entity and
* it's associations and how they're mapped to the relational database.
+ * It is the backbone of Doctrine's metadata mapping.
*
- * @package Doctrine
- * @subpackage ClassMetadata
* @author Roman Borschel
* @since 2.0
*/
@@ -115,8 +114,8 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
protected $_generators = array();
/**
- * The mapped columns and their mapping definitions.
- * Keys are column names and values are mapping definitions.
+ * The field mappings of the class.
+ * Keys are field names and values are mapping definitions.
*
* The mapping definition array has at least the following values:
*
@@ -128,9 +127,9 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
* -- values enum values
* ... many more
*
- * @var array $columns
- */
- protected $_mappedColumns = array();
+ * @var array
+ */
+ protected $_fieldMappings = array();
/**
* The mapped embedded values (value objects).
@@ -282,7 +281,29 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
* @var array $_invokedMethods method invoker cache
*/
protected $_invokedMethods = array();
+
+ /**
+ * The cached lifecycle listeners. There is only one instance of each
+ * listener class at any time.
+ *
+ * @var array
+ */
+ protected $_lifecycleListenerInstances = array();
+ /**
+ * The registered lifecycle callbacks for Entities of this class.
+ *
+ * @var array
+ */
+ protected $_lifecycleCallbacks = array();
+
+ /**
+ * The registered lifecycle listeners for Entities of this class.
+ *
+ * @var array
+ */
+ protected $_lifecycleListeners = array();
+
/**
* Constructs a new ClassMetadata instance.
@@ -374,7 +395,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function isUniqueField($fieldName)
{
- $mapping = $this->getColumnMapping($fieldName);
+ $mapping = $this->getFieldMapping($fieldName);
if ($mapping !== false) {
return isset($mapping['unique']) && $mapping['unique'] == true;
@@ -391,7 +412,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function isNotNull($fieldName)
{
- $mapping = $this->getColumnMapping($fieldName);
+ $mapping = $this->getFieldMapping($fieldName);
if ($mapping !== false) {
return isset($mapping['notnull']) && $mapping['notnull'] == true;
@@ -531,7 +552,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
public function getColumnName($fieldName)
{
return isset($this->_columnNames[$fieldName]) ?
- $this->_columnNames[$fieldName] : $fieldName;
+ $this->_columnNames[$fieldName] : $fieldName;
}
/**
@@ -542,10 +563,10 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
return $this->getColumnMapping($columnName);
}
- public function getColumnMapping($columnName)
+ public function getFieldMapping($fieldName)
{
- return isset($this->_mappedColumns[$columnName]) ?
- $this->_mappedColumns[$columnName] : false;
+ return isset($this->_fieldMappings[$fieldName]) ?
+ $this->_fieldMappings[$fieldName] : false;
}
/**
@@ -593,6 +614,10 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
* 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)
{
@@ -626,7 +651,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
}
/**
- * Maps a column of the class' database table to a field of the entity.
+ * Maps a field of the class to a database column.
*
* @param string $name The name of the column to map. Syntax: columnName [as propertyName].
* The property name is optional. If not used the column will be
@@ -639,7 +664,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*
* @throws Doctrine_ClassMetadata_Exception If trying use wrongly typed parameter.
*/
- public function mapColumn($name, $type, $length = null, $options = array(), $prepend = false)
+ public function mapColumn($name, $type, $length = null, $options = array())
{
// converts 0 => 'primary' to 'primary' => true etc.
foreach ($options as $k => $option) {
@@ -661,18 +686,14 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
$columnName = $parts[0];
$lcColumnName = strtolower($parts[0]);
- if (isset($this->_mappedColumns[$columnName])) {
+ if (isset($this->_fieldMappings[$fieldName])) {
return;
}
// Fill column name <-> field name lookup maps
- if ($prepend) {
- $this->_columnNames = array_merge(array($fieldName => $columnName), $this->_columnNames);
- $this->_fieldNames = array_merge(array($columnName => $fieldName), $this->_fieldNames);
- } else {
- $this->_columnNames[$fieldName] = $columnName;
- $this->_fieldNames[$columnName] = $fieldName;
- }
+ $this->_columnNames[$fieldName] = $columnName;
+ $this->_fieldNames[$columnName] = $fieldName;
+ $this->_lcColumnToFieldNames[$lcColumnName] = $fieldName;
$this->_lcColumnToFieldNames[$lcColumnName] = $fieldName;
@@ -701,11 +722,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
$options['immutable'] = false;
}*/
- if ($prepend) {
- $this->_mappedColumns = array_merge(array($columnName => $options), $this->_mappedColumns);
- } else {
- $this->_mappedColumns[$columnName] = $options;
- }
+ $this->_fieldMappings[$fieldName] = $options;
$this->_columnCount++;
}
@@ -753,24 +770,6 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
//...
}
- /**
- * setColumn
- *
- * @param string $name
- * @param string $type
- * @param integer $length
- * @param mixed $options
- * @param boolean $prepend Whether to prepend or append the new column to the column list.
- * By default the column gets appended.
- * @throws Doctrine_Table_Exception if trying use wrongly typed parameter
- * @return void
- * @deprecated Use mapColumn()
- */
- public function setColumn($name, $type, $length = null, $options = array(), $prepend = false)
- {
- return $this->mapColumn($name, $type, $length, $options, $prepend);
- }
-
/**
* Gets the names of all validators that are applied on a field.
*
@@ -779,9 +778,8 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function getFieldValidators($fieldName)
{
- $columnName = $this->getColumnName($fieldName);
- return isset($this->_mappedColumns[$columnName]['validators']) ?
- $this->_mappedColumns[$columnName]['validators'] : array();
+ return isset($this->_fieldMappings[$fieldName]['validators']) ?
+ $this->_fieldMappings[$fieldName]['validators'] : array();
}
/**
@@ -803,12 +801,11 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function getDefaultValueOf($fieldName)
{
- $columnName = $this->getColumnName($fieldName);
- if ( ! isset($this->_mappedColumns[$columnName])) {
- throw new Doctrine_Table_Exception("Couldn't get default value. Column ".$columnName." doesn't exist.");
+ if ( ! isset($this->_fieldMappings[$fieldName])) {
+ throw new Doctrine_Table_Exception("Couldn't get default value. Column ".$fieldName." doesn't exist.");
}
- if (isset($this->_mappedColumns[$columnName]['default'])) {
- return $this->_mappedColumns[$columnName]['default'];
+ if (isset($this->_fieldMappings[$fieldName]['default'])) {
+ return $this->_fieldMappings[$fieldName]['default'];
} else {
return null;
}
@@ -867,12 +864,12 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function hasColumn($columnName)
{
- return isset($this->_mappedColumns[$columnName]);
+ return isset($this->_fieldNames[$columnName]);
}
public function hasMappedColumn($columnName)
{
- return isset($this->_mappedColumns[$columnName]);
+ return isset($this->_fieldNames[$columnName]);
}
/**
@@ -890,9 +887,8 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function getEnumValues($fieldName)
{
- $columnName = $this->getColumnName($fieldName);
- if (isset($this->_mappedColumns[$columnName]['values'])) {
- return $this->_mappedColumns[$columnName]['values'];
+ if (isset($this->_fieldMappings[$fieldName]['values'])) {
+ return $this->_fieldMappings[$fieldName]['values'];
} else {
return array();
}
@@ -917,8 +913,8 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
$columnName = $this->getColumnName($fieldName);
if ( ! $this->_em->getAttribute(Doctrine::ATTR_USE_NATIVE_ENUM) &&
- isset($this->_mappedColumns[$columnName]['values'][$index])) {
- $enumValue = $this->_mappedColumns[$columnName]['values'][$index];
+ isset($this->_fieldMappings[$fieldName]['values'][$index])) {
+ $enumValue = $this->_fieldMappings[$fieldName]['values'][$index];
} else {
$enumValue = $index;
}
@@ -973,9 +969,8 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function getCustomAccessor($fieldName)
{
- $columnName = $this->getColumnName($fieldName);
- return isset($this->_mappedColumns[$columnName]['accessor']) ?
- $this->_mappedColumns[$columnName]['accessor'] : null;
+ return isset($this->_fieldMappings[$fieldName]['accessor']) ?
+ $this->_fieldMappings[$fieldName]['accessor'] : null;
}
/**
@@ -985,9 +980,8 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function getCustomMutator($fieldName)
{
- $columnName = $this->getColumnName($fieldName);
- return isset($this->_mappedColumns[$columnName]['mutator']) ?
- $this->_mappedColumns[$columnName]['mutator'] : null;
+ return isset($this->_fieldMappings[$fieldName]['mutator']) ?
+ $this->_fieldMappings[$fieldName]['mutator'] : null;
}
/**
@@ -998,7 +992,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function getColumns()
{
- return $this->_mappedColumns;
+ return $this->_fieldMappings;
}
/**
@@ -1008,7 +1002,12 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function getMappedColumns()
{
- return $this->_mappedColumns;
+ return $this->_fieldMappings;
+ }
+
+ public function getFieldMappings()
+ {
+ return $this->_fieldMappings;
}
/**
@@ -1023,8 +1022,8 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
unset($this->_fieldNames[$columnName]);
- if (isset($this->_mappedColumns[$columnName])) {
- unset($this->_mappedColumns[$columnName]);
+ if (isset($this->_fieldMappings[$fieldName])) {
+ unset($this->_fieldMappings[$fieldName]);
return true;
}
$this->_columnCount--;
@@ -1040,7 +1039,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
public function getColumnNames(array $fieldNames = null)
{
if ($fieldNames === null) {
- return array_keys($this->_mappedColumns);
+ return array_keys($this->_fieldNames);
} else {
$columnNames = array();
foreach ($fieldNames as $fieldName) {
@@ -1092,8 +1091,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function getMappingForField($fieldName)
{
- $columnName = $this->getColumnName($fieldName);
- return $this->getColumnDefinition($columnName);
+ return $this->_fieldMappings[$fieldName];
}
/**
@@ -1104,6 +1102,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function getTypeOf($fieldName)
{
+
return $this->getTypeOfColumn($this->getColumnName($fieldName));
}
@@ -1115,7 +1114,8 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function getTypeOfField($fieldName)
{
- return $this->getTypeOfColumn($this->getColumnName($fieldName));
+ return isset($this->_fieldMappings[$fieldName]) ?
+ $this->_fieldMappings[$fieldName]['type'] : false;
}
/**
@@ -1125,7 +1125,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function getTypeOfColumn($columnName)
{
- return isset($this->_mappedColumns[$columnName]) ? $this->_mappedColumns[$columnName]['type'] : false;
+ return $this->getTypeOfField($this->getFieldName($columnName));
}
/**
@@ -1133,7 +1133,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function getFieldLength($fieldName)
{
- return $this->_mappedColumns[$this->getColumnName($fieldName)]['length'];
+ return $this->_fieldMappings[$fieldName]['length'];
}
/**
@@ -1502,7 +1502,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
// Collect foreign keys from the relations
$options['foreignKeys'] = array();
if ($parseForeignKeys && $this->getAttribute(Doctrine::ATTR_EXPORT)
- & Doctrine::EXPORT_CONSTRAINTS) {
+ & Doctrine::EXPORT_CONSTRAINTS) {
$constraints = array();
$emptyIntegrity = array('onUpdate' => null, 'onDelete' => null);
foreach ($this->getRelations() as $name => $relation) {
@@ -1683,7 +1683,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function isInheritedField($fieldName)
{
- return isset($this->_mappedColumns[$this->getColumnName($fieldName)]['inherited']);
+ return isset($this->_fieldMappings[$fieldName]['inherited']);
}
/**
@@ -1912,6 +1912,86 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
{
}
+
+ /**
+ * Dispatches the lifecycle event of the given Entity to the registered
+ * lifecycle callbacks and lifecycle listeners.
+ *
+ * @param string $event The lifecycle event.
+ * @param Entity $entity The Entity on which the event occured.
+ */
+ public function invokeLifecycleCallbacks($lifecycleEvent, Doctrine_Entity $entity)
+ {
+ foreach ($this->getLifecycleCallbacks($lifecycleEvent) as $callback) {
+ $entity->$callback();
+ }
+ foreach ($this->getLifecycleListeners($lifecycleEvent) as $className => $callback) {
+ if ( ! isset($this->_lifecycleListenerInstances[$className])) {
+ $this->_lifecycleListenerInstances[$className] = new $className;
+ }
+ $this->_lifecycleListenerInstances[$className]->$callback($entity);
+ }
+ }
+
+ /**
+ * Gets the registered lifecycle callbacks for an event.
+ *
+ * @param string $event
+ * @return array
+ */
+ public function getLifecycleCallbacks($event)
+ {
+ return isset($this->_lifecycleCallbacks[$event]) ?
+ $this->_lifecycleCallbacks[$event] : array();
+ }
+
+ /**
+ * Gets the registered lifecycle listeners for an event.
+ *
+ * @param string $event
+ * @return array
+ */
+ public function getLifecycleListeners($event)
+ {
+ return isset($this->_lifecycleListeners[$event]) ?
+ $this->_lifecycleListeners[$event] : array();
+ }
+
+ /**
+ * Adds a lifecycle listener for Entities this class.
+ *
+ * Note: If the same listener class is registered more than once, the old
+ * one will be overridden.
+ *
+ * @param string $listenerClass
+ * @param array $callbacks
+ */
+ public function addLifecycleListener($listenerClass, array $callbacks)
+ {
+ $this->_lifecycleListeners[$event][$listenerClass] = array();
+ foreach ($callbacks as $method => $event) {
+ $this->_lifecycleListeners[$event][$listenerClass][] = $method;
+ }
+ }
+
+ /**
+ * Adds a lifecycle callback for Entities of this class.
+ *
+ * Note: If a the same callback is registered more than once, the old one
+ * will be overridden.
+ *
+ * @param string $callback
+ * @param string $event
+ */
+ public function addLifecycleCallback($callback, $event)
+ {
+ if ( ! isset($this->_lifecycleCallbacks[$event])) {
+ $this->_lifecycleCallbacks[$event] = array();
+ }
+ if ( ! in_array($callback, $this->_lifecycleCallbacks[$event])) {
+ $this->_lifecycleCallbacks[$event][$callback] = $callback;
+ }
+ }
/**
* @todo Implementation. Immutable entities can not be updated or deleted once
diff --git a/lib/Doctrine/ClassMetadata/CodeDriver.php b/lib/Doctrine/ClassMetadata/CodeDriver.php
index c60477bb2..426e85644 100644
--- a/lib/Doctrine/ClassMetadata/CodeDriver.php
+++ b/lib/Doctrine/ClassMetadata/CodeDriver.php
@@ -34,12 +34,19 @@
*/
class Doctrine_ClassMetadata_CodeDriver
{
+ /**
+ * Name of the callback method.
+ *
+ * @todo We could make the name of the callback methods customizable for users.
+ */
+ const CALLBACK_METHOD = 'initMetadata';
+
/**
* Loads the metadata for the specified class into the provided container.
*/
public function loadMetadataForClass($className, Doctrine_ClassMetadata $metadata)
{
- if ( ! method_exists($className, 'initMetadata')) {
+ if ( ! method_exists($className, self::CALLBACK_METHOD)) {
throw new Doctrine_ClassMetadata_Exception("Unable to load metadata for class"
. " '$className'. Callback method 'initMetadata' not found.");
}
diff --git a/lib/Doctrine/ClassMetadata/Factory.php b/lib/Doctrine/ClassMetadata/Factory.php
index 23134a850..d19eca367 100644
--- a/lib/Doctrine/ClassMetadata/Factory.php
+++ b/lib/Doctrine/ClassMetadata/Factory.php
@@ -133,7 +133,10 @@ class Doctrine_ClassMetadata_Factory
foreach ($parentClass->getColumns() as $name => $definition) {
$fullName = "$name as " . $parentClass->getFieldName($name);
$definition['inherited'] = true;
- $subClass->mapColumn($fullName, $definition['type'], $definition['length'],
+ $subClass->mapColumn(
+ $fullName,
+ $definition['type'],
+ $definition['length'],
$definition);
}
}
@@ -154,11 +157,6 @@ class Doctrine_ClassMetadata_Factory
protected function _loadMetadata(Doctrine_ClassMetadata $class, $name)
{
if ( ! class_exists($name) || empty($name)) {
- /*try {
- throw new Exception();
- } catch (Exception $e) {
- echo $e->getTraceAsString();
- }*/
throw new Doctrine_Exception("Couldn't find class " . $name . ".");
}
@@ -175,11 +173,6 @@ class Doctrine_ClassMetadata_Factory
} while ($className = get_parent_class($className));
if ($className === false) {
- try {
- throw new Exception();
- } catch (Exception $e) {
- echo $e->getTraceAsString() . "
";
- }
throw new Doctrine_ClassMetadata_Factory_Exception("Unknown component '$className'.");
}
@@ -207,10 +200,11 @@ class Doctrine_ClassMetadata_Factory
protected function _initIdentifier(Doctrine_ClassMetadata $class)
{
switch (count((array)$class->getIdentifier())) {
- case 0:
+ case 0: // No identifier in the class mapping yet
+
+ // If its a subclass, inherit the identifier from the parent.
if ($class->getInheritanceType() == Doctrine::INHERITANCE_TYPE_JOINED &&
- count($class->getParentClasses()) > 0) {
-
+ count($class->getParentClasses()) > 0) {
$parents = $class->getParentClasses();
$root = end($parents);
$rootClass = $class->getConnection()->getMetadata($root);
@@ -237,17 +231,20 @@ class Doctrine_ClassMetadata_Factory
$definition, true);
}
} else {
+ throw Doctrine_MappingException::identifierRequired($class->getClassName());
+ /* Legacy behavior of auto-adding an id field
$definition = array('type' => 'integer',
'length' => 20,
'autoincrement' => true,
'primary' => true);
- $class->setColumn('id', $definition['type'], $definition['length'], $definition, true);
+ $class->mapColumn('id', $definition['type'], $definition['length'], $definition, true);
$class->setIdentifier(array('id'));
$class->setIdentifierType(Doctrine::IDENTIFIER_AUTOINC);
+ */
}
break;
- case 1:
- foreach ((array)$class->getIdentifier() as $pk) {
+ case 1: // A single identifier is in the mapping
+ foreach ($class->getIdentifier() as $pk) {
$columnName = $class->getColumnName($pk);
$thisColumns = $class->getColumns();
$e = $thisColumns[$columnName];
@@ -294,7 +291,7 @@ class Doctrine_ClassMetadata_Factory
$class->setIdentifier(array($pk));
break;
- default:
+ default: // Multiple identifiers are in the mapping so its a composite id
$class->setIdentifierType(Doctrine::IDENTIFIER_COMPOSITE);
}
}
diff --git a/lib/Doctrine/Connection/UnitOfWork.php b/lib/Doctrine/Connection/UnitOfWork.php
index 9eb7a4494..864cd918f 100644
--- a/lib/Doctrine/Connection/UnitOfWork.php
+++ b/lib/Doctrine/Connection/UnitOfWork.php
@@ -83,7 +83,7 @@ class Doctrine_Connection_UnitOfWork
protected $_removedEntities = array();
/**
- * The EntityManager the unit of work belongs to.
+ * The EntityManager the UnitOfWork belongs to.
*/
protected $_em;
@@ -98,7 +98,7 @@ class Doctrine_Connection_UnitOfWork
/**
* Constructor.
- * Created a new UnitOfWork.
+ * Creates a new UnitOfWork.
*
* @param Doctrine_EntityManager $em
*/
@@ -110,7 +110,8 @@ class Doctrine_Connection_UnitOfWork
/**
* Commits the unit of work, executing all operations that have been postponed
* up to this point.
- *
+ *
+ * @todo Impl
*/
public function commit()
{
@@ -191,7 +192,7 @@ class Doctrine_Connection_UnitOfWork
*/
public function registerRemoved(Doctrine_Entity $entity)
{
- if ($entity->isTransient()) {
+ if ($entity->isNew()) {
return;
}
$this->unregisterIdentity($entity);
@@ -511,4 +512,229 @@ class Doctrine_Connection_UnitOfWork
return isset($this->_identityMap[$rootClassName][$idHash]);
}
+ public function save(Doctrine_Entity $entity)
+ {
+ switch ($entity->_state()) {
+ case Doctrine_Entity::STATE_CLEAN:
+ //nothing to do
+ // ignore $entity but cascade
+ break;
+ case Doctrine_Entity::STATE_DIRTY:
+ // update
+ $this->registerDirty($entity);
+ // todo:cascade
+ break;
+ case Doctrine_Entity::STATE_TCLEAN:
+ case Doctrine_Entity::STATE_TDIRTY:
+ // insert
+ // if identifier type IDENTITY:
+ // cascade
+ // if no transaction is started yet, do it
+ // force insert (directly to persister)
+ // else
+ // cascade
+ // get & assign the identifier, then registerNew()
+ break;
+ }
+ }
+
+ private function _cascadeSave(Doctrine_Entity $entity)
+ {
+
+ }
+
+ private function _cascadeDelete(Doctrine_Entity $entity)
+ {
+
+ }
+
+
+ // 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
+ * application-level delete cascades.
+ *
+ * @param array $deletions Map of the records to delete. Keys=Oids Values=Records.
+ */
+ /*private function _collectDeletions(Doctrine_Record $record, array &$deletions)
+ {
+ if ( ! $record->exists()) {
+ return;
+ }
+
+ $deletions[$record->getOid()] = $record;
+ $this->_cascadeDelete($record, $deletions);
+ }*/
+
+ /**
+ * Cascades an ongoing delete operation to related objects. Applies only on relations
+ * that have 'delete' in their cascade options.
+ * 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.
+ * @throws PDOException If something went wrong at database level
+ * @return void
+ */
+ /*protected function _cascadeDelete(Doctrine_Record $record, array &$deletions)
+ {
+ foreach ($record->getTable()->getRelations() as $relation) {
+ if ($relation->isCascadeDelete()) {
+ $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 {
+ $sql .= $this->_buildSqlSingleKeyCondition($columnNames, count($params));
+ $this->conn->exec($sql, $params);
+ }
+
+ // adjust state, remove from identity map and inform postDelete listeners
+ foreach ($deletedRecords as $record) {
+ // 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
+ * primary key.
+ *
+ * @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 "pk = ? OR pk = ? OR pk = ? ..."
+ */
+ /*private function _buildSqlSingleKeyCondition($columnNames, $numRecords)
+ {
+ $idColumn = $this->conn->quoteIdentifier($columnNames[0]);
+ return implode(' OR ', array_fill(0, $numRecords, "$idColumn = ?"));
+ }*/
+
+ /**
+ * Builds the SQL condition to target multiple records who have a composite primary key.
+ *
+ * @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 = ?) ..."
+ */
+ /*private function _buildSqlCompositeKeyCondition($columnNames, $numRecords)
+ {
+ $singleCondition = "";
+ foreach ($columnNames as $columnName) {
+ $columnName = $this->conn->quoteIdentifier($columnName);
+ if ($singleCondition === "") {
+ $singleCondition .= "($columnName = ?";
+ } else {
+ $singleCondition .= " AND $columnName = ?";
+ }
+ }
+ $singleCondition .= ")";
+ $fullCondition = implode(' OR ', array_fill(0, $numRecords, $singleCondition));
+
+ return $fullCondition;
+ }*/
}
+
+
+
+
diff --git a/lib/Doctrine/Entity.php b/lib/Doctrine/Entity.php
index a82e6ba4c..2fcfe95d8 100644
--- a/lib/Doctrine/Entity.php
+++ b/lib/Doctrine/Entity.php
@@ -22,8 +22,8 @@
#namespace Doctrine::ORM;
/**
- * Doctrine_Entity
- * All record classes should inherit this super class.
+ * Base class for all Entities (objects with persistent state in a RDBMS that are
+ * managed by Doctrine).
*
* 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
@@ -37,8 +37,10 @@
* @link www.phpdoctrine.org
* @since 2.0
* @version $Revision: 4342 $
- * @todo Split up into "Entity" and "ActiveEntity (extends Entity)"???
- * @todo Remove as many methods as possible.
+ * @todo Split up into "Entity" and "ActiveEntity" (extends Entity)
+ * @todo Move entity states into a separate enumeration (EntityStates).
+ * They do not need to be exposed to users in such a way. The states are mainly
+ * for internal use.
*/
abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
{
@@ -47,6 +49,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
* An Entity is in dirty state when its properties are changed.
*/
const STATE_DIRTY = 1;
+ const STATE_MANAGED_DIRTY = 1;
/**
* TDIRTY STATE
@@ -54,6 +57,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
* fields are modified but it is NOT yet persisted into database.
*/
const STATE_TDIRTY = 2;
+ const STATE_NEW_DIRTY = 2;
/**
* CLEAN STATE
@@ -61,19 +65,17 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
* and none of its properties are changed.
*/
const STATE_CLEAN = 3;
-
- /**
- * PROXY STATE
- * An Entity is in proxy state when its properties are not fully loaded.
- */
- //const STATE_PROXY = 4;
+ const STATE_MANAGED_CLEAN = 3;
/**
* NEW TCLEAN
* An Entity is in transient clean state when it is created and none of its
* fields are modified.
+ * @todo Do we need this state? Just STATE_NEW may be enough without differentiating
+ * clean/dirty. A new entity is always "dirty".
*/
const STATE_TCLEAN = 5;
+ const STATE_NEW_CLEAN = 5;
/**
* LOCKED STATE
@@ -81,9 +83,25 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
*
* 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 = 7;
+
+ /**
+ * A removed Entity instance is an instance with a persistent identity,
+ * associated with an EntityManager, that is scheduled for removal from the
+ * database.
+ */
+ const STATE_DELETED = 8;
+
/**
* Index used for creating object identifiers (oid's).
*
@@ -204,114 +222,16 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
// @todo read from attribute the first time and move this initialization elsewhere.
self::$_useAutoAccessorOverride = true;
}
-
- /**
- * _index
- *
- * @return integer
- */
- /*public static function _index()
- {
- return self::$_index;
- }*/
/**
* Returns the object identifier.
*
* @return integer
*/
- public function getOid()
+ final public function getOid()
{
return $this->_oid;
}
-
- /**
- * Empty template method to provide concrete Record classes with the possibility
- * to hook into the serializing procedure.
- */
- public function preSerialize()
- { }
-
- /**
- * Empty template method to provide concrete Record classes with the possibility
- * to hook into the serializing procedure.
- */
- public function postSerialize()
- { }
-
- /**
- * Empty template method to provide concrete Record classes with the possibility
- * to hook into the serializing procedure.
- */
- public function preUnserialize()
- { }
-
- /**
- * Empty template method to provide concrete Record classes with the possibility
- * to hook into the serializing procedure.
- */
- public function postUnserialize()
- { }
-
- /**
- * Empty template method to provide concrete Record classes with the possibility
- * to hook into the saving procedure.
- */
- public function preSave()
- { }
-
- /**
- * Empty template method to provide concrete Record classes with the possibility
- * to hook into the saving procedure.
- */
- public function postSave()
- { }
-
- /**
- * Empty template method to provide concrete Record classes with the possibility
- * to hook into the deletion procedure.
- */
- public function preDelete()
- { }
-
- /**
- * Empty template method to provide concrete Record classes with the possibility
- * to hook into the deletion procedure.
- */
- public function postDelete()
- { }
-
- /**
- * Empty template method to provide concrete Record classes with the possibility
- * to hook into the saving procedure only when the record is going to be
- * updated.
- */
- public function preUpdate()
- { }
-
- /**
- * Empty template method to provide concrete Record classes with the possibility
- * to hook into the saving procedure only when the record is going to be
- * updated.
- */
- public function postUpdate()
- { }
-
- /**
- * Empty template method to provide concrete Record classes with the possibility
- * to hook into the saving procedure only when the record is going to be
- * inserted into the data store the first time.
- */
- public function preInsert()
- { }
-
- /**
- * Empty template method to provide concrete Record classes with the possibility
- * to hook into the saving procedure only when the record is going to be
- * inserted into the data store the first time.
- */
- public function postInsert()
- { }
/**
* setDefaultValues
@@ -345,46 +265,15 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
}*/
/**
- * cleanData
- * leaves the $data array only with values whose key is a field inside this
- * record and returns the values that were removed from $data. Also converts
- * any values of 'null' to objects of type Doctrine_Null.
- *
- * @param array $data data array to be cleaned
- * @return array $tmp values cleaned from data
- * @todo Remove. Should not be necessary. Slows down instantiation.
- */
- /*public function cleanData(&$data)
- {
- $tmp = $data;
- $data = array();
-
- $fieldNames = $this->_em->getEntityPersister($this->_entityName)->getFieldNames();
- foreach ($fieldNames as $fieldName) {
- if (isset($tmp[$fieldName])) {
- $data[$fieldName] = $tmp[$fieldName];
- } else if (array_key_exists($fieldName, $tmp)) {
- $data[$fieldName] = Doctrine_Null::$INSTANCE;
- } else if ( ! isset($this->_data[$fieldName])) {
- $data[$fieldName] = Doctrine_Null::$INSTANCE;
- }
- unset($tmp[$fieldName]);
- }
-
- return $tmp;
- }*/
-
- /**
- * hydrate
* hydrates this object from given array
*
* @param array $data
* @return boolean
*/
- public function hydrate(array $data)
+ final public function hydrate(array $data)
{
$this->_data = array_merge($this->_data, $data);
- //$this->_extractIdentifier(true);
+ $this->_extractIdentifier();
}
/**
@@ -437,8 +326,8 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
*/
public function serialize()
{
- //$event = new Doctrine_Event($this, Doctrine_Event::RECORD_SERIALIZE);
- //$this->preSerialize($event);
+ //$this->_em->getEventManager()->dispatchEvent(Event::preSerialize);
+ //$this->_class->dispatchLifecycleEvent(Event::preSerialize, $this);
$vars = get_object_vars($this);
@@ -533,7 +422,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
/**
* INTERNAL:
- * returns / assigns the state of this record
+ * 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
@@ -577,7 +466,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
* this record represents does not exist anymore)
* @return boolean
* @todo Implementation to EntityManager.
- * @todo ActiveEntity method.
+ * @todo Move to ActiveEntity (extends Entity).
*/
public function refresh($deep = false)
{
@@ -674,9 +563,10 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
}*/
/**
- * INTERNAL: (Usage from within extending classes is fine)
+ * INTERNAL: (Usage from within extending classes is intended)
+ *
* Gets the value of a field (regular field or reference).
- * If the property is not yet loaded this method does NOT load it.
+ * If the field is not yet loaded this method does NOT load it.
*
* NOTE: Use of this method from outside the scope of an extending class
* is strongly discouraged.
@@ -684,6 +574,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
* @param $name name of the property
* @throws Doctrine_Entity_Exception if trying to get an unknown field
* @return mixed
+ * @todo Rename to _get()
*/
final public function _rawGet($fieldName)
{
@@ -697,7 +588,8 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
}
/**
- * INTERNAL: (Usage from within extending classes is fine)
+ * INTERNAL: (Usage from within extending classes is intended)
+ *
* Sets the value of a field (regular field or reference).
* If the field is not yet loaded this method does NOT load it.
*
@@ -707,6 +599,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
* @param $name name of the field
* @throws Doctrine_Entity_Exception if trying to get an unknown field
* @return mixed
+ * @todo Rename to _set
*/
final public function _rawSet($fieldName, $value)
{
@@ -805,7 +698,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
}
if (isset($this->_references[$name])) {
$this->_references[$name]->setData($value->getData());
- return $this;
+ return;
}
} else {
$relatedTable = $value->getTable();
@@ -819,12 +712,12 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
if ($rel instanceof Doctrine_Relation_LocalKey) {
$idFieldNames = $value->getTable()->getIdentifier();
if ( ! empty($foreignFieldName) && $foreignFieldName != $idFieldNames[0]) {
- $this->set($localFieldName, $value->_rawGet($foreignFieldName), false);
+ $this->set($localFieldName, $value->_rawGet($foreignFieldName));
} else {
- $this->set($localFieldName, $value, false);
+ $this->set($localFieldName, $value);
}
} else {
- $value->set($foreignFieldName, $this, false);
+ $value->set($foreignFieldName, $this);
}
}
} else if ($rel instanceof Doctrine_Relation_Association) {
@@ -894,6 +787,13 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
}
}
+ /**
+ * 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])) {
@@ -916,6 +816,13 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
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])) {
@@ -952,10 +859,6 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
*
* @param mixed $name name of the property or reference
* @param mixed $value value of the property or reference
- * @param boolean $load whether or not to refresh / load the uninitialized record data
- *
- * @throws Doctrine_Record_Exception if trying to set a value for unknown property / related component
- * @throws Doctrine_Record_Exception if trying to set a value of wrong type for related component
*/
final public function set($fieldName, $value)
{
@@ -978,9 +881,9 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
if ($old != $value) {
$this->_data[$fieldName] = $value;
- $this->_modified[] = $fieldName;
+ $this->_modified[$fieldName] = array($old => $value);
- if ($this->isTransient() && $this->_class->isIdentifier($fieldName)) {
+ if ($this->isNew() && $this->_class->isIdentifier($fieldName)) {
$this->_id[$fieldName] = $value;
}
@@ -1001,7 +904,9 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
}
/**
- * contains
+ * Checks whether a field is set (not null).
+ *
+ * NOTE: Invoked by Doctrine::ORM::Access#__isset().
*
* @param string $name
* @return boolean
@@ -1025,6 +930,10 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
}
/**
+ * Clears the value of a field.
+ *
+ * NOTE: Invoked by Doctrine::ORM::Access#__unset().
+ *
* @param string $name
* @return void
*/
@@ -1220,8 +1129,8 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
}
/**
- * Merges this record with an array of values
- * or with another existing instance of this object
+ * Merges this Entity with an array of values
+ * or with another existing instance of.
*
* @param mixed $data Data to merge. Either another instance of this model or an array
* @param bool $deep Bool value for whether or not to merge the data deep
@@ -1341,41 +1250,17 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
* Checks whether the entity already has a persistent state.
*
* @return boolean TRUE if the object is new, FALSE otherwise.
- * @deprecated Use isTransient()
*/
final public function isNew()
{
return $this->_state == self::STATE_TCLEAN || $this->_state == self::STATE_TDIRTY;
}
-
- /**
- * Checks whether the entity already has a persistent state.
- *
- * @return boolean TRUE if the object is new, FALSE otherwise.
- */
- final public function isTransient()
- {
- return $this->_state == self::STATE_TCLEAN || $this->_state == self::STATE_TDIRTY;
- }
-
- /**
- * 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 isDirty()
- {
- return ($this->_state === Doctrine_Entity::STATE_DIRTY ||
- $this->_state === Doctrine_Entity::STATE_TDIRTY);
- }
/**
* 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.
- * @deprecated Use isDirty()
*/
final public function isModified()
{
@@ -1512,7 +1397,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
* @return boolean
* @todo Better name? hasAssociation() ?
*/
- public function hasReference($name)
+ final public function hasReference($name)
{
return isset($this->_references[$name]);
}
@@ -1523,7 +1408,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
* @param string $name
* @throws Doctrine_Record_Exception if trying to get an unknown related component
*/
- public function obtainReference($name)
+ final public function obtainReference($name)
{
if (isset($this->_references[$name])) {
return $this->_references[$name];
@@ -1774,6 +1659,8 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
/**
* Gets the ClassMetadata object that describes the entity class.
+ *
+ * @return Doctrine::ORM::Mapping::ClassMetadata
*/
final public function getClassMetadata()
{
@@ -1781,9 +1668,10 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
}
/**
- * Enter description here...
+ * Gets the EntityManager that is responsible for the persistence of
+ * the Entity.
*
- * @return unknown
+ * @return Doctrine::ORM::EntityManager
*/
final public function getEntityManager()
{
@@ -1791,9 +1679,9 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
}
/**
- * Enter description here...
+ * Gets the EntityRepository of the Entity.
*
- * @return unknown
+ * @return Doctrine::ORM::EntityRepository
*/
final public function getRepository()
{
diff --git a/lib/Doctrine/EntityManager.php b/lib/Doctrine/EntityManager.php
index f00041b86..973d2cd92 100644
--- a/lib/Doctrine/EntityManager.php
+++ b/lib/Doctrine/EntityManager.php
@@ -31,10 +31,8 @@
/**
- * The EntityManager is a central access point to ORM functionality.
+ * The EntityManager is the central access point to ORM functionality.
*
- * @package Doctrine
- * @subpackage EntityManager
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.org
* @since 2.0
@@ -88,7 +86,6 @@ class Doctrine_EntityManager
/**
* The EntityPersister instances.
- * @todo Implementation.
*
* @var array
*/
@@ -109,7 +106,7 @@ class Doctrine_EntityManager
private $_flushMode = 'commit';
/**
- * The unit of work.
+ * The unit of work used to coordinate object-level transactions.
*
* @var UnitOfWork
*/
@@ -122,13 +119,6 @@ class Doctrine_EntityManager
*/
private $_eventManager;
- /**
- * Enter description here...
- *
- * @var unknown_type
- */
- //private $_dataTemplates = array();
-
/**
* Container that is used temporarily during hydration.
*
@@ -163,7 +153,7 @@ class Doctrine_EntityManager
}
/**
- * Returns the metadata for a class. Alias for getClassMetadata().
+ * Gets the metadata for a class. Alias for getClassMetadata().
*
* @return Doctrine_Metadata
* @todo package:orm
@@ -172,6 +162,17 @@ class Doctrine_EntityManager
{
return $this->getClassMetadata($className);
}
+
+ /**
+ * Gets the transaction object used by the EntityManager to manage
+ * database transactions.
+ *
+ * @return Doctrine::DBAL::Transaction
+ */
+ public function getTransaction()
+ {
+ return $this->_conn->getTransaction();
+ }
/**
* Returns the metadata for a class.
@@ -184,8 +185,8 @@ class Doctrine_EntityManager
}
/**
- * Sets the driver that is used to obtain metadata informations about entity
- * classes.
+ * Sets the driver that is used to obtain metadata mapping information
+ * about Entities.
*
* @param $driver The driver to use.
*/
@@ -197,7 +198,8 @@ class Doctrine_EntityManager
/**
* Creates a new Doctrine_Query object that operates on this connection.
*
- * @return Doctrine_Query
+ * @param string The DQL string.
+ * @return Doctrine::ORM::Query
* @todo package:orm
*/
public function createQuery($dql = "")
@@ -211,10 +213,12 @@ class Doctrine_EntityManager
}
/**
- * Enter description here...
+ * Gets the EntityPersister for an Entity.
+ *
+ * This is usually not of interest for users, mainly for internal use.
*
- * @param unknown_type $entityName
- * @return unknown
+ * @param string $entityName The name of the Entity.
+ * @return Doctrine::ORM::Internal::EntityPersister
*/
public function getEntityPersister($entityName)
{
@@ -241,30 +245,6 @@ class Doctrine_EntityManager
return $this->_unitOfWork->unregisterIdentity($entity);
}
- /**
- * Returns the current internal transaction nesting level.
- *
- * @return integer The nesting level. A value of 0 means theres no active transaction.
- * @todo package:orm???
- */
- public function getInternalTransactionLevel()
- {
- return $this->transaction->getInternalTransactionLevel();
- }
-
- /**
- * Initiates a transaction.
- *
- * This method must only be used by Doctrine itself to initiate transactions.
- * Userland-code must use {@link beginTransaction()}.
- *
- * @todo package:orm???
- */
- public function beginInternalTransaction($savepoint = null)
- {
- return $this->transaction->beginInternalTransaction($savepoint);
- }
-
/**
* Creates a query with the specified name.
*
@@ -307,16 +287,16 @@ class Doctrine_EntityManager
*/
public function flush()
{
- $this->beginInternalTransaction();
$this->_unitOfWork->flush();
- $this->commit();
}
/**
- * Enter description here...
+ * Finds an Entity by its identifier.
+ * This is just a convenient shortcut for getRepository()->find().
*
- * @param unknown_type $entityName
- * @param unknown_type $identifier
+ * @param string $entityName
+ * @param mixed $identifier
+ * @return Doctrine::ORM::Entity
*/
public function find($entityName, $identifier)
{
@@ -356,11 +336,8 @@ class Doctrine_EntityManager
{
if ($entityName === null) {
$this->_unitOfWork->detachAll();
- foreach ($this->_mappers as $mapper) {
- $mapper->clear(); // clear identity map of each mapper
- }
} else {
- $this->getMapper($entityName)->clear();
+ //...
}
}
@@ -370,7 +347,7 @@ class Doctrine_EntityManager
*/
public function close()
{
-
+ //Doctrine_EntityManagerFactory::releaseManager($this);
}
/**
@@ -408,24 +385,7 @@ class Doctrine_EntityManager
*/
public function save(Doctrine_Entity $entity)
{
- $state = $entity->_state();
- if ($state == Doctrine_Entity::STATE_CLEAN || $state == Doctrine_Entity::STATE_LOCKED) {
- return;
- }
-
- //...
- //$this->_unitOfWork->
- switch ($entity->_state()) {
- case Doctrine_Entity::STATE_CLEAN:
- //nothing to do
- break;
- case Doctrine_Entity::STATE_DIRTY:
- $this->_unitOfWork->registerDirty($entity);
- break;
- case Doctrine_Entity::STATE_TCLEAN:
- case Doctrine_Entity::STATE_TDIRTY:
- //...
- }
+ $this->_unitOfWork->save($entity);
}
/**
@@ -433,14 +393,14 @@ class Doctrine_EntityManager
*/
public function delete(Doctrine_Entity $entity)
{
- //...
+ $this->_unitOfWork->delete($entity);
}
/**
- * Gets the repository for the given entity name.
+ * Gets the repository for an Entity.
*
- * @return Doctrine_EntityRepository The repository.
- * @todo Implementation.
+ * @param string $entityName The name of the Entity.
+ * @return Doctrine::ORM::EntityRepository The repository.
*/
public function getRepository($entityName)
{
diff --git a/lib/Doctrine/EntityPersister/Abstract.php b/lib/Doctrine/EntityPersister/Abstract.php
index adaa86e9a..d078ff725 100644
--- a/lib/Doctrine/EntityPersister/Abstract.php
+++ b/lib/Doctrine/EntityPersister/Abstract.php
@@ -62,21 +62,11 @@ abstract class Doctrine_EntityPersister_Abstract
*/
protected $_em;
- /**
- * The concrete mapping strategy that is used.
- */
- protected $_mappingStrategy;
-
/**
* Null object.
*/
private $_nullObject;
- /**
- * A list of registered entity listeners.
- */
- private $_entityListeners = array();
-
/**
* Enter description here...
*
@@ -318,8 +308,8 @@ abstract class Doctrine_EntityPersister_Abstract
*/
protected function _insertOrUpdate(Doctrine_Entity $record)
{
- $record->preSave();
- $this->notifyEntityListeners($record, 'preSave', Doctrine_Event::RECORD_SAVE);
+ //$record->preSave();
+ //$this->notifyEntityListeners($record, 'preSave', Doctrine_Event::RECORD_SAVE);
switch ($record->_state()) {
case Doctrine_Entity::STATE_TDIRTY:
@@ -335,8 +325,8 @@ abstract class Doctrine_EntityPersister_Abstract
break;
}
- $record->postSave();
- $this->notifyEntityListeners($record, 'postSave', Doctrine_Event::RECORD_SAVE);
+ //$record->postSave();
+ //$this->notifyEntityListeners($record, 'postSave', Doctrine_Event::RECORD_SAVE);
}
/**
@@ -351,7 +341,6 @@ abstract class Doctrine_EntityPersister_Abstract
}
/**
- * _saveRelated
* saves all related records to $record
*
* @throws PDOException if something went wrong at database level
diff --git a/lib/Doctrine/EntityPersister/JoinedSubclass.php b/lib/Doctrine/EntityPersister/JoinedSubclass.php
index 9b8b706a2..1e48ce1a6 100644
--- a/lib/Doctrine/EntityPersister/JoinedSubclass.php
+++ b/lib/Doctrine/EntityPersister/JoinedSubclass.php
@@ -20,12 +20,10 @@
*/
/**
- * The joined mapping strategy maps a single entity instance to several tables in the
+ * The joined subclass persister maps a single entity instance to several tables in the
* database as it is defined by Class Table Inheritance.
*
* @author Roman Borschel
- * @package Doctrine
- * @subpackage JoinedSubclass
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @version $Revision$
* @link www.phpdoctrine.org
@@ -33,7 +31,7 @@
*/
class Doctrine_EntityPersister_JoinedSubclass extends Doctrine_EntityPersister_Abstract
{
- protected $_columnNameFieldNameMap = array();
+ //protected $_columnNameFieldNameMap = array();
/**
* Inserts an entity that is part of a Class Table Inheritance hierarchy.
@@ -94,7 +92,6 @@ class Doctrine_EntityPersister_JoinedSubclass extends Doctrine_EntityPersister_A
*
* @param Doctrine_Entity $record record to be updated
* @return boolean whether or not the update was successful
- * @todo Move to Doctrine_Table (which will become Doctrine_Mapper).
*/
protected function _doUpdate(Doctrine_Entity $record)
{
@@ -223,34 +220,6 @@ class Doctrine_EntityPersister_JoinedSubclass extends Doctrine_EntityPersister_A
return $fieldNames;
}
- /**
- *
- */
- /*public function getFieldName($columnName)
- {
- if (isset($this->_columnNameFieldNameMap[$columnName])) {
- return $this->_columnNameFieldNameMap[$columnName];
- }
-
- $classMetadata = $this->_classMetadata;
- $conn = $this->_conn;
-
- if ($classMetadata->hasColumn($columnName)) {
- $this->_columnNameFieldNameMap[$columnName] = $classMetadata->getFieldName($columnName);
- return $this->_columnNameFieldNameMap[$columnName];
- }
-
- foreach ($classMetadata->getSubclasses() as $subClass) {
- $subTable = $conn->getClassMetadata($subClass);
- if ($subTable->hasColumn($columnName)) {
- $this->_columnNameFieldNameMap[$columnName] = $subTable->getFieldName($columnName);
- return $this->_columnNameFieldNameMap[$columnName];
- }
- }
-
- throw new Doctrine_Mapper_Exception("No field name found for column name '$columnName'.");
- }*/
-
/**
*
* @todo Looks like this better belongs into the ClassMetadata class.
diff --git a/lib/Doctrine/EntityRepository.php b/lib/Doctrine/EntityRepository.php
index a037721b4..496addf48 100644
--- a/lib/Doctrine/EntityRepository.php
+++ b/lib/Doctrine/EntityRepository.php
@@ -22,6 +22,7 @@
#namespace Doctrine::ORM;
/**
+ * A repository provides the illusion of an in-memory Entity store.
* Base class for all custom user-defined repositories.
* Provides basic finder methods, common to all repositories.
*
@@ -48,7 +49,6 @@ class Doctrine_EntityRepository
}
/**
- * createQuery
* creates a new Doctrine_Query object and adds the component name
* of this table as the query 'from' part
*
diff --git a/lib/Doctrine/EventListener.php b/lib/Doctrine/EventListener.php
index 7afc68f21..6a4c4c9bc 100644
--- a/lib/Doctrine/EventListener.php
+++ b/lib/Doctrine/EventListener.php
@@ -29,93 +29,11 @@
* @subpackage EventListener
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.org
- * @since 1.0
+ * @since 2.0
* @version $Revision$
* @todo Remove. The 2.0 event system has no listener interfaces.
*/
-class Doctrine_EventListener implements Doctrine_EventListener_Interface
+interface Doctrine_EventSubscriber
{
- public function preClose(Doctrine_Event $event)
- { }
- public function postClose(Doctrine_Event $event)
- { }
-
- public function onCollectionDelete(Doctrine_Collection $collection)
- { }
- public function onPreCollectionDelete(Doctrine_Collection $collection)
- { }
-
- public function onOpen(Doctrine_Connection $connection)
- { }
-
- public function preTransactionCommit(Doctrine_Event $event)
- { }
- public function postTransactionCommit(Doctrine_Event $event)
- { }
-
- public function preTransactionRollback(Doctrine_Event $event)
- { }
- public function postTransactionRollback(Doctrine_Event $event)
- { }
-
- public function preTransactionBegin(Doctrine_Event $event)
- { }
- public function postTransactionBegin(Doctrine_Event $event)
- { }
-
-
- public function preSavepointCommit(Doctrine_Event $event)
- { }
- public function postSavepointCommit(Doctrine_Event $event)
- { }
-
- public function preSavepointRollback(Doctrine_Event $event)
- { }
- public function postSavepointRollback(Doctrine_Event $event)
- { }
-
- public function preSavepointCreate(Doctrine_Event $event)
- { }
- public function postSavepointCreate(Doctrine_Event $event)
- { }
-
- public function postConnect(Doctrine_Event $event)
- { }
- public function preConnect(Doctrine_Event $event)
- { }
-
- public function preQuery(Doctrine_Event $event)
- { }
- public function postQuery(Doctrine_Event $event)
- { }
-
- public function prePrepare(Doctrine_Event $event)
- { }
- public function postPrepare(Doctrine_Event $event)
- { }
-
- public function preExec(Doctrine_Event $event)
- { }
- public function postExec(Doctrine_Event $event)
- { }
-
- public function preError(Doctrine_Event $event)
- { }
- public function postError(Doctrine_Event $event)
- { }
-
- public function preFetch(Doctrine_Event $event)
- { }
- public function postFetch(Doctrine_Event $event)
- { }
-
- public function preFetchAll(Doctrine_Event $event)
- { }
- public function postFetchAll(Doctrine_Event $event)
- { }
-
- public function preStmtExecute(Doctrine_Event $event)
- { }
- public function postStmtExecute(Doctrine_Event $event)
- { }
+ public function getSubscribedEvents();
}
diff --git a/lib/Doctrine/EventManager.php b/lib/Doctrine/EventManager.php
index 1dee726f6..46ec435a3 100644
--- a/lib/Doctrine/EventManager.php
+++ b/lib/Doctrine/EventManager.php
@@ -56,9 +56,9 @@ class Doctrine_EventManager
foreach ($this->_listeners[$callback] as $listener) {
$listener->$callback($event);
}
+ return ! $event->getDefaultPrevented();
}
-
- return ! $event->getDefaultPrevented();
+ return true;
}
/**
diff --git a/lib/Doctrine/HydratorNew.php b/lib/Doctrine/HydratorNew.php
index 78ab83d82..0b25a64f8 100644
--- a/lib/Doctrine/HydratorNew.php
+++ b/lib/Doctrine/HydratorNew.php
@@ -227,6 +227,7 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
} else if (isset($resultPointers[$parent])) {
$baseElement =& $resultPointers[$parent];
} else {
+ unset($prev[$dqlAlias]); // Ticket #1228
continue;
}
@@ -256,7 +257,8 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
} else {
// x-1 relation
$oneToOne = true;
- if ( ! isset($nonemptyComponents[$dqlAlias])) {
+ if ( ! isset($nonemptyComponents[$dqlAlias]) &&
+ ! $driver->isFieldSet($baseElement, $relationAlias)) {
$driver->setRelatedElement($baseElement, $relationAlias,
$driver->getNullPointer());
} else if ( ! $driver->isFieldSet($baseElement, $relationAlias)) {
@@ -293,8 +295,6 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
}
/**
- * _setLastElement
- *
* sets the last element of given data array / collection
* as previous element
*
@@ -308,7 +308,8 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
*/
protected function _setLastElement(&$resultPointers, &$coll, $index, $dqlAlias, $oneToOne)
{
- if ($coll === $this->_nullObject) {
+ if ($coll === $this->_nullObject || $coll === null) {
+ unset($resultPointers[$dqlAlias]); // Ticket #1228
return false;
}
@@ -330,8 +331,6 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
$resultPointers[$dqlAlias] = $coll;
} else if (count($coll) > 0) {
$resultPointers[$dqlAlias] = $coll->getLast();
- } else if (isset($resultPointers[$dqlAlias])) {
- unset($resultPointers[$dqlAlias]);
}
}
diff --git a/lib/Doctrine/Query/Production/IndexBy.php b/lib/Doctrine/Query/Production/IndexBy.php
index 6bde673f9..794446109 100644
--- a/lib/Doctrine/Query/Production/IndexBy.php
+++ b/lib/Doctrine/Query/Production/IndexBy.php
@@ -77,7 +77,7 @@ class Doctrine_Query_Production_IndexBy extends Doctrine_Query_Production
}
// The INDEXBY field must be either the (primary && not part of composite pk) || (unique && notnull)
- $columnMapping = $classMetadata->getColumnMapping($this->_fieldName);
+ $columnMapping = $classMetadata->getFieldMapping($this->_fieldName);
if ( ! $classMetadata->isIdentifier($this->_fieldName) && ! $classMetadata->isUniqueField($this->_fieldName) && ! $classMetadata->isNotNull($this->_fieldName)) {
$this->_parser->semanticalError(
diff --git a/tests/Orm/Entity/AccessorTestCase.php b/tests/Orm/Entity/AccessorTestCase.php
index 0bee542de..863d282f0 100644
--- a/tests/Orm/Entity/AccessorTestCase.php
+++ b/tests/Orm/Entity/AccessorTestCase.php
@@ -22,6 +22,7 @@ class CustomAccessorMutatorTestEntity extends Doctrine_Entity
{
public static function initMetadata($class)
{
+ $class->mapColumn('id', 'integer', 4, array('primary'));
$class->mapColumn('username', 'string', 50, array(
'accessor' => 'getUsernameCustom',
'mutator' => 'setUsernameCustom'));
@@ -42,6 +43,7 @@ class MagicAccessorMutatorTestEntity extends Doctrine_Entity
{
public static function initMetadata($class)
{
+ $class->mapColumn('id', 'integer', 4, array('primary'));
$class->mapColumn('username', 'string', 50, array());
}
diff --git a/tests/Orm/Entity/ConstructorTest.php b/tests/Orm/Entity/ConstructorTest.php
index 5e0c2f99e..137027bd3 100644
--- a/tests/Orm/Entity/ConstructorTest.php
+++ b/tests/Orm/Entity/ConstructorTest.php
@@ -6,7 +6,7 @@ class Orm_Entity_ConstructorTest extends Doctrine_OrmTestCase
public function testFieldInitializationInConstructor()
{
$entity = new ConstructorTestEntity1("romanb");
- $this->assertTrue($entity->isTransient());
+ $this->assertTrue($entity->isNew());
$this->assertEquals("romanb", $entity->username);
}
}
@@ -16,7 +16,7 @@ class ConstructorTestEntity1 extends Doctrine_Entity
public function __construct($username = null)
{
parent::__construct();
- if ($this->isTransient()) {
+ if ($this->isNew()) {
$this->username = $username;
}
}
@@ -24,6 +24,7 @@ class ConstructorTestEntity1 extends Doctrine_Entity
/* The mapping definition */
public static function initMetadata($class)
{
+ $class->mapColumn('id', 'integer', 4, array('primary'));
$class->mapColumn('username', 'string', 50, array());
}
}
diff --git a/tests/models/forum/ForumBoard.php b/tests/models/forum/ForumBoard.php
index f68eeca8c..11dc974ca 100755
--- a/tests/models/forum/ForumBoard.php
+++ b/tests/models/forum/ForumBoard.php
@@ -1,9 +1,24 @@
mapColumn('position', 'integer');
- $class->mapColumn('category_id', 'integer');
- $class->hasOne('ForumCategory as category',
+ public static function initMetadata($metadata) {
+ /*$metadata->mapField(array(
+ 'fieldName' => 'id',
+ 'id' => true,
+ 'type' => 'integer',
+ 'length' => 4
+ ));
+ */
+ $metadata->mapColumn('id', 'integer', 4, array('primary'));
+ $metadata->mapColumn('position', 'integer');
+ $metadata->mapColumn('category_id', 'integer');
+ $metadata->hasOne('ForumCategory as category',
array('local' => 'category_id', 'foreign' => 'id'));
+ /*
+ $metadata->mapOneToOne(array(
+ 'fieldName' => 'category', // optional, defaults to targetEntity
+ 'targetEntity' => 'ForumCategory',
+ 'joinColumns' => array('category_id' => 'id')
+ ));
+ */
}
}
diff --git a/tests/models/forum/ForumCategory.php b/tests/models/forum/ForumCategory.php
index a5f166d31..0b1bbb340 100755
--- a/tests/models/forum/ForumCategory.php
+++ b/tests/models/forum/ForumCategory.php
@@ -1,6 +1,7 @@
mapColumn('id', 'integer', 4, array('primary'));
$class->mapColumn('position', 'integer');
$class->mapColumn('name', 'string', 255);
$class->hasMany('ForumBoard as boards', array(