Commit d9975c36 authored by romanb's avatar romanb

Checkin of occasional work from the past weeks.

parent c43f9588
<?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::ORM::Mapping;
/**
* Base class for association mappings.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @todo Rename to AssociationMapping.
*/
class Doctrine_Association implements Serializable
{
const FETCH_MANUAL = 1;
const FETCH_LAZY = 2;
const FETCH_EAGER = 3;
/**
* Cascade types enumeration.
*
* @var array
*/
protected static $_cascadeTypes = array(
'all',
'none',
'save',
'delete',
'refresh'
);
protected $_cascades = array();
protected $_isCascadeDelete;
protected $_isCascadeSave;
protected $_isCascadeRefresh;
/**
* The fetch mode used for the association.
*
* @var integer
*/
protected $_fetchMode = self::FETCH_MANUAL;
/**
* Flag that indicates whether the class that defines this mapping is
* the owning side of the association.
*
* @var boolean
*/
protected $_isOwningSide = true;
/**
* The name of the source Entity (the Entity that defines this mapping).
*
* @var string
*/
protected $_sourceEntityName;
/**
* The name of the target Entity (the Enitity that is the target of the
* association).
*
* @var unknown_type
*/
protected $_targetEntityName;
/**
* Identifies the field on the source class (the class this AssociationMapping
* belongs to) that represents the association.
*
* @var string
*/
protected $_sourceFieldName;
/**
* Identifies the field on the owning side that has the mapping for the
* association.
*
* @var string
*/
protected $_mappedByFieldName;
/**
* Constructor.
* Creates a new AssociationMapping.
*
* @param array $mapping The mapping definition.
*/
public function __construct(array $mapping)
{
$this->_validateMapping($mapping);
if ($this->_isOwningSide) {
$this->_sourceEntityName = $mapping['sourceEntity'];
$this->_targetEntityName = $mapping['targetEntity'];
$this->_sourceFieldName = $mapping['fieldName'];
} else {
$this->_mappedByFieldName = $mapping['mappedBy'];
}
}
/**
* Validates & completes the mapping. Mapping defaults are applied here.
*
* @param array $mapping
* @return array The validated & completed mapping.
*/
protected function _validateMapping(array $mapping)
{
if (isset($mapping['mappedBy'])) {
$this->_isOwningSide = false;
}
if ($this->_isOwningSide) {
if ( ! isset($mapping['targetEntity'])) {
throw Doctrine_MappingException::missingTargetEntity();
} else if ( ! isset($mapping['fieldName'])) {
throw Doctrine_MappingException::missingFieldName();
}
}
return $mapping;
}
public function isCascadeDelete()
{
if (is_null($this->_isCascadeDelete)) {
$this->_isCascadeDelete = in_array('delete', $this->_cascades);
}
return $this->_isCascadeDelete;
}
public function isCascadeSave()
{
if (is_null($this->_isCascadeSave)) {
$this->_isCascadeSave = in_array('save', $this->_cascades);
}
return $this->_isCascadeSave;
}
public function isCascadeRefresh()
{
if (is_null($this->_isCascadeRefresh)) {
$this->_isCascadeRefresh = in_array('refresh', $this->_cascades);
}
return $this->_isCascadeRefresh;
}
public function isEagerlyFetched()
{
return $this->_fetchMode == self::FETCH_EAGER;
}
public function isLazilyFetched()
{
return $this->_fetchMode == self::FETCH_LAZY;
}
public function isManuallyFetched()
{
return $this->_fetchMode == self::FETCH_MANUAL;
}
public function isOwningSide()
{
return $this->_isOwningSide;
}
public function isInverseSide()
{
return ! $this->_isOwningSide;
}
public function getSourceEntityName()
{
return $this->_sourceEntityName;
}
public function getTargetEntityName()
{
return $this->_targetEntityName;
}
/**
* Get the name of the field the association is mapped into.
*
*/
public function getSourceFieldName()
{
return $this->_sourceFieldName;
}
public function getMappedByFieldName()
{
return $this->_mappedByFieldName;
}
/* Serializable implementation */
public function serialize()
{
return "";
}
public function unserialize($serialized)
{
return true;
}
}
?>
\ No newline at end of file
<?php
#namespace Doctrine::ORM::Mappings;
/**
* A many-to-many mapping describes the mapping between two collections of
* entities.
*
* @since 2.0
* @todo Rename to ManyToManyMapping
*/
class Doctrine_Association_ManyToMany extends Doctrine_Association
{
/**
* Whether the mapping uses an association class.
*
* @var boolean
*/
private $_usesAssociationClass;
/**
* The name of the association class (if an association class is used).
*
* @var string
*/
private $_associationClassName;
/**
* The name of the intermediate table.
*
* @var string
*/
private $_relationTableName;
/** The field in the source table that corresponds to the key in the relation table */
protected $_sourceKeyFields;
/** The field in the target table that corresponds to the key in the relation table */
protected $_targetKeyFields;
/** The field in the intermediate table that corresponds to the key in the source table */
protected $_sourceRelationKeyFields;
/** The field in the intermediate table that corresponds to the key in the target table */
protected $_targetRelationKeyFields;
/**
* Whether the mapping uses an association class for the intermediary
* table.
*
* @return boolean
*/
public function usesAssociationClass()
{
}
/**
* Gets the name of the intermediate table.
*
* @return string
*/
public function getRelationTableName()
{
return $this->_relationTableName;
}
/**
* Gets the name of the association class.
*
* @return string
*/
public function getAssociationClassName()
{
return $this->_associationClassName;
}
}
?>
\ No newline at end of file
<?php
#namespace Doctrine::ORM::Mappings;
class Doctrine_Association_OneToMany extends Doctrine_Association
{
/** The target foreign key fields that reference the sourceKeyFields. */
protected $_targetForeignKeyFields;
/** The (typically primary) source key fields that are referenced by the targetForeignKeyFields. */
protected $_sourceKeyFields;
/** This maps the target foreign key fields to the corresponding (primary) source key fields. */
protected $_targetForeignKeysToSourceKeys;
/** This maps the (primary) source key fields to the corresponding target foreign key fields. */
protected $_sourceKeysToTargetForeignKeys;
/** Whether to delete orphaned elements (removed from the collection) */
protected $_isCascadeDeleteOrphan = false;
public function isCascadeDeleteOrphan()
{
return $this->_isCascadeDeleteOrphan;
}
}
?>
\ No newline at end of file
<?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::ORM::Mappings;
#use Doctrine::ORM::Entity;
/**
* A one-to-one mapping describes a uni-directional mapping from one entity
* to another entity.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @todo Rename to OneToOneMapping
*/
class Doctrine_Association_OneToOne extends Doctrine_Association
{
/**
* Maps the source foreign/primary key fields to the target primary/foreign key fields.
* i.e. source.id (pk) => target.user_id (fk).
* Reverse mapping of _targetToSourceKeyFields.
*/
protected $_sourceToTargetKeyColumns = array();
/**
* Maps the target primary/foreign key fields to the source foreign/primary key fields.
* i.e. target.user_id (fk) => source.id (pk).
* Reverse mapping of _sourceToTargetKeyFields.
*/
protected $_targetToSourceKeyColumns = array();
/**
* Constructor.
* Creates a new OneToOneMapping.
*
* @param array $mapping The mapping info.
*/
public function __construct(array $mapping)
{
parent::__construct($mapping);
if ($this->isOwningSide()) {
$this->_sourceToTargetKeyColumns = $mapping['joinColumns'];
$this->_targetToSourceKeyColumns = array_flip($this->_sourceToTargetKeyColumns);
}
}
/**
* Validates & completes the mapping. Mapping defaults are applied here.
*
* @param array $mapping The mapping to validate & complete.
* @return array The validated & completed mapping.
* @override
*/
protected function _validateMapping(array $mapping)
{
$mapping = parent::_validateMapping($mapping);
if ($this->isOwningSide()) {
if ( ! isset($mapping['joinColumns'])) {
throw Doctrine_MappingException::missingJoinColumns();
}
}
return $mapping;
}
/**
* Gets the source-to-target key column mapping.
*
* @return unknown
*/
public function getSourceToTargetKeyColumns()
{
return $this->_sourceToTargetKeyColumns;
}
/**
* Gets the target-to-source key column mapping.
*
* @return unknown
*/
public function getTargetToSourceKeyColumns()
{
return $this->_targetToSourceKeyColumns;
}
/**
* Lazy-loads the associated entity for a given entity.
*
* @param Doctrine::ORM::Entity $entity
* @return void
*/
public function lazyLoadFor(Doctrine_Entity $entity)
{
if ($entity->getClassName() != $this->_sourceClass->getClassName()) {
//error?
}
$dql = 'SELECT t.* FROM ' . $this->_targetClass->getClassName() . ' t WHERE ';
$params = array();
foreach ($this->_sourceToTargetKeyFields as $sourceKeyField => $targetKeyField) {
if ($params) {
$dql .= " AND ";
}
$dql .= "t.$targetKeyField = ?";
$params[] = $entity->_rawGetField($sourceKeyField);
}
$otherEntity = $this->_targetClass->getEntityManager()
->query($dql, $params)
->getFirst();
if ( ! $otherEntity) {
$otherEntity = Doctrine_Null::$INSTANCE;
}
$entity->_rawSetReference($this->_sourceFieldName, $otherEntity);
}
}
?>
\ No newline at end of file
This diff is collapsed.
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
* <http://www.phpdoctrine.org>. * <http://www.phpdoctrine.org>.
*/ */
#namespace Doctrine::ORM::Internal;
/** /**
* The metadata factory is used to create ClassMetadata objects that contain all the * The metadata factory is used to create ClassMetadata objects that contain all the
* metadata of a class. * metadata of a class.
...@@ -31,10 +33,11 @@ ...@@ -31,10 +33,11 @@
* @version $Revision$ * @version $Revision$
* @link www.phpdoctrine.org * @link www.phpdoctrine.org
* @since 2.0 * @since 2.0
* @todo Rename to ClassMetadataFactory.
*/ */
class Doctrine_ClassMetadata_Factory class Doctrine_ClassMetadata_Factory
{ {
protected $_conn; protected $_em;
protected $_driver; protected $_driver;
/** /**
...@@ -52,7 +55,7 @@ class Doctrine_ClassMetadata_Factory ...@@ -52,7 +55,7 @@ class Doctrine_ClassMetadata_Factory
*/ */
public function __construct(Doctrine_EntityManager $em, $driver) public function __construct(Doctrine_EntityManager $em, $driver)
{ {
$this->_conn = $em; $this->_em = $em;
$this->_driver = $driver; $this->_driver = $driver;
} }
...@@ -101,7 +104,7 @@ class Doctrine_ClassMetadata_Factory ...@@ -101,7 +104,7 @@ class Doctrine_ClassMetadata_Factory
$class = $classes[$loadedParentClass]; $class = $classes[$loadedParentClass];
} else { } else {
$rootClassOfHierarchy = count($parentClasses) > 0 ? array_shift($parentClasses) : $name; $rootClassOfHierarchy = count($parentClasses) > 0 ? array_shift($parentClasses) : $name;
$class = new Doctrine_ClassMetadata($rootClassOfHierarchy, $this->_conn); $class = new Doctrine_ClassMetadata($rootClassOfHierarchy, $this->_em);
$this->_loadMetadata($class, $rootClassOfHierarchy); $this->_loadMetadata($class, $rootClassOfHierarchy);
$classes[$rootClassOfHierarchy] = $class; $classes[$rootClassOfHierarchy] = $class;
} }
...@@ -115,7 +118,7 @@ class Doctrine_ClassMetadata_Factory ...@@ -115,7 +118,7 @@ class Doctrine_ClassMetadata_Factory
$parent = $class; $parent = $class;
foreach ($parentClasses as $subclassName) { foreach ($parentClasses as $subclassName) {
$subClass = new Doctrine_ClassMetadata($subclassName, $this->_conn); $subClass = new Doctrine_ClassMetadata($subclassName, $this->_em);
$subClass->setInheritanceType($parent->getInheritanceType(), $parent->getInheritanceOptions()); $subClass->setInheritanceType($parent->getInheritanceType(), $parent->getInheritanceOptions());
$this->_addInheritedFields($subClass, $parent); $this->_addInheritedFields($subClass, $parent);
$this->_addInheritedRelations($subClass, $parent); $this->_addInheritedRelations($subClass, $parent);
...@@ -130,14 +133,10 @@ class Doctrine_ClassMetadata_Factory ...@@ -130,14 +133,10 @@ class Doctrine_ClassMetadata_Factory
protected function _addInheritedFields($subClass, $parentClass) protected function _addInheritedFields($subClass, $parentClass)
{ {
foreach ($parentClass->getFieldMappings() as $name => $definition) { foreach ($parentClass->getFieldMappings() as $fieldName => $mapping) {
$fullName = "$name as " . $parentClass->getFieldName($name); $fullName = "$name as " . $parentClass->getFieldName($name);
$definition['inherited'] = true; $mapping['inherited'] = true;
$subClass->mapColumn( $subClass->mapField($mapping);
$fullName,
$definition['type'],
$definition['length'],
$definition);
} }
} }
...@@ -163,6 +162,7 @@ class Doctrine_ClassMetadata_Factory ...@@ -163,6 +162,7 @@ class Doctrine_ClassMetadata_Factory
$names = array(); $names = array();
$className = $name; $className = $name;
// get parent classes // get parent classes
//TODO: Skip Entity types MappedSuperclass/Transient
do { do {
if ($className === 'Doctrine_Entity') { if ($className === 'Doctrine_Entity') {
break; break;
...@@ -182,11 +182,13 @@ class Doctrine_ClassMetadata_Factory ...@@ -182,11 +182,13 @@ class Doctrine_ClassMetadata_Factory
// load further metadata // load further metadata
$this->_driver->loadMetadataForClass($name, $class); $this->_driver->loadMetadataForClass($name, $class);
// set default table name, if necessary
$tableName = $class->getTableName(); $tableName = $class->getTableName();
if ( ! isset($tableName)) { if ( ! isset($tableName)) {
$class->setTableName(Doctrine::tableize($class->getClassName())); $class->setTableName(Doctrine::tableize($class->getClassName()));
} }
// complete identifier mapping
$this->_initIdentifier($class); $this->_initIdentifier($class);
return $class; return $class;
...@@ -199,7 +201,7 @@ class Doctrine_ClassMetadata_Factory ...@@ -199,7 +201,7 @@ class Doctrine_ClassMetadata_Factory
*/ */
protected function _initIdentifier(Doctrine_ClassMetadata $class) protected function _initIdentifier(Doctrine_ClassMetadata $class)
{ {
switch (count((array)$class->getIdentifier())) { /*switch (count($class->getIdentifier())) {
case 0: // No identifier in the class mapping yet case 0: // No identifier in the class mapping yet
// If its a subclass, inherit the identifier from the parent. // If its a subclass, inherit the identifier from the parent.
...@@ -217,7 +219,7 @@ class Doctrine_ClassMetadata_Factory ...@@ -217,7 +219,7 @@ class Doctrine_ClassMetadata_Factory
} }
// add all inherited primary keys // add all inherited primary keys
foreach ((array) $class->getIdentifier() as $id) { foreach ($class->getIdentifier() as $id) {
$definition = $rootClass->getDefinitionOf($id); $definition = $rootClass->getDefinitionOf($id);
// inherited primary keys shouldn't contain autoinc // inherited primary keys shouldn't contain autoinc
...@@ -232,15 +234,6 @@ class Doctrine_ClassMetadata_Factory ...@@ -232,15 +234,6 @@ class Doctrine_ClassMetadata_Factory
} }
} else { } else {
throw Doctrine_MappingException::identifierRequired($class->getClassName()); throw Doctrine_MappingException::identifierRequired($class->getClassName());
/* Legacy behavior of auto-adding an id field
$definition = array('type' => 'integer',
'length' => 20,
'autoincrement' => true,
'primary' => true);
$class->mapColumn('id', $definition['type'], $definition['length'], $definition, true);
$class->setIdentifier(array('id'));
$class->setIdentifierType(Doctrine::IDENTIFIER_AUTOINC);
*/
} }
break; break;
case 1: // A single identifier is in the mapping case 1: // A single identifier is in the mapping
...@@ -293,6 +286,36 @@ class Doctrine_ClassMetadata_Factory ...@@ -293,6 +286,36 @@ class Doctrine_ClassMetadata_Factory
break; break;
default: // Multiple identifiers are in the mapping so its a composite id default: // Multiple identifiers are in the mapping so its a composite id
$class->setIdentifierType(Doctrine::IDENTIFIER_COMPOSITE); $class->setIdentifierType(Doctrine::IDENTIFIER_COMPOSITE);
}*/
// If the chosen generator type is "auto", then pick the one appropriate for
// the database.
// FIXME: This is very ugly here. Such switch()es on the database driver
// are unnecessary as we can easily replace them with polymorphic calls on
// the connection (or another) object. We just need to decide where to put
// the id generation types.
if ($class->getIdGeneratorType() == Doctrine_ClassMetadata::GENERATOR_TYPE_AUTO) {
switch (strtolower($this->_em->getConnection()->getDriverName())) {
case 'mysql':
// pick IDENTITY
$class->setIdGeneratorType(Doctrine_ClassMetadata::GENERATOR_TYPE_IDENTITY);
break;
case 'oracle':
//pick SEQUENCE
break;
case 'postgres':
//pick SEQUENCE
break;
case 'firebird':
//pick what?
break;
case 'mssql':
//pick what?
default:
throw new Doctrine_Exception("Encountered unknown database driver: "
. $this->_em->getConnection()->getDriverName());
}
} }
} }
......
This diff is collapsed.
...@@ -138,7 +138,8 @@ abstract class Doctrine_Connection implements Countable ...@@ -138,7 +138,8 @@ abstract class Doctrine_Connection implements Countable
* @var array $properties * @var array $properties
*/ */
protected $properties = array( protected $properties = array(
'sql_comments' => array(array('start' => '--', 'end' => "\n", 'escape' => false), 'sql_comments' => array(
array('start' => '--', 'end' => "\n", 'escape' => false),
array('start' => '/*', 'end' => '*/', 'escape' => false)), array('start' => '/*', 'end' => '*/', 'escape' => false)),
'identifier_quoting' => array('start' => '"', 'end' => '"','escape' => '"'), 'identifier_quoting' => array('start' => '"', 'end' => '"','escape' => '"'),
'string_quoting' => array('start' => "'", 'end' => "'", 'escape' => false, 'string_quoting' => array('start' => "'", 'end' => "'", 'escape' => false,
...@@ -154,6 +155,8 @@ abstract class Doctrine_Connection implements Countable ...@@ -154,6 +155,8 @@ abstract class Doctrine_Connection implements Countable
/** /**
* The parameters used during creation of the Connection. * The parameters used during creation of the Connection.
*
* @var array
*/ */
protected $_params = array(); protected $_params = array();
...@@ -364,7 +367,10 @@ abstract class Doctrine_Connection implements Countable ...@@ -364,7 +367,10 @@ abstract class Doctrine_Connection implements Countable
//$this->getListener()->preConnect($event); //$this->getListener()->preConnect($event);
// TODO: the extension_loaded check can happen earlier, maybe in the factory // TODO: the extension_loaded check can happen earlier, maybe in the factory
if (extension_loaded('pdo')) { if ( ! extension_loaded('pdo')) {
throw new Doctrine_Connection_Exception("Couldn't locate driver named " . $e[0]);
}
$driverOptions = isset($this->_params['driverOptions']) ? $driverOptions = isset($this->_params['driverOptions']) ?
$this->_params['driverOptions'] : array(); $this->_params['driverOptions'] : array();
$user = isset($this->_params['user']) ? $user = isset($this->_params['user']) ?
...@@ -379,9 +385,6 @@ abstract class Doctrine_Connection implements Countable ...@@ -379,9 +385,6 @@ abstract class Doctrine_Connection implements Countable
); );
$this->_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->_pdo->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); $this->_pdo->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
} else {
throw new Doctrine_Connection_Exception("Couldn't locate driver named " . $e[0]);
}
// attach the pending attributes to adapter // attach the pending attributes to adapter
/*foreach($this->pendingAttributes as $attr => $value) { /*foreach($this->pendingAttributes as $attr => $value) {
...@@ -427,9 +430,8 @@ abstract class Doctrine_Connection implements Countable ...@@ -427,9 +430,8 @@ abstract class Doctrine_Connection implements Countable
*/ */
public function supports($feature) public function supports($feature)
{ {
return (isset($this->supported[$feature]) return (isset($this->supported[$feature]) &&
&& ($this->supported[$feature] === 'emulated' ($this->supported[$feature] === 'emulated' || $this->supported[$feature]));
|| $this->supported[$feature]));
} }
/** /**
...@@ -662,8 +664,7 @@ abstract class Doctrine_Connection implements Countable ...@@ -662,8 +664,7 @@ abstract class Doctrine_Connection implements Countable
} }
/** /**
* quote * Quotes given input parameter
* quotes given input parameter
* *
* @param mixed $input parameter to be quoted * @param mixed $input parameter to be quoted
* @param string $type * @param string $type
...@@ -794,11 +795,10 @@ abstract class Doctrine_Connection implements Countable ...@@ -794,11 +795,10 @@ abstract class Doctrine_Connection implements Countable
$this->getAttribute(Doctrine::ATTR_LISTENER)->postPrepare($event); $this->getAttribute(Doctrine::ATTR_LISTENER)->postPrepare($event);
return new Doctrine_Connection_Statement($this, $stmt); return new Doctrine_Connection_Statement($this, $stmt);
} catch(Doctrine_Adapter_Exception $e) { } catch (PDOException $e) {
} catch(PDOException $e) { }
$this->rethrowException($e, $this); $this->rethrowException($e, $this);
} }
}
/** /**
* queries the database with limit and offset * queries the database with limit and offset
...@@ -859,11 +859,10 @@ abstract class Doctrine_Connection implements Countable ...@@ -859,11 +859,10 @@ abstract class Doctrine_Connection implements Countable
return $stmt; return $stmt;
} }
} catch (Doctrine_Adapter_Exception $e) { } catch (PDOException $e) {
} catch (PDOException $e) { }
$this->rethrowException($e, $this); $this->rethrowException($e, $this);
} }
}
/** /**
* exec * exec
...@@ -894,11 +893,10 @@ abstract class Doctrine_Connection implements Countable ...@@ -894,11 +893,10 @@ abstract class Doctrine_Connection implements Countable
return $count; return $count;
} }
} catch (Doctrine_Adapter_Exception $e) { } catch (PDOException $e) {
} catch (PDOException $e) { }
$this->rethrowException($e, $this); $this->rethrowException($e, $this);
} }
}
/** /**
* *
...@@ -923,7 +921,7 @@ abstract class Doctrine_Connection implements Countable ...@@ -923,7 +921,7 @@ abstract class Doctrine_Connection implements Countable
} }
/** /**
* rethrowException * Wraps the given exception into a driver-specific exception and rethrows it.
* *
* @throws Doctrine_Connection_Exception * @throws Doctrine_Connection_Exception
*/ */
...@@ -940,9 +938,7 @@ abstract class Doctrine_Connection implements Countable ...@@ -940,9 +938,7 @@ abstract class Doctrine_Connection implements Countable
} }
$exc->processErrorInfo($e->errorInfo); $exc->processErrorInfo($e->errorInfo);
if ($this->getAttribute(Doctrine::ATTR_THROW_EXCEPTIONS)) {
throw $exc; throw $exc;
}
//$this->getListener()->postError($event); //$this->getListener()->postError($event);
} }
...@@ -1178,7 +1174,6 @@ abstract class Doctrine_Connection implements Countable ...@@ -1178,7 +1174,6 @@ abstract class Doctrine_Connection implements Countable
} }
} }
public function getFormatter() public function getFormatter()
{ {
if ( ! $this->modules['formatter']) { if ( ! $this->modules['formatter']) {
...@@ -1186,4 +1181,25 @@ abstract class Doctrine_Connection implements Countable ...@@ -1186,4 +1181,25 @@ abstract class Doctrine_Connection implements Countable
} }
return $this->modules['formatter']; return $this->modules['formatter'];
} }
public function getSequenceModule()
{
if ( ! $this->modules['sequence']) {
$class = "Doctrine_Sequence_" . $this->_driverName;
$this->modules['sequence'] = new $class;
}
return $this->modules['sequence'];
}
/**
* Gets the default (preferred) Id generation strategy of the database platform.
*
* @todo Sure, the id generator types are more ORM functionality but they're
* still kind of dbal related. Maybe we need another set of classes (DatabasePlatform?)
* but im not so sure...
*/
/*abstract*/ public function getDefaultIdGeneratorType()
{
}
} }
This diff is collapsed.
This diff is collapsed.
...@@ -42,6 +42,10 @@ ...@@ -42,6 +42,10 @@
*/ */
class Doctrine_EntityManager class Doctrine_EntityManager
{ {
const FLUSHMODE_AUTO = 'auto';
const FLUSHMODE_COMMIT = 'commit';
const FLUSHMODE_MANUAL = 'manual';
/** /**
* The unique name of the EntityManager. The name is used to bind entity classes * The unique name of the EntityManager. The name is used to bind entity classes
* to certain EntityManagers. * to certain EntityManagers.
...@@ -70,11 +74,11 @@ class Doctrine_EntityManager ...@@ -70,11 +74,11 @@ class Doctrine_EntityManager
private static $_flushModes = array( private static $_flushModes = array(
// auto: Flush occurs automatically after each operation that issues database // auto: Flush occurs automatically after each operation that issues database
// queries. No operations are queued. // queries. No operations are queued.
'auto', self::FLUSHMODE_AUTO,
// commit: Flush occurs automatically at transaction commit. // commit: Flush occurs automatically at transaction commit.
'commit', self::FLUSHMODE_COMMIT,
// manual: Flush occurs never automatically. // manual: Flush occurs never automatically.
'manual' self::FLUSHMODE_MANUAL
); );
/** /**
...@@ -242,7 +246,7 @@ class Doctrine_EntityManager ...@@ -242,7 +246,7 @@ class Doctrine_EntityManager
*/ */
public function detach(Doctrine_Entity $entity) public function detach(Doctrine_Entity $entity)
{ {
return $this->_unitOfWork->unregisterIdentity($entity); return $this->_unitOfWork->removeFromIdentityMap($entity);
} }
/** /**
...@@ -287,7 +291,7 @@ class Doctrine_EntityManager ...@@ -287,7 +291,7 @@ class Doctrine_EntityManager
*/ */
public function flush() public function flush()
{ {
$this->_unitOfWork->flush(); $this->_unitOfWork->commit();
} }
/** /**
...@@ -386,6 +390,9 @@ class Doctrine_EntityManager ...@@ -386,6 +390,9 @@ class Doctrine_EntityManager
public function save(Doctrine_Entity $entity) public function save(Doctrine_Entity $entity)
{ {
$this->_unitOfWork->save($entity); $this->_unitOfWork->save($entity);
if ($this->_flushMode == self::FLUSHMODE_AUTO) {
$this->flush();
}
} }
/** /**
...@@ -453,7 +460,7 @@ class Doctrine_EntityManager ...@@ -453,7 +460,7 @@ class Doctrine_EntityManager
return $entity; return $entity;
} else { } else {
$entity = new $className; $entity = new $className;
$this->_unitOfWork->registerIdentity($entity); $this->_unitOfWork->addToIdentityMap($entity);
} }
} }
} else { } else {
...@@ -469,6 +476,17 @@ class Doctrine_EntityManager ...@@ -469,6 +476,17 @@ class Doctrine_EntityManager
return $entity; return $entity;
} }
/**
* Checks if the instance is managed by the EntityManager.
*
* @return boolean
*/
public function contains(Doctrine_Entity $entity)
{
return $this->_unitOfWork->isInIdentityMap($entity) &&
! $this->_unitOfWork->isRegisteredRemoved($entity);
}
/** /**
* INTERNAL: * INTERNAL:
* For internal hydration purposes only. * For internal hydration purposes only.
...@@ -546,7 +564,7 @@ class Doctrine_EntityManager ...@@ -546,7 +564,7 @@ class Doctrine_EntityManager
} }
/** /**
* Gets the COnfiguration used by the EntityManager. * Gets the Configuration used by the EntityManager.
* *
* @return Configuration * @return Configuration
*/ */
...@@ -555,6 +573,16 @@ class Doctrine_EntityManager ...@@ -555,6 +573,16 @@ class Doctrine_EntityManager
return $this->_config; return $this->_config;
} }
/**
* Gets the UnitOfWork used by the EntityManager to coordinate operations.
*
* @return Doctrine::ORM::UnitOfWork
*/
public function getUnitOfWork()
{
return $this->_unitOfWork;
}
} }
?> ?>
\ No newline at end of file
<?php <?php
/* /*
* $Id$ * $Id: EventListener.php 4653 2008-07-10 17:17:58Z romanb $
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
...@@ -20,18 +20,13 @@ ...@@ -20,18 +20,13 @@
*/ */
/** /**
* Doctrine_EventListener all event listeners extend this base class * EventSubscriber.
* the empty methods allow child classes to only implement the methods they need to implement
*
* *
* @author Konsta Vesterinen <kvesteri@cc.hut.fi> * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @package Doctrine
* @subpackage EventListener
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.org * @link www.phpdoctrine.org
* @since 2.0 * @since 2.0
* @version $Revision$ * @version $Revision: 4653 $
* @todo Remove. The 2.0 event system has no listener interfaces.
*/ */
interface Doctrine_EventSubscriber interface Doctrine_EventSubscriber
{ {
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
* @version $Revision$ * @version $Revision$
* @author Konsta Vesterinen <kvesteri@cc.hut.fi> * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @todo Rename to DoctrineException
*/ */
class Doctrine_Exception extends Exception class Doctrine_Exception extends Exception
{ {
......
...@@ -72,7 +72,7 @@ class Doctrine_Hydrator_RecordDriver ...@@ -72,7 +72,7 @@ class Doctrine_Hydrator_RecordDriver
public function initRelatedCollection(Doctrine_Entity $entity, $name) public function initRelatedCollection(Doctrine_Entity $entity, $name)
{ {
if ( ! isset($this->_initializedRelations[$entity->getOid()][$name])) { if ( ! isset($this->_initializedRelations[$entity->getOid()][$name])) {
$relation = $entity->getClassMetadata()->getRelation($name); $relation = $entity->getClass()->getRelation($name);
$relatedClass = $relation->getTable(); $relatedClass = $relation->getTable();
$coll = $this->getElementCollection($relatedClass->getClassName()); $coll = $this->getElementCollection($relatedClass->getClassName());
$coll->setReference($entity, $relation); $coll->setReference($entity, $relation);
......
...@@ -519,16 +519,12 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract ...@@ -519,16 +519,12 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
* $table->prepareValue($field, $value); // Doctrine_Null * $table->prepareValue($field, $value); // Doctrine_Null
* </code> * </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 $field the name of the field
* @param string $value field value * @param string $value field value
* @param string $typeHint A hint on the type of the value. If provided, the type lookup * @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 * for the field can be skipped. Used i.e. during hydration to
* improve performance on large and/or complex results. * improve performance on large and/or complex results.
* @return mixed prepared value * @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(Doctrine_ClassMetadata $class, $fieldName, $value, $typeHint = null) public function prepareValue(Doctrine_ClassMetadata $class, $fieldName, $value, $typeHint = null)
{ {
...@@ -540,15 +536,17 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract ...@@ -540,15 +536,17 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
$type = is_null($typeHint) ? $class->getTypeOf($fieldName) : $typeHint; $type = is_null($typeHint) ? $class->getTypeOf($fieldName) : $typeHint;
switch ($type) { switch ($type) {
case 'integer': case 'integer':
case 'string'; case 'string':
// don't do any casting here PHP INT_MAX is smaller than what the databases support
break;
case 'enum': case 'enum':
return $class->enumValue($fieldName, $value);
break;
case 'boolean': case 'boolean':
return (boolean) $value; // don't do any conversions on primitive types
break; break;
//case 'enum':
// return $class->enumValue($fieldName, $value);
//break;
//case 'boolean':
// return (boolean) $value;
//break;
case 'array': case 'array':
case 'object': case 'object':
if (is_string($value)) { if (is_string($value)) {
......
<?php
#namespace Doctrine::ORM::Internal;
/**
* The CommitOrderCalculator is used by the UnitOfWork to sort out the
* correct order in which changes to entities need to be persisted.
*
* @since 2.0
* @todo Rename to: CommitOrderCalculator
* @author Roman Borschel <roman@code-factory.org>
*/
class Doctrine_Internal_CommitOrderCalculator
{
private $_currentTime;
/**
* The node list used for sorting.
*
* @var array
*/
private $_nodes = array();
/**
* The topologically sorted list of items. Note that these are not nodes
* but the wrapped items.
*
* @var array
*/
private $_sorted;
/**
* Orders the given list of CommitOrderNodes based on their dependencies.
*
* Uses a depth-first search (DFS) to traverse the graph.
* The desired topological sorting is the reverse postorder of these searches.
*
* @param array $nodes The list of (unordered) CommitOrderNodes.
* @return array The list of ordered items. These are the items wrapped in the nodes.
*/
public function getCommitOrder()
{
// Check whether we need to do anything. 0 or 1 node is easy.
$nodeCount = count($this->_nodes);
if ($nodeCount == 0) {
return array();
} else if ($nodeCount == 1) {
$node = array_pop($this->_nodes);
return array($node->getClass());
}
$this->_sorted = array();
// Init
foreach ($this->_nodes as $node) {
$node->markNotVisited();
$node->setPredecessor(null);
}
$this->_currentTime = 0;
// Go
foreach ($this->_nodes as $node) {
if ($node->isNotVisited()) {
$node->visit();
}
}
return $this->_sorted;
}
public function addNode($key, $node)
{
$this->_nodes[$key] = $node;
}
public function addNodeWithItem($key, $item)
{
$this->_nodes[$key] = new Doctrine_Internal_CommitOrderNode($item, $this);
}
public function getNodeForKey($key)
{
return $this->_nodes[$key];
}
public function hasNodeWithKey($key)
{
return isset($this->_nodes[$key]);
}
public function clear()
{
$this->_nodes = array();
$this->_sorted = array();
}
public function getNextTime()
{
return ++$this->_currentTime;
}
public function prependNode($node)
{
array_unshift($this->_sorted, $node->getClass());
}
}
?>
\ No newline at end of file
<?php
#namespace Doctrine::ORM::Internal;
#use Doctrine::ORM::Mapping::ClassMetadata;
/**
* A CommitOrderNode is a temporary wrapper around ClassMetadata objects
* that is used to sort the order of commits.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
*/
class Doctrine_Internal_CommitOrderNode
{
const NOT_VISITED = 1;
const IN_PROGRESS = 2;
const VISITED = 3;
private $_traversalState;
private $_predecessor;
private $_status;
private $_calculator;
private $_relatedNodes = array();
private $_discoveryTime;
private $_finishingTime;
private $_wrappedObj;
private $_relationEdges = array();
public function __construct($wrappedObj, Doctrine_Internal_CommitOrderCalculator $calc)
{
$this->_wrappedObj = $wrappedObj;
$this->_calculator = $calc;
}
public function getClass()
{
return $this->_wrappedObj;
}
public function setPredecessor($node)
{
$this->_predecessor = $node;
}
public function getPredecessor()
{
return $this->_predecessor;
}
public function markNotVisited()
{
$this->_traversalState = self::NOT_VISITED;
}
public function markInProgress()
{
$this->_traversalState = self::IN_PROGRESS;
}
public function markVisited()
{
$this->_traversalState = self::VISITED;
}
public function isNotVisited()
{
return $this->_traversalState == self::NOT_VISITED;
}
public function isInProgress()
{
return $this->_traversalState == self::IN_PROGRESS;
}
public function visit()
{
$this->markInProgress();
$this->setDiscoveryTime($this->_calculator->getNextTime());
foreach ($this->getRelatedNodes() as $node) {
if ($node->isNotVisited()) {
$node->setPredecessor($this);
$node->visit();
}
if ($node->isInProgress()) {
// back edge => cycle
//TODO: anything to do here?
}
}
$this->markVisited();
$this->_calculator->prependNode($this);
$this->setFinishingTime($this->_calculator->getNextTime());
}
public function setDiscoveryTime($time)
{
$this->_discoveryTime = $time;
}
public function setFinishingTime($time)
{
$this->_finishingTime = $time;
}
public function getDiscoveryTime()
{
return $this->_discoveryTime;
}
public function getFinishingTime()
{
return $this->_finishingTime;
}
public function getRelatedNodes()
{
return $this->_relatedNodes;
}
/**
* Adds a directed dependency (an edge). "$this -before-> $other".
*
* @param Doctrine_Internal_CommitOrderNode $node
*/
public function before(Doctrine_Internal_CommitOrderNode $node)
{
$this->_relatedNodes[] = $node;
}
}
?>
\ No newline at end of file
...@@ -12,6 +12,22 @@ class Doctrine_MappingException extends Doctrine_Exception ...@@ -12,6 +12,22 @@ class Doctrine_MappingException extends Doctrine_Exception
return new self("No identifier specified for Entity '$entityName'." return new self("No identifier specified for Entity '$entityName'."
. " Every Entity must have an identifier."); . " Every Entity must have an identifier.");
} }
public static function invalidInheritanceType($type)
{
return new self("The inheritance type '$type' does not exist.");
}
public static function invalidInheritanceOption($name)
{
return new self("The inheritance option '$name' does not exist.");
}
public static function generatorNotAllowedWithCompositeId()
{
return new self("Id generators can't be used with a composite id.");
}
} }
?> ?>
\ No newline at end of file
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
* @since 1.0 * @since 1.0
* @version $Revision$ * @version $Revision$
* @author Konsta Vesterinen <kvesteri@cc.hut.fi> * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @todo Really needed? * @todo Really needed? Remove.
*/ */
interface Doctrine_Overloadable { interface Doctrine_Overloadable {
/** /**
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
* @since 1.0 * @since 1.0
* @version $Revision$ * @version $Revision$
* @author Joe Simms <joe.simms@websites4.com> * @author Joe Simms <joe.simms@websites4.com>
* @todo Move to NestedSet behavior.
*/ */
class Doctrine_Tree class Doctrine_Tree
{ {
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
* @version $Revision$ * @version $Revision$
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @author Konsta Vesterinen <kvesteri@cc.hut.fi> * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @todo Move to validator package.
*/ */
class Doctrine_Validator class Doctrine_Validator
{ {
......
<?php
require_once 'lib/DoctrineTestInit.php';
class Orm_Associations_CascadeTest extends Doctrine_OrmTestCase
{
protected function setUp() {
;
}
protected function tearDown() {
;
}
public function testDeleteCascade()
{
$container = array();
$cascade = new DeleteCascade();
$cascade->cascade($entity, $container);
}
}
abstract class Cascade
{
public function cascade(Doctrine_Entity $record, array &$container)
{
if ($this->shouldCascadeTo($record)) {
$container[$record->getOid()] = $record;
}
foreach ($record->getTable()->getRelations() as $relation) {
if ($this->doCascade($relation)) {
$this->prepareCascade($record, $relation);
$relatedObjects = $record->get($relation->getAlias());
if ($relatedObjects instanceof Doctrine_Record && $this->shouldCascadeTo($relatedObjects)
&& ! isset($container[$relatedObjects->getOid()])) {
$this->cascade($relatedObjects, $container);
} else if ($relatedObjects instanceof Doctrine_Collection && count($relatedObjects) > 0) {
foreach ($relatedObjects as $object) {
if ( ! isset($container[$object->getOid()])) {
$this->cascade($object, $container);
}
}
}
}
}
}
}
class DeleteCascade extends Cascade
{
public function doCascade($relation)
{
return $relation->isCascadeDelete();
}
public function prepareCascade($record, $relation)
{
$fieldName = $relation->getAlias();
// if it's a xToOne relation and the related object is already loaded
// we don't need to refresh, else we need to.
if ( ! ($relation->getType() == Doctrine_Relation::ONE && isset($record->$fieldName))) {
$record->refreshRelated($relation->getAlias());
}
}
public function shouldCascadeTo(Doctrine_Entity $entity)
{
//TODO: also ignore removed Entities. incorporate that in exists() with a new
// state? (DELETED?)
return ! $entity->exists();
}
}
\ No newline at end of file
<?php
require_once 'lib/DoctrineTestInit.php';
class Orm_Associations_OneToOneMappingTest extends Doctrine_OrmTestCase
{
public function testCorrectOneToOneBidirectionalMapping()
{
$owningSideMapping = array(
'fieldName' => 'address',
'targetEntity' => 'Address',
'joinColumns' => array('address_id' => 'id'),
'sourceEntity' => 'Person' // This is normally filled by ClassMetadata
);
$oneToOneMapping = new Doctrine_Association_OneToOne($owningSideMapping);
$this->assertEquals(array('address_id' => 'id'), $oneToOneMapping->getSourceToTargetKeyColumns());
$this->assertEquals(array('id' => 'address_id'), $oneToOneMapping->getTargetToSourceKeyColumns());
$this->assertEquals('Address', $oneToOneMapping->getTargetEntityName());
$this->assertEquals('Person', $oneToOneMapping->getSourceEntityName());
$this->assertEquals('address', $oneToOneMapping->getSourceFieldName());
$this->assertTrue($oneToOneMapping->isOwningSide());
$inverseSideMapping = array(
'mappedBy' => 'address'
);
$oneToOneMapping = new Doctrine_Association_OneToOne($inverseSideMapping);
$this->assertEquals('address', $oneToOneMapping->getMappedByFieldName());
$this->assertTrue($oneToOneMapping->isInverseSide());
}
}
?>
\ No newline at end of file
...@@ -106,17 +106,17 @@ class Orm_Component_CollectionTest extends Doctrine_OrmTestCase ...@@ -106,17 +106,17 @@ class Orm_Component_CollectionTest extends Doctrine_OrmTestCase
/** /**
* @test * @test
*/ */
public function shouldSetKeyColumnWhenAddingNewRowAsArray() /*public function shouldSetKeyColumnWhenAddingNewRowAsArray()
{ {
$this->assertTrue(isset($this->cmsColl['test'])); $this->assertTrue(isset($this->cmsColl['test']));
$this->assertEquals($this->cmsUser, $this->cmsColl['test']); $this->assertEquals($this->cmsUser, $this->cmsColl['test']);
} }*/
/** /**
* @test * @test
*/ */
public function shouldSerializeAndUnserializeCollectionWithData() /*public function shouldSerializeAndUnserializeCollectionWithData()
{ {
$serialized = serialize($this->cmsColl); $serialized = serialize($this->cmsColl);
$coll = unserialize($serialized); $coll = unserialize($serialized);
...@@ -126,6 +126,6 @@ class Orm_Component_CollectionTest extends Doctrine_OrmTestCase ...@@ -126,6 +126,6 @@ class Orm_Component_CollectionTest extends Doctrine_OrmTestCase
$user = $coll['test']; $user = $coll['test'];
$this->assertTrue($user instanceOf CmsUser); $this->assertTrue($user instanceOf CmsUser);
$this->assertEquals('test', $user['username']); $this->assertEquals('test', $user['username']);
} }*/
} }
...@@ -22,12 +22,21 @@ class Orm_Entity_AccessorTest extends Doctrine_OrmTestCase ...@@ -22,12 +22,21 @@ class Orm_Entity_AccessorTest extends Doctrine_OrmTestCase
class CustomAccessorMutatorTestEntity extends Doctrine_Entity class CustomAccessorMutatorTestEntity extends Doctrine_Entity
{ {
public static function initMetadata($class) public static function initMetadata($mapping)
{ {
$class->mapColumn('id', 'integer', 4, array('primary')); $mapping->mapField(array(
$class->mapColumn('username', 'string', 50, array( 'fieldName' => 'id',
'type' => 'integer',
'length' => 4,
'id' => true
));
$mapping->mapField(array(
'fieldName' => 'username',
'type' => 'string',
'length' => 50,
'accessor' => 'getUsernameCustom', 'accessor' => 'getUsernameCustom',
'mutator' => 'setUsernameCustom')); 'mutator' => 'setUsernameCustom'
));
} }
public function getUsernameCustom() public function getUsernameCustom()
...@@ -43,10 +52,19 @@ class CustomAccessorMutatorTestEntity extends Doctrine_Entity ...@@ -43,10 +52,19 @@ class CustomAccessorMutatorTestEntity extends Doctrine_Entity
class MagicAccessorMutatorTestEntity extends Doctrine_Entity class MagicAccessorMutatorTestEntity extends Doctrine_Entity
{ {
public static function initMetadata($class) public static function initMetadata($mapping)
{ {
$class->mapColumn('id', 'integer', 4, array('primary')); $mapping->mapField(array(
$class->mapColumn('username', 'string', 50, array()); 'fieldName' => 'id',
'type' => 'integer',
'length' => 4,
'id' => true
));
$mapping->mapField(array(
'fieldName' => 'username',
'type' => 'string',
'length' => 50
));
} }
public function getUsername() public function getUsername()
......
...@@ -22,10 +22,19 @@ class ConstructorTestEntity1 extends Doctrine_Entity ...@@ -22,10 +22,19 @@ class ConstructorTestEntity1 extends Doctrine_Entity
} }
/* The mapping definition */ /* The mapping definition */
public static function initMetadata($class) public static function initMetadata($mapping)
{ {
$class->mapColumn('id', 'integer', 4, array('primary')); $mapping->mapField(array(
$class->mapColumn('username', 'string', 50, array()); 'fieldName' => 'id',
'type' => 'integer',
'length' => 4,
'id' => true
));
$mapping->mapField(array(
'fieldName' => 'username',
'type' => 'string',
'length' => 50
));
} }
} }
......
<?php <?php
require_once 'lib/DoctrineTestInit.php'; require_once 'lib/DoctrineTestInit.php';
require_once 'lib/mocks/Doctrine_EntityManagerMock.php';
require_once 'lib/mocks/Doctrine_ConnectionMock.php';
/**
* UnitOfWork tests.
* These tests run without a database through mocking the
* persister/connection/sequence used by the UnitOfWork.
*/
class Orm_UnitOfWorkTest extends Doctrine_OrmTestCase class Orm_UnitOfWorkTest extends Doctrine_OrmTestCase
{ {
private $_unitOfWork; private $_unitOfWork;
private $_user; private $_user;
// Mocks
// Provides a sequence mock to the UnitOfWork
private $_connectionMock;
// The sequence mock
private $_sequenceMock;
// The persister mock used by the UnitOfWork
private $_persisterMock;
// The EntityManager mock that provides the mock persister
private $_emMock;
protected function setUp() { protected function setUp() {
parent::setUp(); parent::setUp();
$this->_user = new ForumUser(); $this->_user = new ForumUser();
$this->_unitOfWork = new Doctrine_Connection_UnitOfWork($this->_em); $this->_user->id = 1;
$this->_user->username = 'romanb';
$this->_connectionMock = new Doctrine_ConnectionMock(array());
$this->_sequenceMock = $this->_connectionMock->getSequenceModule();
$this->_emMock = new Doctrine_EntityManagerMock($this->_connectionMock);
$this->_persisterMock = $this->_emMock->getEntityPersister("ForumUser");
$this->_unitOfWork = $this->_emMock->getUnitOfWork();
} }
protected function tearDown() { protected function tearDown() {
$this->_user->free(); $this->_user->free();
} }
/* Basic registration tests */
public function testRegisterNew() public function testRegisterNew()
{ {
$this->_user->username = 'romanb';
$this->_user->id = 1;
// registerNew() is normally called in save()/persist() // registerNew() is normally called in save()/persist()
$this->_unitOfWork->registerNew($this->_user); $this->_unitOfWork->registerNew($this->_user);
$this->assertTrue($this->_unitOfWork->isRegisteredNew($this->_user)); $this->assertTrue($this->_unitOfWork->isRegisteredNew($this->_user));
$this->assertTrue($this->_unitOfWork->contains($this->_user)); $this->assertTrue($this->_unitOfWork->isInIdentityMap($this->_user));
$this->assertFalse($this->_unitOfWork->isRegisteredDirty($this->_user)); $this->assertFalse($this->_unitOfWork->isRegisteredDirty($this->_user));
$this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user)); $this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user));
} }
/*public function testRegisterNewPerf() {
$s = microtime(true);
for ($i=1; $i<40000; $i++) {
$user = new ForumUser();
$user->id = $i;
$this->_unitOfWork->registerNew($user);
}
$e = microtime(true);
echo $e - $s . " seconds" . PHP_EOL;
}*/
public function testRegisterDirty() public function testRegisterDirty()
{ {
$this->_user->username = 'romanb'; $this->assertEquals(Doctrine_Entity::STATE_NEW, $this->_user->_state());
$this->_user->id = 1; $this->assertFalse($this->_unitOfWork->isInIdentityMap($this->_user));
$this->assertEquals(Doctrine_Entity::STATE_TDIRTY, $this->_user->_state());
$this->assertFalse($this->_unitOfWork->contains($this->_user));
$this->_unitOfWork->registerDirty($this->_user); $this->_unitOfWork->registerDirty($this->_user);
$this->assertTrue($this->_unitOfWork->isRegisteredDirty($this->_user)); $this->assertTrue($this->_unitOfWork->isRegisteredDirty($this->_user));
$this->assertFalse($this->_unitOfWork->isRegisteredNew($this->_user)); $this->assertFalse($this->_unitOfWork->isRegisteredNew($this->_user));
$this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user)); $this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user));
} }
public function testRegisterRemovedOnTransientEntityIsIgnored() public function testRegisterRemovedOnNewEntityIsIgnored()
{ {
$this->_user->username = 'romanb';
$this->_user->id = 1;
$this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user)); $this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user));
$this->_unitOfWork->registerRemoved($this->_user); $this->_unitOfWork->registerDeleted($this->_user);
$this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user)); $this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user));
} }
/*public function testSavedEntityHasIdentityAndIsManaged()
/* Operational tests */
public function testSavingSingleEntityWithIdentityColumnForcesInsert()
{ {
$this->_user->username = 'romanb'; $this->assertEquals(Doctrine_Entity::STATE_NEW, $this->_user->_state());
$this->_user->save();
$this->assertTrue($this->_unitOfWork->hasIdentity($this->_user)); $this->_unitOfWork->save($this->_user);
$this->assertTrue($this->_unitOfWork->isManaged($this->_user));
}*/ $this->assertEquals(1, count($this->_persisterMock->getInserts())); // insert forced
$this->assertEquals(0, count($this->_persisterMock->getUpdates()));
$this->assertEquals(0, count($this->_persisterMock->getDeletes()));
$this->assertTrue($this->_unitOfWork->isInIdentityMap($this->_user));
$this->assertEquals(Doctrine_Entity::STATE_MANAGED, $this->_user->_state());
// should no longer be scheduled for insert
$this->assertFalse($this->_unitOfWork->isRegisteredNew($this->_user));
// should have an id
$this->assertTrue(is_numeric($this->_user->id));
// Now lets check whether a subsequence commit() does anything
$this->_persisterMock->reset();
$this->_unitOfWork->commit(); // shouldnt do anything
// verify that nothing happened
$this->assertEquals(0, count($this->_persisterMock->getInserts()));
$this->assertEquals(0, count($this->_persisterMock->getUpdates()));
$this->assertEquals(0, count($this->_persisterMock->getDeletes()));
}
public function testSavingSingleEntityWithSequenceIdGeneratorSchedulesInsert()
{
//...
}
public function testSavingSingleEntityWithTableIdGeneratorSchedulesInsert()
{
//...
}
public function testSavingSingleEntityWithSingleNaturalIdForcesInsert()
{
//...
}
public function testSavingSingleEntityWithCompositeIdForcesInsert()
{
//...
}
public function testSavingEntityGraphWithIdentityColumnsForcesInserts()
{
//...
}
public function testSavingEntityGraphWithSequencesDelaysInserts()
{
//...
}
public function testSavingEntityGraphWithNaturalIdsForcesInserts()
{
//...
}
public function testSavingEntityGraphWithMixedIdGenerationStrategies()
{
//...
}
} }
\ No newline at end of file
<?php
require_once 'lib/mocks/Doctrine_SequenceMock.php';
class Doctrine_ConnectionMock extends Doctrine_Connection
{
protected $_driverName = 'Mysql';
private $_sequenceModuleMock;
public function getSequenceModule()
{
if ( ! $this->_sequenceModuleMock) {
$this->_sequenceModuleMock = new Doctrine_SequenceMock($this);
}
return $this->_sequenceModuleMock;
}
}
?>
\ No newline at end of file
<?php
require_once 'lib/mocks/Doctrine_EntityPersisterMock.php';
class Doctrine_EntityManagerMock extends Doctrine_EntityManager
{
private $_persisterMock;
/**
* Enter description here...
*
* @param unknown_type $entityName
* @override
*/
public function getEntityPersister($entityName)
{
if ( ! $this->_persisterMock) {
$this->_persisterMock = new Doctrine_EntityPersisterMock($this, $this->getClassMetadata($entityName));
}
return $this->_persisterMock;
}
}
?>
\ No newline at end of file
<?php
class Doctrine_EntityPersisterMock extends Doctrine_EntityPersister_Standard
{
private $_inserts = array();
private $_updates = array();
private $_deletes = array();
private $_identityColumnValueCounter = 0;
public function insert($entity)
{
if ($entity->getClass()->isIdGeneratorIdentity()) {
$entity->_assignIdentifier($this->_identityColumnValueCounter++);
$this->_em->getUnitOfWork()->addToIdentityMap($entity);
}
$this->_inserts[] = $entity;
}
public function update($entity)
{
$this->_updates[] = $entity;
}
public function delete($entity)
{
$this->_deletes[] = $entity;
}
public function getInserts()
{
return $this->_inserts;
}
public function getUpdates()
{
return $this->_updates;
}
public function getDeletes()
{
return $this->_deletes;
}
public function reset()
{
$this->_identityColumnValueCounter = 0;
$this->_inserts = array();
$this->_updates = array();
$this->_deletes = array();
}
}
?>
\ No newline at end of file
<?php
class Doctrine_SequenceMock extends Doctrine_Sequence
{
private $_sequenceNumber = 0;
/**
* @override
*/
public function nextId($seqName, $ondemand = true)
{
return $this->_sequenceNumber++;
}
/**
* @override
*/
public function lastInsertId($table = null, $field = null)
{
return $this->_sequenceNumber - 1;
}
/**
* @override
*/
public function currId($seqName)
{
return $this->_sequenceNumber;
}
public function reset()
{
$this->_sequenceNumber = 0;
}
}
?>
\ No newline at end of file
<?php <?php
#namespace Doctrine::Tests::ORM::Models::CMS;
#use Doctrine::ORM::Entity;
class CmsArticle extends Doctrine_Entity class CmsArticle extends Doctrine_Entity
{ {
public static function initMetadata($class) #protected $id;
#protected $topic;
#protected $text;
#protected $user_id;
public static function initMetadata($mapping)
{ {
$class->mapColumn('id', 'integer', 4, array('primary' => true, 'autoincrement' => true)); $mapping->mapField(array(
$class->mapColumn('topic', 'string', 255); 'fieldName' => 'id',
$class->mapColumn('text', 'string'); 'type' => 'integer',
$class->mapColumn('user_id', 'integer', 4); 'length' => 4,
$class->hasMany('CmsComment as comments', array( 'id' => true,
'generatorType' => 'auto'
));
$mapping->mapField(array(
'fieldName' => 'topic',
'type' => 'string',
'length' => 255
));
$mapping->mapField(array(
'fieldName' => 'text',
'type' => 'string'
));
$mapping->mapField(array(
'fieldName' => 'user_id',
'type' => 'integer',
'length' => 4
));
$mapping->hasMany('CmsComment as comments', array(
'local' => 'id', 'foreign' => 'article_id')); 'local' => 'id', 'foreign' => 'article_id'));
} }
} }
<?php <?php
#namespace Doctrine::Tests::ORM::Models::CMS;
#use Doctrine::ORM::Entity;
class CmsComment extends Doctrine_Entity class CmsComment extends Doctrine_Entity
{ {
public static function initMetadata($class) #protected $id;
#protected $topic;
#protected $text;
#protected $article_id;
public static function initMetadata($mapping)
{ {
$class->mapColumn('id', 'integer', 4, array('primary' => true, 'autoincrement' => true)); $mapping->mapField(array(
$class->mapColumn('topic', 'string', 255); 'fieldName' => 'id',
$class->mapColumn('text', 'string'); 'type' => 'integer',
$class->mapColumn('article_id', 'integer', 4); 'length' => 4,
'id' => true,
'generatorType' => 'auto'
));
$mapping->mapField(array(
'fieldName' => 'topic',
'type' => 'string',
'length' => 255
));
$mapping->mapField(array(
'fieldName' => 'text',
'type' => 'string'
));
$mapping->mapField(array(
'fieldName' => 'article_id',
'type' => 'integer',
'length' => 4
));
} }
} }
<?php <?php
class CmsPhonenumber extends Doctrine_Entity class CmsPhonenumber extends Doctrine_Entity
{ {
public static function initMetadata($class) #protected $user_id;
#protected $phonenumber;
public static function initMetadata($mapping)
{ {
$class->mapColumn('user_id', 'integer', 4); $mapping->mapField(array(
$class->mapColumn('phonenumber', 'string', 50, array('primary' => true)); 'fieldName' => 'user_id',
'type' => 'integer',
'length' => 4
));
$mapping->mapField(array(
'fieldName' => 'phonenumber',
'type' => 'string',
'length' => 50,
'id' => true
));
} }
} }
<?php <?php
#namespace Doctrine::Test::ORM::Models;
#use Doctrine::ORM::Entity;
class CmsUser extends Doctrine_Entity class CmsUser extends Doctrine_Entity
{ {
public static function initMetadata($class) #protected $id;
#protected $status;
#protected $username;
#protected $name;
public static function initMetadata($mapping)
{ {
$class->mapColumn('id', 'integer', 4, array('primary' => true, 'autoincrement' => true)); $mapping->mapField(array(
$class->mapColumn('status', 'string', 50); 'fieldName' => 'id',
$class->mapColumn('username', 'string', 255); 'type' => 'integer',
$class->mapColumn('name', 'string', 255); 'length' => 4,
'id' => true,
'generatorType' => 'auto'
));
$mapping->mapField(array(
'fieldName' => 'status',
'type' => 'string',
'length' => 50
));
$mapping->mapField(array(
'fieldName' => 'username',
'type' => 'string',
'length' => 255
));
$mapping->mapField(array(
'fieldName' => 'name',
'type' => 'string',
'length' => 255
));
$class->hasMany('CmsPhonenumber as phonenumbers', array( $mapping->hasMany('CmsPhonenumber as phonenumbers', array(
'local' => 'id', 'foreign' => 'user_id')); 'local' => 'id', 'foreign' => 'user_id'));
$class->hasMany('CmsArticle as articles', array( $mapping->hasMany('CmsArticle as articles', array(
'local' => 'id', 'foreign' => 'user_id')); 'local' => 'id', 'foreign' => 'user_id'));
} }
} }
...@@ -2,9 +2,14 @@ ...@@ -2,9 +2,14 @@
class ForumAdministrator extends ForumUser class ForumAdministrator extends ForumUser
{ {
public static function initMetadata($class) public static function initMetadata($mapping)
{ {
$class->mapColumn('access_level as accessLevel', 'integer', 1); $mapping->mapField(array(
'fieldName' => 'accessLevel',
'columnName' => 'access_level',
'type' => 'integer',
'length' => 1
));
} }
public function banUser(ForumUser $user) {} public function banUser(ForumUser $user) {}
......
<?php <?php
class ForumBoard extends Doctrine_Entity { class ForumBoard extends Doctrine_Entity
public static function initMetadata($metadata) { {
public static function initMetadata($mapping)
{
/*$metadata->mapField(array( /*$metadata->mapField(array(
'fieldName' => 'id', 'fieldName' => 'id',
'id' => true, 'id' => true,
...@@ -8,10 +10,22 @@ class ForumBoard extends Doctrine_Entity { ...@@ -8,10 +10,22 @@ class ForumBoard extends Doctrine_Entity {
'length' => 4 'length' => 4
)); ));
*/ */
$metadata->mapColumn('id', 'integer', 4, array('primary')); $mapping->mapField(array(
$metadata->mapColumn('position', 'integer'); 'fieldName' => 'id',
$metadata->mapColumn('category_id', 'integer'); 'type' => 'integer',
$metadata->hasOne('ForumCategory as category', 'length' => 4,
'id' => true
));
$mapping->mapField(array(
'fieldName' => 'position',
'type' => 'integer'
));
$mapping->mapField(array(
'fieldName' => 'category_id',
'type' => 'integer'
));
$mapping->hasOne('ForumCategory as category',
array('local' => 'category_id', 'foreign' => 'id')); array('local' => 'category_id', 'foreign' => 'id'));
/* /*
$metadata->mapOneToOne(array( $metadata->mapOneToOne(array(
......
<?php <?php
class ForumCategory extends Doctrine_Entity { class ForumCategory extends Doctrine_Entity
public static function initMetadata($class) { {
$class->mapColumn('id', 'integer', 4, array('primary'));
$class->mapColumn('position', 'integer'); public static function initMetadata($mapping)
$class->mapColumn('name', 'string', 255); {
$class->hasMany('ForumBoard as boards', array( $mapping->mapField(array(
'fieldName' => 'id',
'type' => 'integer',
'length' => 4,
'id' => true
));
$mapping->mapField(array(
'fieldName' => 'position',
'type' => 'integer'
));
$mapping->mapField(array(
'fieldName' => 'name',
'type' => 'string',
'length' => 255
));
$mapping->hasMany('ForumBoard as boards', array(
'local' => 'id' , 'foreign' => 'category_id')); 'local' => 'id' , 'foreign' => 'category_id'));
} }
} }
<?php
#namespace Doctrine::Test::ORM::Models;
#use Doctrine::ORM::Entity;
class ForumEntry extends Doctrine_Entity
{
#protected $id;
#protected $topic;
public static function initMetadata($mapping)
{
$mapping->mapField(array(
'fieldName' => 'id',
'type' => 'integer',
'length' => 4,
'id' => true,
'generatorType' => 'auto'
));
$mapping->mapField(array(
'fieldName' => 'topic',
'type' => 'string',
'length' => 50
));
}
}
?>
\ No newline at end of file
<?php <?php
#namespace Doctrine::Tests::ORM::Models::Forum;
#use Doctrine::ORM::Entity;
class ForumUser extends Doctrine_Entity class ForumUser extends Doctrine_Entity
{ {
public static function initMetadata($class) #protected $dtype;
#protected $id;
#protected $username;
public static function initMetadata($mapping)
{ {
// inheritance mapping // inheritance mapping
$class->setInheritanceType(Doctrine::INHERITANCE_TYPE_JOINED, array( $mapping->setInheritanceType('joined', array(
'discriminatorColumn' => 'dtype', 'discriminatorColumn' => 'dtype',
'discriminatorMap' => array( 'discriminatorMap' => array(
'user' => 'ForumUser', 'user' => 'ForumUser',
'admin' => 'ForumAdministrator') 'admin' => 'ForumAdministrator')
)); ));
// register subclasses // register subclasses
$class->setSubclasses(array('ForumAdministrator')); $mapping->setSubclasses(array('ForumAdministrator'));
// the discriminator column // the discriminator column
$class->mapColumn('dtype', 'string', 50); $mapping->mapField(array(
'fieldName' => 'dtype',
'type' => 'string',
'length' => 50
));
// column-to-field mapping // column-to-field mapping
$class->mapColumn('id', 'integer', 4, array( $mapping->mapField(array(
'primary' => true, 'fieldName' => 'id',
'autoincrement' => true)); 'type' => 'integer',
$class->mapColumn('username', 'string', 50, array()); 'length' => 4,
'id' => true,
'generatorType' => 'auto'
));
$mapping->mapField(array(
'fieldName' => 'username',
'type' => 'string',
'length' => 50
));
} }
......
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