Commit d8b76a54 authored by romanb's avatar romanb

continued refactorings.

parent 3e20fc6a
......@@ -34,8 +34,7 @@
* @since 1.0
* @version $Revision$
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @todo Consider making Collection & Entity implement ArrayAccess directly.
* This base class is not really a huge benefit.
* @todo Remove.
*/
abstract class Doctrine_Access implements ArrayAccess
{
......
......@@ -24,8 +24,8 @@
/**
* Base class for association mappings.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
* @todo Rename to AssociationMapping.
*/
class Doctrine_Association implements Serializable
......@@ -52,6 +52,9 @@ class Doctrine_Association implements Serializable
protected $_isCascadeSave;
protected $_isCascadeRefresh;
protected $_customAccessor;
protected $_customMutator;
/**
* The fetch mode used for the association.
*
......@@ -67,6 +70,14 @@ class Doctrine_Association implements Serializable
*/
protected $_isOwningSide = true;
/**
* Whether the association is optional (0..X) or not (1..X).
* By default all associations are optional.
*
* @var boolean
*/
protected $_isOptional = true;
/**
* The name of the source Entity (the Entity that defines this mapping).
*
......@@ -84,7 +95,8 @@ class Doctrine_Association implements Serializable
/**
* Identifies the field on the source class (the class this AssociationMapping
* belongs to) that represents the association.
* belongs to) that represents the association and stores the reference to the
* other entity/entities.
*
* @var string
*/
......@@ -106,12 +118,13 @@ class Doctrine_Association implements Serializable
*/
public function __construct(array $mapping)
{
$this->_validateMapping($mapping);
if ($this->_isOwningSide) {
$this->_validateAndCompleteMapping($mapping);
$this->_sourceEntityName = $mapping['sourceEntity'];
$this->_targetEntityName = $mapping['targetEntity'];
$this->_sourceFieldName = $mapping['fieldName'];
} else {
if ( ! $this->_isOwningSide) {
$this->_mappedByFieldName = $mapping['mappedBy'];
}
}
......@@ -122,23 +135,50 @@ class Doctrine_Association implements Serializable
* @param array $mapping
* @return array The validated & completed mapping.
*/
protected function _validateMapping(array $mapping)
protected function _validateAndCompleteMapping(array $mapping)
{
if (isset($mapping['mappedBy'])) {
// Inverse side, mapping can be found on the owning side.
$this->_isOwningSide = false;
} else {
// Owning side
}
if ($this->_isOwningSide) {
if ( ! isset($mapping['targetEntity'])) {
throw Doctrine_MappingException::missingTargetEntity();
} else if ( ! isset($mapping['fieldName'])) {
// Mandatory attributes
if ( ! isset($mapping['fieldName'])) {
throw Doctrine_MappingException::missingFieldName();
}
if ( ! isset($mapping['sourceEntity'])) {
throw Doctrine_MappingException::missingSourceEntity($mapping['fieldName']);
}
if ( ! isset($mapping['targetEntity'])) {
throw Doctrine_MappingException::missingTargetEntity($mapping['fieldName']);
}
// Optional attributes
$this->_customAccessor = isset($mapping['accessor']) ? $mapping['accessor'] : null;
$this->_customMutator = isset($mapping['mutator']) ? $mapping['mutator'] : null;
$this->_isOptional = isset($mapping['isOptional']) ? (bool)$mapping['isOptional'] : true;
return $mapping;
}
protected function _validateAndCompleteInverseSideMapping()
{
}
protected function _validateAndCompleteOwningSideMapping()
{
}
/**
* Whether the association cascades delete() operations from the source entity
* to the target entity/entities.
*
* @return boolean
*/
public function isCascadeDelete()
{
if (is_null($this->_isCascadeDelete)) {
......@@ -147,6 +187,12 @@ class Doctrine_Association implements Serializable
return $this->_isCascadeDelete;
}
/**
* Whether the association cascades save() operations from the source entity
* to the target entity/entities.
*
* @return boolean
*/
public function isCascadeSave()
{
if (is_null($this->_isCascadeSave)) {
......@@ -155,6 +201,12 @@ class Doctrine_Association implements Serializable
return $this->_isCascadeSave;
}
/**
* Whether the association cascades refresh() operations from the source entity
* to the target entity/entities.
*
* @return boolean
*/
public function isCascadeRefresh()
{
if (is_null($this->_isCascadeRefresh)) {
......@@ -163,36 +215,81 @@ class Doctrine_Association implements Serializable
return $this->_isCascadeRefresh;
}
/**
* Whether the target entity/entities of the association are eagerly fetched.
*
* @return boolean
*/
public function isEagerlyFetched()
{
return $this->_fetchMode == self::FETCH_EAGER;
}
/**
* Whether the target entity/entities of the association are lazily fetched.
*
* @return boolean
*/
public function isLazilyFetched()
{
return $this->_fetchMode == self::FETCH_LAZY;
}
/**
* Whether the target entity/entities of the association are manually fetched.
*
* @return boolean
*/
public function isManuallyFetched()
{
return $this->_fetchMode == self::FETCH_MANUAL;
}
/**
* Whether the source entity of this association represents the owning side.
*
* @return boolean
*/
public function isOwningSide()
{
return $this->_isOwningSide;
}
/**
* Whether the source entity of this association represents the inverse side.
*
* @return boolean
*/
public function isInverseSide()
{
return ! $this->_isOwningSide;
}
/**
* Whether the association is optional (0..X), or not (1..X).
*
* @return boolean TRUE if the association is optional, FALSE otherwise.
*/
public function isOptional()
{
return $this->_isOptional;
}
/**
* Gets the name of the source entity class.
*
* @return string
*/
public function getSourceEntityName()
{
return $this->_sourceEntityName;
}
/**
* Gets the name of the target entity class.
*
* @return string
*/
public function getTargetEntityName()
{
return $this->_targetEntityName;
......@@ -201,17 +298,80 @@ class Doctrine_Association implements Serializable
/**
* Get the name of the field the association is mapped into.
*
* @return string
*/
public function getSourceFieldName()
{
return $this->_sourceFieldName;
}
/**
* Gets the field name of the owning side in a bi-directional association.
*
* @return string
*/
public function getMappedByFieldName()
{
return $this->_mappedByFieldName;
}
/**
* Whether the source field of the association has a custom accessor.
*
* @return boolean TRUE if the source field of the association has a custom accessor,
* FALSE otherwise.
*/
public function hasCustomAccessor()
{
return isset($this->_customAccessor);
}
/**
* Gets the name of the custom accessor method of the source field.
*
* @return string The name of the accessor method or NULL.
*/
public function getCustomAccessor()
{
return $this->_customAccessor;
}
/**
* Whether the source field of the association has a custom mutator.
*
* @return boolean TRUE if the source field of the association has a custom mutator,
* FALSE otherwise.
*/
public function hasCustomMutator()
{
return isset($this->_customMutator);
}
/**
* Gets the name of the custom mutator method of the source field.
*
* @return string The name of the mutator method or NULL.
*/
public function getCustomMutator()
{
return $this->_customMutator;
}
public function isOneToOne()
{
return false;
}
public function isOneToMany()
{
return false;
}
public function isManyToMany()
{
return false;
}
/* Serializable implementation */
public function serialize()
......
......@@ -105,6 +105,17 @@ class Doctrine_Association_OneToOne extends Doctrine_Association
return $this->_targetToSourceKeyColumns;
}
/**
* Whether the association is one-to-one.
*
* @return boolean
* @override
*/
public function isOneToOne()
{
return true;
}
/**
* Lazy-loads the associated entity for a given entity.
*
......
......@@ -48,12 +48,18 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
const GENERATOR_TYPE_NONE = 'none';
/* The Entity types */
// A regular entity is assumed to have persistent state that Doctrine should manage.
/**
* A regular entity is assumed to have persistent state that Doctrine should manage.
*/
const ENTITY_TYPE_REGULAR = 'regular';
// A transient entity is ignored by Doctrine.
/**
* A transient entity is ignored by Doctrine.
*/
const ENTITY_TYPE_TRANSIENT = 'transient';
// A mapped superclass entity is itself not persisted by Doctrine but it's
// field & association mappings are inherited by subclasses.
/**
* A mapped superclass entity is itself not persisted by Doctrine but it's
* field & association mappings are inherited by subclasses.
*/
const ENTITY_TYPE_MAPPED_SUPERCLASS = 'mappedSuperclass';
/**
......@@ -123,15 +129,6 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
protected $_generatorType = self::GENERATOR_TYPE_NONE;
/**
* An array containing all behaviors attached to the class.
*
* @see Doctrine_Template
* @var array $_templates
* @todo Unify under 'Behaviors'.
*/
//protected $_behaviors = array();
/**
* The field mappings of the class.
* Keys are field names and values are mapping definitions.
......@@ -219,13 +216,6 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
protected $_lcColumnToFieldNames = array();
/**
* Enter description here...
*
* @var unknown_type
*/
//protected $_subclassFieldNames = array();
/**
* Relation parser object. Manages the relations for the class.
*
......@@ -311,10 +301,14 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
protected $_isIdentifierComposite = false;
protected $_customAssociationAccessors = array();
protected $_customAssociationMutators = array();
/**
* Constructs a new ClassMetadata instance.
*
* @param string $entityName Name of the entity class the metadata info is used for.
* @param Doctrine::ORM::Entitymanager $em
*/
public function __construct($entityName, Doctrine_EntityManager $em)
{
......@@ -326,13 +320,10 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
}
/**
* @deprecated
* Gets the EntityManager that holds this ClassMetadata.
*
* @return Doctrine::ORM::EntityManager
*/
public function getConnection()
{
return $this->_em;
}
public function getEntityManager()
{
return $this->_em;
......@@ -360,14 +351,6 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
return $this->_rootEntityName;
}
/**
* @deprecated
*/
public function getComponentName()
{
return $this->getClassName();
}
/**
* Checks whether a field is part of the identifier/primary key field(s).
*
......@@ -429,8 +412,6 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
}
/**
* addIndex
*
* adds an index to this table
*
* @return void
......@@ -458,22 +439,6 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
return false;
}
/**
* setOption
* sets an option and returns this object in order to
* allow flexible method chaining
*
* @see Doctrine_Table::$_options for available options
* @param string $name the name of the option to set
* @param mixed $value the value of the option
* @return Doctrine_Table this object
* @deprecated
*/
public function setOption($name, $value)
{
$this->_options[$name] = $value;
}
/**
* Sets a table option.
*/
......@@ -497,43 +462,6 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
return $this->_tableOptions[$name];
}
/*public function getBehaviorForMethod($method)
{
return (isset($this->_invokedMethods[$method])) ?
$this->_invokedMethods[$method] : false;
}
public function addBehaviorMethod($method, $behavior)
{
$this->_invokedMethods[$method] = $class;
}*/
/**
* returns the value of given option
*
* @param string $name the name of the option
* @return mixed the value of given option
*/
public function getOption($name)
{
if (isset($this->_options[$name])) {
return $this->_options[$name];
} else if (isset($this->_tableOptions[$name])) {
return $this->_tableOptions[$name];
}
return null;
}
/**
* getOptions
* returns all options of this table and the associated values
*
* @return array all options and their values
*/
public function getOptions()
{
return $this->_options;
}
/**
* getTableOptions
* returns all table options.
......@@ -703,8 +631,8 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
/**
* Validates & completes the field mapping. Default values are applied here.
*
* @param array $mapping
* @return array
* @param array $mapping The field mapping to validated & complete.
* @return array The validated and completed field mapping.
*/
private function _validateAndCompleteFieldMapping(array $mapping)
{
......@@ -800,18 +728,6 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
//...
}
/**
* Gets the names of all validators that are applied on a field.
*
* @param string The field name.
* @return array The names of all validators that are applied on the specified field.
*/
/*public function getFieldValidators($fieldName)
{
return isset($this->_fieldMappings[$fieldName]['validators']) ?
$this->_fieldMappings[$fieldName]['validators'] : array();
}*/
/**
* Gets the identifier (primary key) field names of the class.
*
......@@ -871,8 +787,12 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function getCustomAccessor($fieldName)
{
return isset($this->_fieldMappings[$fieldName]['accessor']) ?
$this->_fieldMappings[$fieldName]['accessor'] : null;
if (isset($this->_fieldMappings[$fieldName]['accessor'])) {
return $this->_fieldMappings[$fieldName]['accessor'];
} else if (isset($this->_customAssociationAccessors[$fieldName])) {
return $this->_customAssociationAccessors[$fieldName];
}
return null;
}
/**
......@@ -883,8 +803,12 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public function getCustomMutator($fieldName)
{
return isset($this->_fieldMappings[$fieldName]['mutator']) ?
$this->_fieldMappings[$fieldName]['mutator'] : null;
if (isset($this->_fieldMappings[$fieldName]['mutator'])) {
return $this->_fieldMappings[$fieldName]['mutator'];
} else if (isset($this->_customAssociationMutators[$fieldName])) {
return $this->_customAssociationMutators[$fieldName];
}
return null;
}
/**
......@@ -1053,18 +977,6 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
//...
}
/**
* getBehaviors
* returns all behaviors attached to the class.
*
* @return array an array containing all templates
* @todo Unify under 'Behaviors'
*/
/*public function getBehaviors()
{
return $this->_behaviors;
}*/
/**
* Gets the inheritance type used by the class.
*
......@@ -1156,8 +1068,8 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
throw Doctrine_MappingException::invalidInheritanceType($type);
}
if ($type == Doctrine::INHERITANCE_TYPE_SINGLE_TABLE ||
$type == Doctrine::INHERITANCE_TYPE_JOINED) {
if ($type == self::INHERITANCE_TYPE_SINGLE_TABLE ||
$type == self::INHERITANCE_TYPE_JOINED) {
$this->_checkRequiredDiscriminatorOptions($options);
}
......@@ -1240,25 +1152,25 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
}
/**
* export
* exports this class to the database based on its mapping.
*
* @throws Doctrine_Connection_Exception If some error other than Doctrine::ERR_ALREADY_EXISTS
* occurred during the create table operation.
* @return boolean Whether or not the export operation was successful
* false if table already existed in the database.
* @todo Reimpl. & Placement.
*/
public function export()
{
$this->_em->export->exportTable($this);
//$this->_em->export->exportTable($this);
}
/**
* getExportableFormat
* Returns an array with all the information needed to create the main database table
* for the class.
*
* @return array
* @todo Reimpl. & placement.
*/
public function getExportableFormat($parseForeignKeys = true)
{
......@@ -1268,7 +1180,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
// If the class is part of a Single Table Inheritance hierarchy, collect the fields
// of all classes in the hierarchy.
if ($this->_inheritanceType == Doctrine::INHERITANCE_TYPE_SINGLE_TABLE) {
if ($this->_inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE) {
$parents = $this->getParentClasses();
if ($parents) {
$rootClass = $this->_em->getClassMetadata(array_pop($parents));
......@@ -1280,7 +1192,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
$subClassMetadata = $this->_em->getClassMetadata($subClass);
$allColumns = array_merge($allColumns, $subClassMetadata->getColumns());
}
} else if ($this->_inheritanceType == Doctrine::INHERITANCE_TYPE_JOINED) {
} else if ($this->_inheritanceType == self::INHERITANCE_TYPE_JOINED) {
// Remove inherited, non-pk fields. They're not in the table of this class
foreach ($allColumns as $name => $definition) {
if (isset($definition['primary']) && $definition['primary'] === true) {
......@@ -1293,7 +1205,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
unset($allColumns[$name]);
}
}
} else if ($this->_inheritanceType == Doctrine::INHERITANCE_TYPE_TABLE_PER_CLASS) {
} else if ($this->_inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS) {
// If this is a subclass, just remove existing autoincrement options on the pk
if ($this->getParentClasses()) {
foreach ($allColumns as $name => $definition) {
......@@ -1423,7 +1335,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
}
/**
* Checks whether the given name identifies an entity type.
* Checks whether the given type identifies an entity type.
*
* @param string $type
* @return boolean
......@@ -1436,7 +1348,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
}
/**
* Checks whether the given name identifies an inheritance type.
* Checks whether the given type identifies an inheritance type.
*
* @param string $type
* @return boolean
......@@ -1450,7 +1362,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
}
/**
* Checks whether the given name identifies an id generator type.
* Checks whether the given type identifies an id generator type.
*
* @param string $type
* @return boolean
......@@ -1465,116 +1377,88 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
}
/**
* Adds a one-to-one association mapping.
* Makes some automatic additions to the association mapping to make the life
* easier for the user.
*
* @todo Implementation.
* @param array $mapping
* @return unknown
* @todo Pass param by ref?
*/
public function mapOneToOne(array $mapping)
private function _completeAssociationMapping(array $mapping)
{
$oneToOneMapping = new Doctrine_Association_OneToOne($mapping);
if (isset($this->_associationMappings[$oneToOneMapping->getSourceFieldName()])) {
throw Doctrine_MappingException::duplicateFieldMapping();
}
$this->_associationMappings[$oneToOneMapping->getSourceFieldName()] = $oneToOneMapping;
$mapping['sourceEntity'] = $this->_entityName;
return $mapping;
}
/**
* @todo Implementation.
* 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
*/
public function mapOneToMany(array $mapping)
private function _registerCustomAssociationAccessors(Doctrine_Association $assoc, $fieldName)
{
if ($acc = $assoc->getCustomAccessor()) {
$this->_customAssociationAccessors[$fieldName] = $acc;
}
if ($mut = $assoc->getCustomMutator()) {
$this->_customAssociationMutators[$fieldName] = $mut;
}
}
/**
* Adds a one-to-one association mapping.
*
* @todo Implementation.
*/
public function mapManyToOne(array $mapping)
public function mapOneToOne(array $mapping)
{
$mapping = $this->_completeAssociationMapping($mapping);
$oneToOneMapping = new Doctrine_Association_OneToOne($mapping);
$sourceFieldName = $oneToOneMapping->getSourceFieldName();
if (isset($this->_associationMappings[$sourceFieldName])) {
throw Doctrine_MappingException::duplicateFieldMapping();
}
$this->_associationMappings[$sourceFieldName] = $oneToOneMapping;
$this->_registerCustomAssociationAccessors($oneToOneMapping, $sourceFieldName);
}
/**
* @todo Implementation.
*/
public function mapManyToMany(array $mapping)
public function mapOneToMany(array $mapping)
{
$mapping = $this->_completeAssociationMapping($mapping);
$oneToManyMapping = new Doctrine_Association_OneToMany($mapping);
$sourceFieldName = $oneToManyMapping->getSourceFieldName();
if (isset($this->_associationMappings[$sourceFieldName])) {
throw Doctrine_MappingException::duplicateFieldMapping();
}
$this->_associationMappings[$sourceFieldName] = $oneToManyMapping;
$this->_registerCustomAssociationAccessors($oneToManyMapping, $sourceFieldName);
}
/**
* actAs
* loads the given plugin
*
* @param mixed $tpl
* @param array $options
* @todo Unify under 'Behaviors'.
* @todo Implementation.
*/
/*public function actAs($tpl, array $options = array())
public function mapManyToOne(array $mapping)
{
if ( ! is_object($tpl)) {
if (class_exists($tpl, true)) {
$tpl = new $tpl($options);
} else {
$className = 'Doctrine_Template_' . $tpl;
if ( ! class_exists($className, true)) {
throw new Doctrine_Record_Exception("Couldn't load plugin.");
}
$tpl = new $className($options);
}
}
if ( ! ($tpl instanceof Doctrine_Template)) {
throw new Doctrine_Record_Exception('Loaded plugin class is not an instance of Doctrine_Template.');
}
$className = get_class($tpl);
$this->addBehavior($className, $tpl);
$tpl->setTable($this);
$tpl->setUp();
$tpl->setTableDefinition();
return $this;
}*/
/**
* check
* adds a check constraint
*
* @param mixed $constraint either a SQL constraint portion or an array of CHECK constraints
* @param string $name optional constraint name
* @return Doctrine_Entity this object
* @todo Should be done through $_tableOptions
* @deprecated
* @todo Implementation.
*/
/*public function check($constraint, $name = null)
public function mapManyToMany(array $mapping)
{
if (is_array($constraint)) {
foreach ($constraint as $name => $def) {
$this->_addCheckConstraint($def, $name);
}
} else {
$this->_addCheckConstraint($constraint, $name);
}
return $this;
}
protected function _addCheckConstraint($definition, $name)
{
if (is_string($name)) {
$this->_tableOptions['checks'][$name] = $definition;
} else {
$this->_tableOptions['checks'][] = $definition;
}
}*/
/**
* Registers a custom mapper for the entity class.
*
* @param string $mapperClassName The class name of the custom mapper.
* @deprecated
*/
public function setCustomRepositoryClass($repositoryClassName)
{
......@@ -1607,7 +1491,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
}
/**
* Binds the entity instance of this class to a specific EntityManager.
* Binds the entity instances of this class to a specific EntityManager.
*
* @todo Implementation. Replaces the bindComponent() methods on the old Doctrine_Manager.
* Binding an Entity to a specific EntityManager in 2.0 is the same as binding
......@@ -1698,6 +1582,24 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
}
}
/**
* Completes the identifier mapping of the class.
* NOTE: Should only be called by the ClassMetadataFactory!
*/
public function completeIdentifierMapping()
{
if ($this->getIdGeneratorType() == self::GENERATOR_TYPE_AUTO) {
$platform = $this->_em->getConnection()->getDatabasePlatform();
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
* they are created. This means the entity can only be modified as long as it's
......@@ -1713,6 +1615,27 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
return $columnName === $this->_inheritanceOptions['discriminatorColumn'];
}
public function hasAttribute($name)
{
return isset($this->_attributes[$name]);
}
public function getAttribute($name)
{
if ($this->hasAttribute($name)) {
return $this->_attributes[$name];
}
}
public function setAttribute($name, $value)
{
if ($this->hasAttribute($name)) {
$this->_attributes[$name] = $value;
}
}
/* The following stuff needs to be touched for the association mapping rewrite */
/**
* hasOne
* binds One-to-One aggregate relation
......@@ -1721,6 +1644,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
* @param string $options relation options
* @see Doctrine_Relation::_$definition
* @return Doctrine_Entity this object
* @deprecated
*/
public function hasOne()
{
......@@ -1737,6 +1661,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
* @param string $options relation options
* @see Doctrine_Relation::_$definition
* @return Doctrine_Entity this object
* @deprecated
*/
public function hasMany()
{
......@@ -1745,27 +1670,9 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
return $this;
}
public function hasAttribute($name)
{
return isset($this->_attributes[$name]);
}
public function getAttribute($name)
{
if ($this->hasAttribute($name)) {
return $this->_attributes[$name];
}
}
public function setAttribute($name, $value)
{
if ($this->hasAttribute($name)) {
$this->_attributes[$name] = $value;
}
}
/* The following stuff needs to be touched for the association mapping rewrite */
/**
* @deprecated
*/
public function bindRelation($args, $type)
{
return $this->bind($args, $type);
......@@ -1773,6 +1680,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
/**
* @todo Relation mapping rewrite.
* @deprecated
*/
public function bind($args, $type)
{
......@@ -1798,22 +1706,32 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*
* @param string $alias the relation to check if exists
* @return boolean true if the relation exists otherwise false
* @deprecated
*/
public function hasRelation($alias)
{
return $this->_parser->hasRelation($alias);
}
public function hasAssociation($fieldName)
{
return isset($this->_associationMappings[$fieldName]);
}
/**
* getRelation
*
* @param string $alias relation alias
* @deprecated
*/
public function getRelation($alias, $recursive = true)
{
return $this->_parser->getRelation($alias, $recursive);
}
/**
* @deprecated
*/
public function getRelationParser()
{
return $this->_parser;
......@@ -1824,34 +1742,13 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
* returns an array containing all relation objects
*
* @return array an array of Doctrine_Relation objects
* @deprecated
*/
public function getRelations()
{
return $this->_parser->getRelations();
}
/**
* @todo Set by the factory if type is AUTO. Not pretty. Find sth. better.
*/
public function setIdGeneratorType($type)
{
$this->_generatorType = $type;
}
public function completeIdentifierMapping()
{
if ($this->getIdGeneratorType() == self::GENERATOR_TYPE_AUTO) {
$platform = $this->_em->getConnection()->getDatabasePlatform();
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;
}
}
}
/**
*
*/
......
......@@ -23,7 +23,7 @@
/**
* The metadata factory is used to create ClassMetadata objects that contain all the
* metadata of a class.
* metadata mapping informations of a class.
*
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
......@@ -45,7 +45,7 @@ class Doctrine_ClassMetadata_Factory
/**
* Constructor.
* Creates a new factory instance that uses the given connection and metadata driver
* Creates a new factory instance that uses the given EntityManager and metadata driver
* implementations.
*
* @param $conn The connection to use.
......@@ -129,6 +129,12 @@ class Doctrine_ClassMetadata_Factory
}
}
/**
* Adds inherited fields to the subclass mapping.
*
* @param unknown_type $subClass
* @param unknown_type $parentClass
*/
protected function _addInheritedFields($subClass, $parentClass)
{
foreach ($parentClass->getFieldMappings() as $fieldName => $mapping) {
......@@ -138,6 +144,12 @@ class Doctrine_ClassMetadata_Factory
}
}
/**
* Adds inherited associations to the subclass mapping.
*
* @param unknown_type $subClass
* @param unknown_type $parentClass
*/
protected function _addInheritedRelations($subClass, $parentClass)
{
foreach ($parentClass->getRelationParser()->getRelationDefinitions() as $name => $definition) {
......@@ -177,7 +189,7 @@ class Doctrine_ClassMetadata_Factory
// save parents
$class->setParentClasses($names);
// load further metadata
// load user-specified mapping metadata through the driver
$this->_driver->loadMetadataForClass($name, $class);
// set default table name, if necessary
......
......@@ -23,24 +23,40 @@
/**
* A persistent collection of entities.
*
* A collection object is strongly typed in the sense that it can only contain
* entities of a specific type or one it's subtypes.
* entities of a specific type or one of it's subtypes. A collection object is
* basically a wrapper around an ordinary php array and just like a php array
* it can have List or Map semantics.
*
* A collection of entities represents only the associations (links) to those entities.
* That means, if the collection is part of a many-many mapping and you remove
* entities from the collection, only the links in the xref table are removed.
* Similarly, if you remove entities from a collection that is part of a one-many
* mapping this will only result in the nulling out of the foreign keys
* (or removal of the links in the xref table if the one-many is mapped through an
* xref table). If you want entities in a one-many collection to be removed when
* they're removed from the collection, use deleteOrphans => true on the one-many
* mapping.
*
* @package Doctrine
* @subpackage Collection
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.org
* @since 1.0
* @version $Revision$
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
*/
class Doctrine_Collection extends Doctrine_Access implements Countable, IteratorAggregate, Serializable
class Doctrine_Collection implements Countable, IteratorAggregate, Serializable, ArrayAccess
{
/**
* The base type of the collection.
*
* @var string
*/
protected $_entityBaseType;
/**
* An array containing the records of this collection.
* An array containing the entries of this collection.
* This is the wrapped php array.
*
* @var array
*/
......@@ -83,20 +99,17 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
//protected static $null;
protected $_mapping;
/**
* The EntityManager.
*
* @var EntityManager
*/
protected $_em;
protected $_isDirty = false;
/**
* Constructor.
*
* @param Doctrine_Mapper|string $mapper The mapper used by the collection.
* @param string $keyColumn The field name that will be used as the key
* in the collection.
*/
public function __construct($entityBaseType, $keyField = null)
{
......@@ -115,7 +128,6 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
* setData
*
* @param array $data
* @return Doctrine_Collection
*/
public function setData(array $data)
{
......@@ -124,7 +136,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
/**
* Serializes the collection.
* This method is automatically called when this Doctrine_Collection is serialized.
* This method is automatically called when the Collection is serialized.
*
* Part of the implementation of the Serializable interface.
*
......@@ -145,7 +157,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
/**
* Reconstitutes the collection object from it's serialized form.
* This method is automatically called everytime a Doctrine_Collection object is unserialized.
* This method is automatically called everytime the Collection object is unserialized.
*
* Part of the implementation of the Serializable interface.
*
......@@ -172,8 +184,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
/**
* setKeyField
* sets the key column for this collection
* Sets the key column for this collection
*
* @param string $column
* @return Doctrine_Collection
......@@ -185,7 +196,6 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
/**
* getKeyField
* returns the name of the key column
*
* @return string
......@@ -196,7 +206,6 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
/**
* getData
* returns all the records as an array
*
* @return array
......@@ -207,7 +216,6 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
/**
* getFirst
* returns the first record in the collection
*
* @return mixed
......@@ -218,7 +226,6 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
/**
* getLast
* returns the last record in the collection
*
* @return mixed
......@@ -254,7 +261,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*
* @return void
*/
public function setReference(Doctrine_Entity $entity, Doctrine_Relation $relation)
public function setReference(Doctrine_Entity $entity, $relation)
{
$this->_owner = $entity;
//$this->relation = $relation;
......@@ -296,21 +303,122 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
{
$removed = $this->_data[$key];
unset($this->_data[$key]);
//TODO: Register collection as dirty with the UoW if necessary
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 void
*/
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
* @todo Rename to containsKey().
*/
public function contains($key)
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)
{
//...
}
/**
*
*/
......@@ -336,49 +444,25 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
/**
* Returns the primary keys of all records in the collection.
* Gets all keys.
* (Map method)
*
* @return array An array containing all primary keys.
* @todo Rename.
* @return array
*/
public function getPrimaryKeys()
public function getKeys()
{
$list = array();
$idFieldNames = (array)$this->_mapper->getClassMetadata()->getIdentifier();
foreach ($this->_data as $record) {
if (is_array($record)) {
if (count($idFieldNames) > 1) {
$id = array();
foreach ($idFieldNames as $fieldName) {
if (isset($record[$fieldName])) {
$id[] = $record[$fieldName];
}
}
$list[] = $id;
} else {
$idField = $idFieldNames[0];
if (isset($record[$idField])) {
$list[] = $record[$idField];
}
}
} else {
// @todo does not take composite keys into account
$ids = $record->identifier();
$list[] = count($ids) > 0 ? array_pop($ids) : null;
}
}
return $list;
return array_keys($this->_data);
}
/**
* returns all keys
* Gets all values.
* (Map method)
*
* @return array
*/
public function getKeys()
public function getValues()
{
return array_keys($this->_data);
return array_values($this->_data);
}
/**
......@@ -394,66 +478,76 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
/**
* set
* 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 Doctrine_Entity $record
* @param mixed $value
* @return void
* @internal Can't type-hint the second parameter to Doctrine_Entity because we need
* to adhere to the Doctrine_Access::set() signature.
*/
public function set($key, $value)
{
if ( ! $value instanceof Doctrine_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 a record to collection
* Adds an entry to the collection.
*
* @param Doctrine_Entity $record record to be added
* @param string $key optional key for the record
* @param mixed $value
* @param string $key
* @return boolean
*/
public function add($value, $key = null)
{
/** @TODO Use raw getters/setters */
if ( ! $value instanceof Doctrine_Entity) {
throw new Doctrine_Record_Exception('Value variable in set is not an instance of Doctrine_Entity.');
}
/*
* for some weird reason in_array cannot be used here (php bug ?)
*
* if used it results in fatal error : [ nesting level too deep ]
*/
foreach ($this->_data as $val) {
if ($val === $value) {
// Neither Maps nor Lists allow duplicates, both are Sets
if (in_array($value, $this->_data, true)) {
return false;
}
}
if (isset($key)) {
if (isset($this->_data[$key])) {
return false;
}
$this->_data[$key] = $value;
return true;
} else {
$this->_data[] = $value;
}
$this->_data[] = $value;
//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)
{
//...
//TODO: Register collection as dirty with the UoW if necessary
//$this->_changed();
}
/**
* INTERNAL:
* loadRelated
*
* @param mixed $name
* @return boolean
* @todo New implementation & maybe move elsewhere.
*/
public function loadRelated($name = null)
{
......@@ -506,8 +600,9 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
* @param string $name
* @param Doctrine_Collection $coll
* @return void
* @todo New implementation & maybe move elsewhere.
*/
public function populateRelated($name, Doctrine_Collection $coll)
protected function populateRelated($name, Doctrine_Collection $coll)
{
$rel = $this->_mapper->getTable()->getRelation($name);
$table = $rel->getTable();
......@@ -562,38 +657,23 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
/**
* getNormalIterator
* returns normal iterator - an iterator that will not expand this collection
*
* @return Doctrine_Iterator_Normal
*/
public function getNormalIterator()
{
return new Doctrine_Collection_Iterator_Normal($this);
}
/**
* takeSnapshot
* takes a snapshot from this collection
* INTERNAL: Takes a snapshot from this collection.
*
* snapshots are used for diff processing, for example
* Snapshots are used for diff processing, for example
* when a fetched collection has three elements, then two of those
* are being removed the diff would contain one element
* are being removed the diff would contain one element.
*
* Doctrine_Collection::save() attaches the diff with the help of last
* snapshot.
* Collection::save() attaches the diff with the help of last snapshot.
*
* @return Doctrine_Collection
* @return void
*/
public function takeSnapshot()
{
$this->_snapshot = $this->_data;
return $this;
}
/**
* getSnapshot
* returns the data of the last snapshot
* INTERNAL: Returns the data of the last snapshot.
*
* @return array returns the data in last snapshot
*/
......@@ -603,8 +683,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
/**
* processDiff
* processes the difference of the last snapshot and the current data
* Processes the difference of the last snapshot and the current data.
*
* an example:
* Snapshot with the objects 1, 2 and 4
......@@ -623,7 +702,6 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
/**
* toArray
* Mimics the result of a $query->execute(array(), Doctrine::FETCH_ARRAY);
*
* @param boolean $deep
......@@ -639,15 +717,18 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
return $data;
}
/**
* Checks whether the collection is empty.
*
* @return boolean TRUE if the collection is empty, FALSE otherwise.
*/
public function isEmpty()
{
return $this->count() == 0;
}
/**
* fromArray
*
* Populate a Doctrine_Collection from an array of data
* Populate a Doctrine_Collection from an array of data.
*
* @param string $array
* @return void
......@@ -661,8 +742,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
/**
* synchronizeFromArray
* synchronizes a Doctrine_Collection with data from an array
* Synchronizes a Doctrine_Collection with data from an array.
*
* it expects an array representation of a Doctrine_Collection similar to the return
* value of the toArray() method. It will create Dectrine_Records that don't exist
......@@ -689,8 +769,6 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
/**
* exportTo
*
* Export a Doctrine_Collection to one of the supported Doctrine_Parser formats
*
* @param string $type
......@@ -698,18 +776,16 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
* @return void
* @todo Move elsewhere.
*/
public function exportTo($type, $deep = false)
/*public function exportTo($type, $deep = false)
{
if ($type == 'array') {
return $this->toArray($deep);
} else {
return Doctrine_Parser::dump($this->toArray($deep, true), $type);
}
}
}*/
/**
* importFrom
*
* Import data to a Doctrine_Collection from one of the supported Doctrine_Parser formats
*
* @param string $type
......@@ -717,19 +793,19 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
* @return void
* @todo Move elsewhere.
*/
public function importFrom($type, $data)
/*public function importFrom($type, $data)
{
if ($type == 'array') {
return $this->fromArray($data);
} else {
return $this->fromArray(Doctrine_Parser::load($data, $type));
}
}
}*/
/**
* getDeleteDiff
* INTERNAL: getDeleteDiff
*
* @return void
* @return array
*/
public function getDeleteDiff()
{
......@@ -737,9 +813,9 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
/**
* getInsertDiff
* INTERNAL getInsertDiff
*
* @return void
* @return array
*/
public function getInsertDiff()
{
......@@ -747,8 +823,9 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
/**
* _compareRecords
* Compares two records. To be used on _snapshot diffs using array_udiff.
*
* @return integer
*/
protected function _compareRecords($a, $b)
{
......@@ -766,7 +843,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
* @param Doctrine_Connection $conn optional connection parameter
* @return Doctrine_Collection
*/
public function save()
/*public function save()
{
$conn = $this->_mapper->getConnection();
......@@ -786,14 +863,15 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
return $this;
}
}*/
/**
* Deletes all records from the collection.
* Shorthand for calling delete() for all entities in the collection.
*
* @return void
*/
public function delete($clearColl = false)
/*public function delete()
{
$conn = $this->_mapper->getConnection();
......@@ -811,10 +889,8 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
throw $e;
}
if ($clearColl) {
$this->clear();
}
}
}*/
public function free($deep = false)
......@@ -853,16 +929,54 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
/**
* returns the relation object
* @return object Doctrine_Relation
* Gets the association mapping of the collection.
*
* @return Doctrine::ORM::Mapping::AssociationMapping
*/
public function getRelation()
public function getMapping()
{
return $this->relation;
}
/**
* @todo Experiment. Waiting for 5.3 closures.
* Example usage:
*
* $map = $coll->mapElements(function($key, $entity) {
* return array($entity->id, $entity->name);
* });
*
* or:
*
* $map = $coll->mapElements(function($key, $entity) {
* return array($entity->name, strtoupper($entity->name));
* });
*
*/
public function mapElements($lambda) {
$result = array();
foreach ($this->_data as $key => $entity) {
list($key, $value) = $lambda($key, $entity);
$result[$key] = $value;
}
return $result;
}
/**
* Clears the collection.
*
* @return void
*/
public function clear()
{
$this->_data = array();
//TODO: Register collection as dirty with the UoW if necessary
}
private function _changed()
{
/*if ( ! $this->_em->getUnitOfWork()->isCollectionScheduledForUpdate($this)) {
$this->_em->getUnitOfWork()->scheduleCollectionUpdate($this);
}*/
}
}
<?php
class Doctrine_CollectionPersister_Abstract
{
public function recreate(Doctrine_Collection $coll)
{
if ($coll->getRelation()->isInverseSide()) {
return;
}
//...
}
public function delete(Doctrine_Collection $coll)
{
if ($coll->getRelation()->isInverseSide()) {
return;
}
//...
if ($coll->getRelation() instanceof Doctrine_Association_OneToManyMapping) {
//...
} else if ($coll->getRelation() instanceof Doctrine_Association_ManyToManyMapping) {
//...
}
}
/* collection update actions */
public function deleteRows()
{
}
public function updateRows()
{
}
public function insertRows()
{
}
}
?>
\ No newline at end of file
<?php
class Doctrine_CollectionPersister_OneToManyPersister extends Doctrine_CollectionPersister_Abstract
{
}
?>
\ No newline at end of file
......@@ -78,6 +78,27 @@ class Doctrine_Connection_UnitOfWork
*/
protected $_deletedEntities = array();
/**
* All collection deletions.
*
* @var array
*/
protected $_collectionDeletions = array();
/**
* All collection creations.
*
* @var array
*/
protected $_collectionCreations = array();
/**
* All collection updates.
*
* @var array
*/
protected $_collectionUpdates = array();
/**
* The EntityManager the UnitOfWork belongs to.
*
......@@ -115,16 +136,16 @@ class Doctrine_Connection_UnitOfWork
public function commit()
{
// Detect changes in managed entities (mark dirty)
//TODO: Consider using registerDirty() in Entity#set() instead if its
// more performant.
foreach ($this->_identityMap as $entities) {
//TODO: Consider using registerDirty() in Entity#_set() instead if its
// 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) &&
empty($this->_deletedEntities) &&
......@@ -142,7 +163,11 @@ class Doctrine_Connection_UnitOfWork
$this->_executeUpdates($class);
}
// Deletions come last and need to be in reverse commit order
//TODO: collection deletions
//TODO: collection updates (deleteRows, updateRows, insertRows)
//TODO: collection recreations
// Entity deletions come last and need to be in reverse commit order
for ($count = count($commitOrder), $i = $count - 1; $i >= 0; $i--) {
$this->_executeDeletions($commitOrder[$i]);
}
......@@ -232,7 +257,7 @@ class Doctrine_Connection_UnitOfWork
foreach ($node->getClass()->getAssociationMappings() as $assocMapping) {
//TODO: should skip target classes that are not in the changeset.
if ($assocMapping->isOwningSide()) {
$targetClass = $assocMapping->getTargetClass();
$targetClass = $this->_em->getClassMetadata($assocMapping->getTargetEntityName());
$targetClassName = $targetClass->getClassName();
// if the target class does not yet have a node, create it
if ( ! $this->_commitOrderCalculator->hasNodeWithKey($targetClassName)) {
......@@ -711,6 +736,38 @@ class Doctrine_Connection_UnitOfWork
$this->_commitOrderCalculator->clear();
}
public function scheduleCollectionUpdate(Doctrine_Collection $coll)
{
$this->_collectionUpdates[] = $coll;
}
public function isCollectionScheduledForUpdate(Doctrine_Collection $coll)
{
//...
}
public function scheduleCollectionDeletion(Doctrine_Collection $coll)
{
//TODO: if $coll is already scheduled for recreation ... what to do?
// Just remove $coll from the scheduled recreations?
$this->_collectionDeletions[] = $coll;
}
public function isCollectionScheduledForDeletion(Doctrine_Collection $coll)
{
//...
}
public function scheduleCollectionRecreation(Doctrine_Collection $coll)
{
$this->_collectionRecreations[] = $coll;
}
public function isCollectionScheduledForRecreation(Doctrine_Collection $coll)
{
//...
}
// Stuff from 0.11/1.0 that we will need later (need to modify it though)
......
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.org>.
*/
#namespace Doctrine::DBAL::Platforms;
......@@ -9,6 +28,7 @@
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @author Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library)
*/
abstract class Doctrine_DatabasePlatform
{
......
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.org>.
*/
/**
* The MySqlPlatform provides the behavior, features and SQL dialect of the
......
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.org>.
*/
/**
* Base class for all DatabasePlatforms. The DatabasePlatforms are the central
* point of abstraction of platform-specific behaviors, features and SQL dialects.
* They are a passive source of information.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @author Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library)
*/
class Doctrine_DatabasePlatform_OraclePlatform extends Doctrine_DatabasePlatform
{
/**
......
......@@ -23,7 +23,7 @@
/**
* Base class for all Entities (objects with persistent state in a RDBMS that are
* managed by Doctrine).
* managed by Doctrine). Kind of a Layer Suptertype.
*
* 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
......@@ -40,7 +40,7 @@
* @since 2.0
* @version $Revision: 4342 $
*/
abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
abstract class Doctrine_Entity implements ArrayAccess, Serializable
{
/**
* MANAGED
......@@ -145,23 +145,37 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
private $_state;
/**
* The names of fields that have been modified but not yet persisted.
* The changes that happened to fields of the entity.
* Keys are field names, values oldValue => newValue tuples.
*
* @var array
* @todo Rename to $_changeSet
*/
private $_modified = 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 persistence of the entity.
* The EntityManager that is responsible for the persistent state of the entity.
*
* @var Doctrine::ORM::EntityManager
*/
......@@ -175,6 +189,14 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
*/
private $_oid;
/**
* Flag that indicates whether the entity is dirty.
* (which means it has local changes)
*
* @var boolean
*/
//private $_isDirty = false;
/**
* Constructor.
* Creates a new Entity instance.
......@@ -238,7 +260,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
*
* Part of the implementation of the Serializable interface.
*
* @return array
* @return string
*/
public function serialize()
{
......@@ -347,19 +369,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
if ($state == null) {
return $this->_state;
}
/* TODO: Do we really need this check? This is only for internal use after all. */
switch ($state) {
case self::STATE_MANAGED:
case self::STATE_DELETED:
case self::STATE_DETACHED:
case self::STATE_NEW:
case self::STATE_LOCKED:
$this->_state = $state;
break;
default:
throw Doctrine_Entity_Exception::invalidState($state);
}
}
/**
......@@ -374,39 +384,83 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
/**
* Gets the value of a field (regular field or reference).
* If the field is not yet loaded this method does NOT load it.
*
* @param $name name of the property
* @throws Doctrine_Entity_Exception if trying to get an unknown field
* @return mixed
* @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_Null::$INSTANCE;
if (isset($this->_data[$fieldName])) {
return $this->_internalGetField($fieldName);
return $this->_data[$fieldName] !== $nullObj ?
$this->_data[$fieldName] : null;
} else if (isset($this->_references[$fieldName])) {
return $this->_internalGetReference($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 {
throw Doctrine_Entity_Exception::unknownField($fieldName);
return null;
}
} else {
throw Doctrine_Entity_Exception::invalidField($fieldName);
}
}
}
/**
* Sets the value of a field (regular field or reference).
* If the field is not yet loaded this method does NOT load it.
*
* @param $name name of the field
* @throws Doctrine_Entity_Exception if trying to get an unknown field
* @return mixed
* @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)) {
return $this->_internalSetField($fieldName, $value);
} else if ($this->_class->hasRelation($fieldName)) {
return $this->_internalSetReference($fieldName, $value);
$old = isset($this->_data[$fieldName]) ? $this->_data[$fieldName] : null;
// NOTE: Common case: $old != $value. Special case: null == 0 (TRUE), which
// is addressed by the type comparison.
if ($old != $value || gettype($old) != gettype($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();
if ($old instanceof Doctrine_Collection) {
$this->_em->getUnitOfWork()->scheduleCollectionDeletion($old);
}
if ($value instanceof Doctrine_Collection) {
$this->_em->getUnitOfWork()->scheduleCollectionRecreation($value);
}
}
} else {
throw Doctrine_Entity_Exception::unknownField($fieldName);
throw Doctrine_Entity_Exception::invalidField($fieldName);
}
}
private function _registerDirty()
{
if ($this->_state == self::STATE_MANAGED &&
! $this->_em->getUnitOfWork()->isRegisteredDirty($this)) {
$this->_em->getUnitOfWork()->registerDirty($this);
}
}
......@@ -420,7 +474,6 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
*
* @param string $fieldName
* @return mixed
* @todo Rename to _unsafeGetField()
*/
final public function _internalGetField($fieldName)
{
......@@ -447,6 +500,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
}
/**
* INTERNAL:
* Gets a reference to another Entity.
*
* NOTE: Use of this method in userland code is strongly discouraged.
......@@ -464,16 +518,15 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
/**
* INTERNAL:
* Sets a reference to another Entity.
* 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
* @todo Refactor. What about composite keys?
* @todo Rename to _unsafeSetReference()
* @todo Refactor.
*/
final public function _internalSetReference($name, $value)
final public function _internalSetReference_OLD($name, $value)
{
if ($value === Doctrine_Null::$INSTANCE) {
$this->_references[$name] = $value;
......@@ -486,7 +539,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
if ($rel instanceof Doctrine_Relation_ForeignKey ||
$rel instanceof Doctrine_Relation_LocalKey) {
if ( ! $rel->isOneToOne()) {
// one-to-many relation found
// one-to-many relation
if ( ! $value instanceof Doctrine_Collection) {
throw Doctrine_Entity_Exception::invalidValueForOneToManyReference();
}
......@@ -524,42 +577,69 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
}
/**
* Generic getter for all persistent fields.
* INTERNAL:
* Sets a reference to another entity or a collection of entities.
*
* @param string $fieldName Name of the field.
* @return mixed
* NOTE: Use of this method in userland code is strongly discouraged.
*
* @param string $fieldName
* @param mixed $value
* @todo Refactor.
*/
final public function get($fieldName)
final public function _internalSetReference($name, $value)
{
if ($getter = $this->_getCustomAccessor($fieldName)) {
return $this->$getter();
if ($value === Doctrine_Null::$INSTANCE) {
$this->_references[$name] = $value;
return;
}
// Use built-in accessor functionality
$nullObj = Doctrine_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 {
$class = $this->_class;
if ($class->hasField($fieldName)) {
return null;
} else if ($class->hasRelation($fieldName)) {
$rel = $class->getRelation($fieldName);
if ($rel->isLazilyLoaded()) {
$this->_references[$fieldName] = $rel->lazyLoadFor($this);
return $this->_references[$fieldName] !== $nullObj ?
$this->_references[$fieldName] : null;
} else {
return null;
$rel = $this->_class->getAssociationMapping($name);
// one-to-many or one-to-one relation
if ($rel->isOneToOne() || $rel->isOneToMany()) {
if ( ! $rel->isOneToOne()) {
// one-to-many relation
if ( ! $value instanceof Doctrine_Collection) {
throw Doctrine_Entity_Exception::invalidValueForOneToManyReference();
}
if (isset($this->_references[$name])) {
$this->_references[$name]->setData($value->getData());
return;
}
} else {
throw Doctrine_Entity_Exception::invalidField($fieldName);
$relatedClass = $value->getClass();
//$foreignFieldName = $rel->getForeignFieldName();
//$localFieldName = $rel->getLocalFieldName();
// one-to-one relation found
if ( ! ($value instanceof Doctrine_Entity)) {
throw Doctrine_Entity_Exception::invalidValueForOneToOneReference();
}
}
} else if ($rel instanceof Doctrine_Relation_Association) {
if ( ! ($value instanceof Doctrine_Collection)) {
throw Doctrine_Entity_Exception::invalidValueForManyToManyReference();
}
}
$this->_references[$name] = $value;
}
/**
* 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);
}
/**
......@@ -630,42 +710,20 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
}
/**
* Generic setter for persistent fields.
* 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);
}
if ($this->_class->hasField($fieldName)) {
/*if ($value instanceof Doctrine_Entity) {
$type = $class->getTypeOf($fieldName);
// FIXME: composite key support
$ids = $value->identifier();
$id = count($ids) > 0 ? array_pop($ids) : null;
if ($id !== null && $type !== 'object') {
$value = $id;
}
}*/
$old = isset($this->_data[$fieldName]) ? $this->_data[$fieldName] : null;
//FIXME: null == 0 => true
if ($old != $value) {
$this->_data[$fieldName] = $value;
$this->_modified[$fieldName] = array($old => $value);
if ($this->isNew() && $this->_class->isIdentifier($fieldName)) {
$this->_id[$fieldName] = $value;
}
}
} else if ($this->_class->hasRelation($fieldName)) {
$this->_internalSetReference($fieldName, $value);
} else {
throw Doctrine_Entity_Exception::invalidField($fieldName);
}
$this->_set($fieldName, $value);
}
/**
......@@ -697,7 +755,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
/**
* Clears the value of a field.
*
* NOTE: Invoked by Doctrine::ORM::Access#__unset().
* Invoked by Doctrine::ORM::Access#__unset().
*
* @param string $name
* @return void
......@@ -708,7 +766,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
$this->_data[$fieldName] = array();
} else if (isset($this->_references[$fieldName])) {
if ($this->_references[$fieldName] instanceof Doctrine_Entity) {
// todo: delete related record when saving $this
// todo: delete related record when saving $this (ONLY WITH deleteOrphans!)
$this->_references[$fieldName] = Doctrine_Null::$INSTANCE;
} else if ($this->_references[$fieldName] instanceof Doctrine_Collection) {
$this->_references[$fieldName]->setData(array());
......@@ -722,72 +780,14 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
*
* @return array
*/
final public function _getChangeSet()
final public function _getDataChangeSet()
{
//return $this->_changeSet;
return $this->_dataChangeSet;
}
/**
* Returns an array of modified fields and values with data preparation
* adds column aggregation inheritance and converts Records into primary key values
*
* @param array $array
* @return array
* @todo What about a little bit more expressive name? getPreparedData?
* @todo Does not look like the best place here ...
* @todo Prop: Move to EntityPersister. There call _getChangeSet() and apply this logic.
*/
final public function getPrepared(array $array = array())
final public function _getReferenceChangeSet()
{
$dataSet = array();
if (empty($array)) {
$modifiedFields = $this->_modified;
}
foreach ($modifiedFields as $field) {
$type = $this->_class->getTypeOfField($field);
if ($this->_data[$field] === Doctrine_Null::$INSTANCE) {
$dataSet[$field] = null;
continue;
}
switch ($type) {
case 'array':
case 'object':
$dataSet[$field] = serialize($this->_data[$field]);
break;
case 'gzip':
$dataSet[$field] = gzcompress($this->_data[$field],5);
break;
case 'boolean':
$dataSet[$field] = $this->_em->getConnection()
->convertBooleans($this->_data[$field]);
break;
case 'enum':
$dataSet[$field] = $this->_class->enumIndex($field, $this->_data[$field]);
break;
default:
$dataSet[$field] = $this->_data[$field];
}
}
// @todo cleanup
// populates the discriminator field in Single & Class Table Inheritance
if ($this->_class->getInheritanceType() == Doctrine::INHERITANCE_TYPE_JOINED ||
$this->_class->getInheritanceType() == Doctrine::INHERITANCE_TYPE_SINGLE_TABLE) {
$discCol = $this->_class->getInheritanceOption('discriminatorColumn');
$discMap = $this->_class->getInheritanceOption('discriminatorMap');
$old = $this->get($discCol, false);
$discValue = array_search($this->_entityName, $discMap);
if ((string) $old !== (string) $discValue || $old === null) {
$dataSet[$discCol] = $discValue;
$this->_data[$discCol] = $discValue;
}
}
return $dataSet;
return $this->_referenceChangeSet;
}
/**
......@@ -808,7 +808,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
*/
final public function isModified()
{
return count($this->_modified) > 0;
return count($this->_fieldChangeSet) > 0;
}
/**
......@@ -830,7 +830,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
$this->_id[$name] = $id;
$this->_data[$name] = $id;
}
$this->_modified = array();
$this->_dataChangeSet = array();
}
/**
......@@ -948,6 +948,8 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
* 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)
{
......@@ -967,4 +969,107 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
$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->remove($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->remove($name);
}
}
......@@ -29,7 +29,6 @@
#use Doctrine::ORM::Internal::UnitOfWork;
#use Doctrine::ORM::Mapping::ClassMetadata;
/**
* The EntityManager is the central access point to ORM functionality.
*
......@@ -38,12 +37,28 @@
* @since 2.0
* @version $Revision$
* @author Roman Borschel <roman@code-factory.org>
* @todo package:orm
*/
class Doctrine_EntityManager
{
/**
* IMMEDIATE: Flush occurs automatically after each operation that issues database
* queries. No operations are queued.
*/
const FLUSHMODE_IMMEDIATE = 'immediated';
/**
* AUTO: Flush occurs automatically in the following situations:
* - Before any query executions (to prevent getting stale data)
* - On EntityManager#commit()
*/
const FLUSHMODE_AUTO = 'auto';
/**
* COMMIT: Flush occurs automatically only on EntityManager#commit().
*/
const FLUSHMODE_COMMIT = 'commit';
/**
* MANUAL: Flush occurs never automatically. The only way to flush is
* through EntityManager#flush().
*/
const FLUSHMODE_MANUAL = 'manual';
/**
......@@ -68,19 +83,6 @@ class Doctrine_EntityManager
*/
private $_conn;
/**
* Flush modes enumeration.
*/
private static $_flushModes = array(
// auto: Flush occurs automatically after each operation that issues database
// queries. No operations are queued.
self::FLUSHMODE_AUTO,
// commit: Flush occurs automatically at transaction commit.
self::FLUSHMODE_COMMIT,
// manual: Flush occurs never automatically.
self::FLUSHMODE_MANUAL
);
/**
* The metadata factory, used to retrieve the metadata of entity classes.
*
......@@ -160,7 +162,6 @@ class Doctrine_EntityManager
* Gets the metadata for a class. Alias for getClassMetadata().
*
* @return Doctrine_Metadata
* @todo package:orm
*/
public function getMetadata($className)
{
......@@ -324,12 +325,20 @@ class Doctrine_EntityManager
*/
public function setFlushMode($flushMode)
{
if ( ! in_array($flushMode, self::$_flushModes)) {
if ( ! $this->_isFlushMode($flushMode)) {
throw Doctrine_EntityManager_Exception::invalidFlushMode();
}
$this->_flushMode = $flushMode;
}
private function _isFlushMode($value)
{
return $value == self::FLUSHMODE_AUTO ||
$value == self::FLUSHMODE_COMMIT ||
$value == self::FLUSHMODE_IMMEDIATE ||
$value == self::FLUSHMODE_MANUAL;
}
/**
* Gets the currently used flush mode.
*
......@@ -365,10 +374,9 @@ class Doctrine_EntityManager
}
/**
* getResultCacheDriver
* Gets the result cache driver used by the EntityManager.
*
* @return Doctrine_Cache_Interface
* @todo package:orm
* @return Doctrine::ORM::Cache::CacheDriver The cache driver.
*/
public function getResultCacheDriver()
{
......@@ -383,7 +391,6 @@ class Doctrine_EntityManager
* getQueryCacheDriver
*
* @return Doctrine_Cache_Interface
* @todo package:orm
*/
public function getQueryCacheDriver()
{
......@@ -396,31 +403,45 @@ class Doctrine_EntityManager
/**
* Saves the given entity, persisting it's state.
*
* @param Doctrine::ORM::Entity $entity
* @return void
*/
public function save(Doctrine_Entity $entity)
{
$this->_unitOfWork->save($entity);
if ($this->_flushMode == self::FLUSHMODE_AUTO) {
if ($this->_flushMode == self::FLUSHMODE_IMMEDIATE) {
$this->flush();
}
}
/**
* Removes the given entity from the persistent store.
*
* @param Doctrine::ORM::Entity $entity
* @return void
*/
public function delete(Doctrine_Entity $entity)
{
$this->_unitOfWork->delete($entity);
if ($this->_flushMode == self::FLUSHMODE_IMMEDIATE) {
$this->flush();
}
}
/**
* Refreshes the persistent state of the entity from the database.
* Refreshes the persistent state of the entity from the database,
* overriding any local changes that have not yet been persisted.
*
* @param Doctrine_Entity $entity
* @param Doctrine::ORM::Entity $entity
* @return void
* @todo FIX Impl
*/
public function refresh(Doctrine_Entity $entity)
{
//...
$this->_mergeData($entity, $entity->getRepository()->find(
$entity->identifier(), Doctrine_Query::HYDRATE_ARRAY),
true);
}
/**
......@@ -463,7 +484,7 @@ class Doctrine_EntityManager
*
* @param string $className The name of the entity class.
* @param array $data The data for the entity.
* @return Doctrine_Entity
* @return Doctrine::ORM::Entity
*/
public function createEntity($className, array $data)
{
......@@ -488,6 +509,7 @@ class Doctrine_EntityManager
$idHash = $this->_unitOfWork->getIdentifierHash($id);
if ($entity = $this->_unitOfWork->tryGetByIdHash($idHash,
$classMetadata->getRootClassName())) {
$this->_mergeData($entity, $data);
return $entity;
} else {
$entity = new $className;
......@@ -501,10 +523,35 @@ class Doctrine_EntityManager
return $entity;
}
/**
* Merges the given data into the given entity, optionally overriding
* local changes.
*
* @param Doctrine::ORM::Entity $entity
* @param array $data
* @param boolean $overrideLocalChanges
* @return void
*/
private function _mergeData(Doctrine_Entity $entity, array $data, $overrideLocalChanges = false) {
if ($overrideLocalChanges) {
foreach ($data as $field => $value) {
$entity->_internalSetField($field, $value);
}
} else {
foreach ($data as $field => $value) {
if ( ! $entity->contains($field) || $entity->_internalGetField($field) === null) {
$entity->_internalSetField($field, $value);
}
}
}
}
/**
* Checks if the instance is managed by the EntityManager.
*
* @return boolean
* @param Doctrine::ORM::Entity $entity
* @return boolean TRUE if this EntityManager currently manages the given entity
* (and has it in the identity map), FALSE otherwise.
*/
public function contains(Doctrine_Entity $entity)
{
......@@ -513,8 +560,11 @@ class Doctrine_EntityManager
}
/**
* INTERNAL:
* For internal hydration purposes only.
* INTERNAL: For internal hydration purposes only.
*
* Gets the temporarily stored entity data.
*
* @return array
*/
public function _getTmpEntityData()
{
......@@ -528,6 +578,8 @@ class Doctrine_EntityManager
* class to instantiate. If no discriminator column is found, the given
* classname will be returned.
*
* @param array $data
* @param string $className
* @return string The name of the class to instantiate.
*/
private function _inferCorrectClassName(array $data, $className)
......@@ -562,6 +614,7 @@ class Doctrine_EntityManager
* Sets the EventManager used by the EntityManager.
*
* @param Doctrine::Common::EventManager $eventManager
* @return void
*/
public function setEventManager(Doctrine_EventManager $eventManager)
{
......@@ -572,6 +625,7 @@ class Doctrine_EntityManager
* Sets the Configuration used by the EntityManager.
*
* @param Doctrine::Common::Configuration $config
* @return void
*/
public function setConfiguration(Doctrine_Configuration $config)
{
......
......@@ -55,7 +55,7 @@ abstract class Doctrine_EntityPersister_Abstract
/**
* The Doctrine_Connection object that the database connection of this mapper.
*
* @var Doctrine_Connection $conn
* @var Doctrine::DBAL::Connection $conn
*/
protected $_conn;
......@@ -85,17 +85,58 @@ abstract class Doctrine_EntityPersister_Abstract
public function insert(Doctrine_Entity $entity)
{
$insertData = array();
$dataChangeSet = $entity->_getDataChangeSet();
$referenceChangeSet = $entity->_getReferenceChangeSet();
foreach ($referenceChangeSet as $field => $change) {
list($old, $new) = each($change);
$assocMapping = $entity->getClass()->getAssociationMapping($field);
if ($assocMapping->isOneToOne()) {
if ($assocMapping->isInverseSide()) {
//echo "INVERSE!";
continue; // ignore inverse side
}
foreach ($assocMapping->getSourceToTargetKeyColumns() as $localColumn => $foreignColumn) {
//echo "$localColumn -- $foreignColumn<br/>";
//$insertData[$localColumn] = 1; //FIX
}
// ... set the foreign key column to the id of the related entity ($new)
}
//...
}
foreach ($dataChangeSet as $field => $change) {
$insertData[$entity->getClass()->getColumnName($field)] = current($change);
}
//TODO: perform insert
$this->_conn->insert($entity->getClass()->getTableName(), $insertData);
}
public function update(Doctrine_Entity $entity)
{
$dataChangeSet = $entity->_getDataChangeSet();
$referenceChangeSet = $entity->_getReferenceChangeSet();
foreach ($referenceChangeSet as $field => $change) {
$assocMapping = $entity->getClass()->getAssociationMapping($field);
if ($assocMapping instanceof Doctrine_Association_OneToOneMapping) {
if ($assocMapping->isInverseSide()) {
continue; // ignore inverse side
}
// ... null out the foreign key
}
//...
}
//TODO: perform update
}
public function delete(Doctrine_Entity $entity)
{
//TODO: perform delete
}
/**
......@@ -182,6 +223,64 @@ abstract class Doctrine_EntityPersister_Abstract
return $converted;
}
/**
* Returns an array of modified fields and values with data preparation
* adds column aggregation inheritance and converts Records into primary key values
*
* @param array $array
* @return array
* @todo Move to EntityPersister. There call _getChangeSet() and apply this logic.
*/
public function prepareData(array $data = array())
{
$dataSet = array();
$modifiedFields = $fields;
foreach ($data as $field => $value) {
$type = $this->_classMetadata->getTypeOfField($field);
if ($value === Doctrine_Null::$INSTANCE) {
$dataSet[$field] = null;
continue;
}
switch ($type) {
case 'array':
case 'object':
$dataSet[$field] = serialize($value);
break;
case 'gzip':
$dataSet[$field] = gzcompress($value, 5);
break;
case 'boolean':
$dataSet[$field] = $this->_em->getConnection()
->convertBooleans($value);
break;
case 'enum':
$dataSet[$field] = $this->_class->enumIndex($field, $value);
break;
default:
$dataSet[$field] = $value;
}
}
// @todo cleanup
// populates the discriminator field in Single & Class Table Inheritance
if ($this->_classMetadata->getInheritanceType() == Doctrine_ClassMetadata::INHERITANCE_TYPE_JOINED ||
$this->_class->getInheritanceType() == Doctrine_ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE) {
$discCol = $this->_classMetadata->getInheritanceOption('discriminatorColumn');
$discMap = $this->_classMetadata->getInheritanceOption('discriminatorMap');
$old = $this->get($discCol, false);
$discValue = array_search($this->_entityName, $discMap);
if ((string) $old !== (string) $discValue || $old === null) {
$dataSet[$discCol] = $discValue;
//$this->_data[$discCol] = $discValue;
}
}
return $dataSet;
}
......@@ -226,75 +325,6 @@ abstract class Doctrine_EntityPersister_Abstract
return $this->_em;
}
/**
* prepareValue
* this method performs special data preparation depending on
* the type of the given column
*
* 1. It unserializes array and object typed columns
* 2. Uncompresses gzip typed columns
* 3. Gets the appropriate enum values for enum typed columns
* 4. Initializes special null object pointer for null values (for fast column existence checking purposes)
*
* example:
* <code type='php'>
* $field = 'name';
* $value = null;
* $table->prepareValue($field, $value); // Doctrine_Null
* </code>
*
* @throws Doctrine_Table_Exception if unserialization of array/object typed column fails or
* @throws Doctrine_Table_Exception if uncompression of gzip typed column fails *
* @param string $field the name of the field
* @param string $value field value
* @param string $typeHint A hint on the type of the value. If provided, the type lookup
* for the field can be skipped. Used i.e. during hydration to
* improve performance on large and/or complex results.
* @return mixed prepared value
* @todo To EntityManager. Make private and use in createEntity().
* .. Or, maybe better: Move to hydrator for performance reasons.
*/
/*public function prepareValue($fieldName, $value, $typeHint = null)
{
if ($value === $this->_nullObject) {
return $this->_nullObject;
} else if ($value === null) {
return null;
} else {
$type = is_null($typeHint) ? $this->_classMetadata->getTypeOf($fieldName) : $typeHint;
switch ($type) {
case 'integer':
case 'string';
// don't do any casting here PHP INT_MAX is smaller than what the databases support
break;
case 'enum':
return $this->_classMetadata->enumValue($fieldName, $value);
break;
case 'boolean':
return (boolean) $value;
break;
case 'array':
case 'object':
if (is_string($value)) {
$value = unserialize($value);
if ($value === false) {
throw new Doctrine_Mapper_Exception('Unserialization of ' . $fieldName . ' failed.');
}
return $value;
}
break;
case 'gzip':
$value = gzuncompress($value);
if ($value === false) {
throw new Doctrine_Mapper_Exception('Uncompressing of ' . $fieldName . ' failed.');
}
return $value;
break;
}
}
return $value;
}*/
/**
* getComponentName
*
......
......@@ -24,8 +24,6 @@
* as is the case in Single Table Inheritance & Concrete Table Inheritance.
*
* @author Roman Borschel <roman@code-factory.org>
* @package Doctrine
* @subpackage Abstract
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @version $Revision$
* @link www.phpdoctrine.org
......@@ -72,7 +70,6 @@ class Doctrine_EntityPersister_Standard extends Doctrine_EntityPersister_Abstrac
return false;
}
//$class = $record->getClassMetadata();
$class = $this->_classMetadata;
$identifier = $class->getIdentifier();
$fields = $this->_convertFieldToColumnNames($fields, $class);
......
......@@ -19,11 +19,11 @@
* <http://www.phpdoctrine.org>.
*/
#namespace Doctrine::Common;
#namespace Doctrine::Common::Event;
/**
* The EventManager is the central point of Doctrine's event listener system.
* Listeners are registered on the manager and events are dispatch through the
* Listeners are registered on the manager and events are dispatched through the
* manager.
*
* @author Roman Borschel <roman@code-factory.org>
......@@ -100,6 +100,17 @@ class Doctrine_EventManager
$this->_listeners[$event] = $listener;
}
}
/**
* Adds an EventSubscriber. The subscriber is asked for all the events he is
* interested in and added as a listener for these events.
*
* @param Doctrine::Common::Event::EventSubscriber $subscriber The subscriber.
*/
public function addEventSubscriber(Doctrine_EventSubscriber $subscriber)
{
$this->addEventListener($subscriber->getSubscribedEvents(), $subscriber);
}
}
?>
\ No newline at end of file
......@@ -49,7 +49,7 @@ class Doctrine_Exception extends Exception
return $this->_innerException;
}
public static function notImplemented($method, $class)
public static function notYetImplemented($method, $class)
{
return new self("The method '$method' is not implemented in the class '$class'.");
}
......
......@@ -70,8 +70,8 @@ class Doctrine_Hydrator_RecordDriver
public function initRelatedCollection(Doctrine_Entity $entity, $name)
{
if ( ! isset($this->_initializedRelations[$entity->getOid()][$name])) {
$relation = $entity->getClass()->getRelation($name);
$relatedClass = $relation->getTable();
$relation = $entity->getClass()->getAssociationMapping($name);
$relatedClass = $this->_em->getClassMetadata($relation->getTargetEntityName());
$coll = $this->getElementCollection($relatedClass->getClassName());
$coll->setReference($entity, $relation);
$entity->_internalSetReference($name, $coll);
......
......@@ -49,8 +49,6 @@
* That's why the performance of the _gatherRowData() methods which are responsible
* for the "numRowsInResult * numColumnsInResult" part is crucial to fast hydration.
*
* @package Doctrine
* @subpackage Hydrator
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.org
* @since 1.0
......@@ -212,7 +210,7 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
$entityName = $map['metadata']->getClassName();
$parent = $map['parent'];
$relation = $map['relation'];
$relationAlias = $relation->getAlias();
$relationAlias = $relation->getSourceFieldName();//$relation->getAlias();
$path = $parent . '.' . $dqlAlias;
// pick the right element that will get the associated element attached
......
......@@ -9,8 +9,8 @@ class Doctrine_MappingException extends Doctrine_Exception
{
public static function identifierRequired($entityName)
{
return new self("No identifier specified for Entity '$entityName'."
. " Every Entity must have an identifier.");
return new self("No identifier/primary key specified for Entity '$entityName'."
. " Every Entity must have an identifier/primary key.");
}
public static function invalidInheritanceType($type)
......@@ -28,6 +28,25 @@ class Doctrine_MappingException extends Doctrine_Exception
return new self("Id generators can't be used with a composite id.");
}
public static function missingFieldName()
{
return new self("The association mapping misses the 'fieldName' attribute.");
}
public static function missingTargetEntity($fieldName)
{
return new self("The association mapping '$fieldName' misses the 'targetEntity' attribute.");
}
public static function missingSourceEntity($fieldName)
{
return new self("The association mapping '$fieldName' misses the 'sourceEntity' attribute.");
}
public static function mappingNotFound($fieldName)
{
return new self("No mapping found for field '$fieldName'.");
}
}
?>
\ No newline at end of file
......@@ -20,10 +20,6 @@
* <http://www.phpdoctrine.org>.
*/
Doctrine::autoload('Doctrine_Query_AbstractResult');
Doctrine::autoload('Doctrine_Query_ParserResult');
Doctrine::autoload('Doctrine_Query_QueryResult');
/**
* Doctrine_Query_CacheHandler
*
......@@ -136,8 +132,8 @@ abstract class Doctrine_Query_CacheHandler
$queryComponents[$alias]['table'] = $queryComponents[$alias]['mapper']->getTable();
} else {
$queryComponents[$alias]['parent'] = $e[0];
$queryComponents[$alias]['relation'] = $queryComponents[$e[0]]['table']->getRelation($e[1]);
$queryComponents[$alias]['mapper'] = $query->getConnection()->getMapper($queryComponents[$alias]['relation']->getForeignComponentName());
$queryComponents[$alias]['relation'] = $queryComponents[$e[0]]['table']->getAssociation($e[1]);
$queryComponents[$alias]['mapper'] = $query->getConnection()->getMapper($queryComponents[$alias]['relation']->getTargetEntityName());
$queryComponents[$alias]['table'] = $queryComponents[$alias]['mapper']->getTable();
}
......
......@@ -50,7 +50,7 @@ class Doctrine_Query_Production_Join extends Doctrine_Query_Production
$this->_parser->match(Doctrine_Query_Token::T_LEFT);
$this->_joinType = 'LEFT';
} elseif ($this->_isNextToken(Doctrine_Query_Token::T_INNER)) {
} else if ($this->_isNextToken(Doctrine_Query_Token::T_INNER)) {
$this->_parser->match(Doctrine_Query_Token::T_INNER);
}
......@@ -64,7 +64,7 @@ class Doctrine_Query_Production_Join extends Doctrine_Query_Production
$this->_whereType = 'ON';
$this->_conditionalExpression = $this->AST('ConditionalExpression', $paramHolder);
} elseif ($this->_isNextToken(Doctrine_Query_Token::T_WITH)) {
} else if ($this->_isNextToken(Doctrine_Query_Token::T_WITH)) {
$this->_parser->match(Doctrine_Query_Token::T_WITH);
$this->_conditionalExpression = $this->AST('ConditionalExpression', $paramHolder);
......
......@@ -93,7 +93,7 @@ class Doctrine_Query_Production_PathExpression extends Doctrine_Query_Production
$relationName = $this->_identifiers[$i];
$path .= '.' . $relationName;
if ( ! $classMetadata->hasRelation($relationName)) {
if ( ! $classMetadata->hasAssociation($relationName)) {
$className = $classMetadata->getClassName();
$this->_parser->semanticalError(
......
......@@ -73,7 +73,7 @@ class Doctrine_Query_Production_PathExpressionEndingWithAsterisk extends Doctrin
$relationName = $this->_identifiers[$i];
$path .= '.' . $relationName;
if ( ! $classMetadata->hasRelation($relationName)) {
if ( ! $classMetadata->hasAssociation($relationName)) {
$className = $classMetadata->getClassName();
$this->_parser->semanticalError(
......
......@@ -191,7 +191,7 @@ class Doctrine_Query_Production_RangeVariableDeclaration extends Doctrine_Query_
}
} else {
// We don't have the query component yet
if ( ! $classMetadata->hasRelation($relationName)) {
if ( ! $classMetadata->hasAssociation($relationName)) {
$className = $classMetadata->getClassName();
$this->_parser->semanticalError("Relation '{$relationName}' does not exist in component '{$className}'");
......@@ -199,13 +199,13 @@ class Doctrine_Query_Production_RangeVariableDeclaration extends Doctrine_Query_
return;
}
// Retrieving ClassMetadata and Mapper
// Retrieving ClassMetadata
try {
$relation = $classMetadata->getRelation($relationName);
$classMetadata = $relation->getClassMetadata();
$relation = $classMetadata->getAssociationMapping($relationName);
$targetClassMetadata = $this->_em->getClassMetadata($relation->getTargetEntityName());
$queryComponent = array(
'metadata' => $classMetadata,
'metadata' => $targetClassMetadata,
'parent' => $parent,
'relation' => $relation,
'map' => null,
......@@ -226,7 +226,7 @@ class Doctrine_Query_Production_RangeVariableDeclaration extends Doctrine_Query_
$this->_identificationVariable = $path;
}
$tableAlias = $parserResult->generateTableAlias($classMetadata->getClassName());
$tableAlias = $parserResult->generateTableAlias($targetClassMetadata->getClassName());
//echo "Table alias: " . $tableAlias . "\n";
......
......@@ -154,7 +154,7 @@ abstract class Doctrine_Relation implements ArrayAccess
}
}
$this->definition = $def;
$this->_foreignMapper = $this->getTable()->getConnection()->getEntityPersister($def['class']);
$this->_foreignMapper = $this->getTable()->getEntityManager()->getEntityPersister($def['class']);
}
/**
......
......@@ -410,7 +410,7 @@ class Doctrine_Relation_Parser
*/
public function completeDefinition($def)
{
$conn = $this->_table->getConnection();
$conn = $this->_table->getEntityManager();
$def['table'] = $this->getImpl($def, 'class');
$def['localTable'] = $this->_table;
......
......@@ -187,7 +187,7 @@ class Doctrine_Schema_MsSqlSchemaManager extends Doctrine_Schema_SchemaManager
if ($query) {
$query .= ', ';
}
$query .= 'ADD ' . $this->conn->getDeclaration($fieldName, $field);
$query .= 'ADD ' . $this->getDeclaration($fieldName, $field);
}
}
......
......@@ -15,6 +15,8 @@ require_once 'Orm/Entity/AllTests.php';
// Tests
require_once 'Orm/UnitOfWorkTest.php';
require_once 'Orm/EntityManagerFactoryTest.php';
require_once 'Orm/EntityManagerTest.php';
require_once 'Orm/EntityPersisterTest.php';
class Orm_AllTests
{
......@@ -29,7 +31,8 @@ class Orm_AllTests
$suite->addTestSuite('Orm_UnitOfWorkTest');
$suite->addTestSuite('Orm_EntityManagerFactoryTest');
//$suite->addTestSuite('Orm_ConfigurableTestCase');
$suite->addTestSuite('Orm_EntityManagerTest');
$suite->addTestSuite('Orm_EntityPersisterTest');
$suite->addTest(Orm_Component_AllTests::suite());
$suite->addTest(Orm_Query_AllTests::suite());
......
......@@ -9,7 +9,7 @@ class Orm_Associations_OneToOneMappingTest extends Doctrine_OrmTestCase
'fieldName' => 'address',
'targetEntity' => 'Address',
'joinColumns' => array('address_id' => 'id'),
'sourceEntity' => 'Person' // This is normally filled by ClassMetadata
'sourceEntity' => 'Person', // This is normally filled by ClassMetadata
);
$oneToOneMapping = new Doctrine_Association_OneToOne($owningSideMapping);
......@@ -23,11 +23,16 @@ class Orm_Associations_OneToOneMappingTest extends Doctrine_OrmTestCase
$inverseSideMapping = array(
'fieldName' => 'person',
'sourceEntity' => 'Address',
'targetEntity' => 'Person',
'mappedBy' => 'address'
);
$oneToOneMapping = new Doctrine_Association_OneToOne($inverseSideMapping);
$this->assertEquals('address', $oneToOneMapping->getMappedByFieldName());
$this->assertEquals('Address', $oneToOneMapping->getSourceEntityName());
$this->assertEquals('Person', $oneToOneMapping->getTargetEntityName());
$this->assertTrue($oneToOneMapping->isInverseSide());
}
......
......@@ -26,7 +26,7 @@
* @author Bjarte Stien Karlsen <doctrine@bjartek.org>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.org
* @since 1.0
* @since 2.0
* @version $Revision: 3754 $
*/
require_once 'lib/DoctrineTestInit.php';
......@@ -95,23 +95,6 @@ class Orm_Component_AccessTest extends Doctrine_OrmTestCase
}
/**
* @test
*/
public function shouldSetArrayOfValusInRecord()
{
$this->user->setArray(array(
'username' => 'meus',
'id' => 22));
$this->assertEquals('meus', $this->user->username);
$this->assertEquals('meus', $this->user['username']);
$this->assertEquals(22, $this->user->id);
$this->assertEquals(22, $this->user['id']);
}
/**
* @test
* @expectedException Doctrine_Entity_Exception
......@@ -130,17 +113,6 @@ class Orm_Component_AccessTest extends Doctrine_OrmTestCase
$this->user['rat'] = 'meus';
}
/**
* @test
* @expectedException Doctrine_Entity_Exception
*/
public function shouldNotBeAbleToSetNonExistantFieldAsPartInSetArray()
{
$this->user->setArray(array(
'rat' => 'meus',
'id' => 22));
}
/**
* @test
......@@ -176,18 +148,6 @@ class Orm_Component_AccessTest extends Doctrine_OrmTestCase
$this->assertFalse(isset($col->test));
}
/**
*
* @test
* @expectedException Doctrine_Exception
*/
public function shouldNotBeAbleToSetNullFieldInRecord()
{
$this->user->offsetSet(null, 'test');
}
/**
* @test
* @expectedException Doctrine_Exception
......
<?php
require_once 'lib/DoctrineTestInit.php';
#namespace Doctrine::Tests::ORM;
/**
* EntityManager tests.
*/
class Orm_EntityManagerTest extends Doctrine_OrmTestCase
{
public function testSettingInvalidFlushModeThrowsException()
{
$prev = $this->_em->getFlushMode();
try {
$this->_em->setFlushMode('foobar');
$this->fail("Setting invalid flushmode did not trigger exception.");
} catch (Doctrine_EntityManager_Exception $expected) {}
$this->_em->setFlushMode($prev);
}
}
\ No newline at end of file
<?php
require_once 'lib/DoctrineTestInit.php';
require_once 'lib/mocks/Doctrine_EntityManagerMock.php';
require_once 'lib/mocks/Doctrine_ConnectionMock.php';
/**
* EntityPersister tests.
*/
class Orm_EntityPersisterTest extends Doctrine_OrmTestCase
{
private $_persister; // SUT
private $_connMock;
private $_emMock;
protected function setUp() {
parent::setUp();
$this->_connMock = new Doctrine_ConnectionMock(array());
$this->_emMock = new Doctrine_EntityManagerMock($this->_connMock);
$this->_connMock->setDatabasePlatform(new Doctrine_DatabasePlatformMock());
$this->_persister = new Doctrine_EntityPersister_Standard(
$this->_emMock, $this->_emMock->getClassMetadata("ForumUser"));
}
public function testTest() {
$user = new ForumUser();
$user->username = "romanb";
$user->avatar = new ForumAvatar();
$this->_persister->insert($user);
$inserts = $this->_connMock->getInserts();
//var_dump($inserts);
$this->assertTrue(isset($inserts['forum_user']));
$this->assertEquals(1, count($inserts['forum_user']));
$this->assertEquals(1, count($inserts['forum_user'][0]));
$this->assertTrue(isset($inserts['forum_user'][0]['username']));
$this->assertEquals('romanb', $inserts['forum_user'][0]['username']);
}
}
\ No newline at end of file
......@@ -123,7 +123,7 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
'p' => array(
'metadata' => $this->_em->getClassMetadata('CmsPhonenumber'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getRelation('phonenumbers'),
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('phonenumbers'),
'map' => null
)
);
......@@ -226,7 +226,7 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
'p' => array(
'metadata' => $this->_em->getClassMetadata('CmsPhonenumber'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getRelation('phonenumbers'),
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('phonenumbers'),
'map' => null,
'agg' => array('0' => 'numPhones')
)
......@@ -311,7 +311,7 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
'p' => array(
'metadata' => $this->_em->getClassMetadata('CmsPhonenumber'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getRelation('phonenumbers'),
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('phonenumbers'),
'map' => 'phonenumber'
)
);
......@@ -415,13 +415,13 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
'p' => array(
'metadata' => $this->_em->getClassMetadata('CmsPhonenumber'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getRelation('phonenumbers'),
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('phonenumbers'),
'map' => null
),
'a' => array(
'metadata' => $this->_em->getClassMetadata('CmsArticle'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getRelation('articles'),
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('articles'),
'map' => null
),
);
......@@ -571,19 +571,19 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
'p' => array(
'metadata' => $this->_em->getClassMetadata('CmsPhonenumber'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getRelation('phonenumbers'),
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('phonenumbers'),
'map' => null
),
'a' => array(
'metadata' => $this->_em->getClassMetadata('CmsArticle'),
'parent' => 'u',
'relation' => $this->_em->getClassMetadata('CmsUser')->getRelation('articles'),
'relation' => $this->_em->getClassMetadata('CmsUser')->getAssociationMapping('articles'),
'map' => null
),
'c' => array(
'metadata' => $this->_em->getClassMetadata('CmsComment'),
'parent' => 'a',
'relation' => $this->_em->getClassMetadata('CmsArticle')->getRelation('comments'),
'relation' => $this->_em->getClassMetadata('CmsArticle')->getAssociationMapping('comments'),
'map' => null
),
);
......@@ -779,7 +779,7 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
'b' => array(
'metadata' => $this->_em->getClassMetadata('ForumBoard'),
'parent' => 'c',
'relation' => $this->_em->getClassMetadata('ForumCategory')->getRelation('boards'),
'relation' => $this->_em->getClassMetadata('ForumCategory')->getAssociationMapping('boards'),
'map' => null
),
);
......
......@@ -84,7 +84,7 @@ class Orm_Query_IdentifierRecognitionTest extends Doctrine_OrmTestCase
$decl = $parserResult->getQueryComponent('p');
$this->assertTrue($decl['metadata'] instanceof Doctrine_ClassMetadata);
$this->assertTrue($decl['relation'] instanceof Doctrine_Relation);
$this->assertTrue($decl['relation'] instanceof Doctrine_Association);
$this->assertEquals('u', $decl['parent']);
$this->assertEquals(null, $decl['scalar']);
$this->assertEquals(null, $decl['map']);
......@@ -108,7 +108,7 @@ class Orm_Query_IdentifierRecognitionTest extends Doctrine_OrmTestCase
$decl = $parserResult->getQueryComponent('a');
$this->assertTrue($decl['metadata'] instanceof Doctrine_ClassMetadata);
$this->assertTrue($decl['relation'] instanceof Doctrine_Relation);
$this->assertTrue($decl['relation'] instanceof Doctrine_Association);
$this->assertEquals('u', $decl['parent']);
$this->assertEquals(null, $decl['scalar']);
$this->assertEquals(null, $decl['map']);
......@@ -116,7 +116,7 @@ class Orm_Query_IdentifierRecognitionTest extends Doctrine_OrmTestCase
$decl = $parserResult->getQueryComponent('pn');
$this->assertTrue($decl['metadata'] instanceof Doctrine_ClassMetadata);
$this->assertTrue($decl['relation'] instanceof Doctrine_Relation);
$this->assertTrue($decl['relation'] instanceof Doctrine_Association);
$this->assertEquals('u', $decl['parent']);
$this->assertEquals(null, $decl['scalar']);
$this->assertEquals('phonenumber', $decl['map']);
......
......@@ -8,6 +8,7 @@ class Doctrine_ConnectionMock extends Doctrine_Connection
protected $_driverName = 'Mock';
private $_sequenceModuleMock;
private $_platformMock;
private $_inserts = array();
public function __construct(array $params)
{
......@@ -30,6 +31,14 @@ class Doctrine_ConnectionMock extends Doctrine_Connection
return $this->_platformMock;
}
/**
* @override
*/
public function insert($tableName, array $data)
{
$this->_inserts[$tableName][] = $data;
}
/* Mock API */
public function setDatabasePlatform($platform)
......@@ -41,6 +50,16 @@ class Doctrine_ConnectionMock extends Doctrine_Connection
{
$this->_sequenceModuleMock = $seqManager;
}
public function getInserts()
{
return $this->_inserts;
}
public function reset()
{
$this->_inserts = array();
}
}
?>
\ No newline at end of file
......@@ -34,7 +34,18 @@ class CmsArticle extends Doctrine_Entity
'type' => 'integer',
'length' => 4
));
$mapping->hasMany('CmsComment as comments', array(
'local' => 'id', 'foreign' => 'article_id'));
/*$mapping->hasMany('CmsComment as comments', array(
'local' => 'id', 'foreign' => 'article_id'));*/
$mapping->mapOneToMany(array(
'fieldName' => 'comments',
'targetEntity' => 'CmsComment',
));
/*$mapping->mapManyToOne(array(
'fieldName' => 'author',
'joinColumns' => array('user_id' => 'id')
));*/
}
}
......@@ -36,9 +36,21 @@ class CmsUser extends Doctrine_Entity
'length' => 255
));
$mapping->hasMany('CmsPhonenumber as phonenumbers', array(
/*$mapping->hasMany('CmsPhonenumber as phonenumbers', array(
'local' => 'id', 'foreign' => 'user_id'));
$mapping->hasMany('CmsArticle as articles', array(
'local' => 'id', 'foreign' => 'user_id'));
'local' => 'id', 'foreign' => 'user_id'));*/
$mapping->mapOneToMany(array(
'fieldName' => 'phonenumbers',
'targetEntity' => 'CmsPhonenumber',
));
$mapping->mapOneToMany(array(
'fieldName' => 'articles',
'targetEntity' => 'CmsArticle',
));
}
}
<?php
class ForumAvatar extends Doctrine_Entity
{
public static function initMetadata($mapping)
{
$mapping->mapField(array(
'fieldName' => 'id',
'type' => 'integer',
'length' => 4,
'id' => true,
'idGenerator' => 'auto'
));
}
}
?>
\ No newline at end of file
......@@ -25,14 +25,13 @@ class ForumBoard extends Doctrine_Entity
'type' => 'integer'
));
$mapping->hasOne('ForumCategory as category',
array('local' => 'category_id', 'foreign' => 'id'));
/*
$metadata->mapOneToOne(array(
'fieldName' => 'category', // optional, defaults to targetEntity
/*$mapping->hasOne('ForumCategory as category',
array('local' => 'category_id', 'foreign' => 'id'));*/
$mapping->mapOneToOne(array(
'fieldName' => 'category',
'targetEntity' => 'ForumCategory',
'joinColumns' => array('category_id' => 'id')
));
*/
}
}
......@@ -20,7 +20,12 @@ class ForumCategory extends Doctrine_Entity
'length' => 255
));
$mapping->hasMany('ForumBoard as boards', array(
'local' => 'id' , 'foreign' => 'category_id'));
/*$mapping->hasMany('ForumBoard as boards', array(
'local' => 'id' , 'foreign' => 'category_id'));*/
$mapping->mapOneToMany(array(
'fieldName' => 'boards',
'targetEntity' => 'ForumBoard'
));
}
}
......@@ -6,9 +6,9 @@
class ForumUser extends Doctrine_Entity
{
#protected $dtype;
#protected $id;
#protected $username;
#protected $avatar;
public static function initMetadata($mapping)
{
......@@ -42,6 +42,12 @@ class ForumUser extends Doctrine_Entity
'length' => 50
));
$mapping->mapOneToOne(array(
'fieldName' => 'avatar',
'targetEntity' => 'ForumAvatar',
'joinColumns' => array('avatar_id' => 'id'),
));
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment