Checkin of occasional work from the past weeks.
This commit is contained in:
parent
c43f9588be
commit
d9975c36a3
228
lib/Doctrine/Association.php
Normal file
228
lib/Doctrine/Association.php
Normal file
@ -0,0 +1,228 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
80
lib/Doctrine/Association/ManyToMany.php
Normal file
80
lib/Doctrine/Association/ManyToMany.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
28
lib/Doctrine/Association/OneToMany.php
Normal file
28
lib/Doctrine/Association/OneToMany.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
139
lib/Doctrine/Association/OneToOne.php
Normal file
139
lib/Doctrine/Association/OneToOne.php
Normal file
@ -0,0 +1,139 @@
|
||||
<?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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
File diff suppressed because it is too large
Load Diff
@ -19,6 +19,8 @@
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
|
||||
#namespace Doctrine::ORM::Internal;
|
||||
|
||||
/**
|
||||
* The metadata factory is used to create ClassMetadata objects that contain all the
|
||||
* metadata of a class.
|
||||
@ -31,10 +33,11 @@
|
||||
* @version $Revision$
|
||||
* @link www.phpdoctrine.org
|
||||
* @since 2.0
|
||||
* @todo Rename to ClassMetadataFactory.
|
||||
*/
|
||||
class Doctrine_ClassMetadata_Factory
|
||||
{
|
||||
protected $_conn;
|
||||
protected $_em;
|
||||
protected $_driver;
|
||||
|
||||
/**
|
||||
@ -52,7 +55,7 @@ class Doctrine_ClassMetadata_Factory
|
||||
*/
|
||||
public function __construct(Doctrine_EntityManager $em, $driver)
|
||||
{
|
||||
$this->_conn = $em;
|
||||
$this->_em = $em;
|
||||
$this->_driver = $driver;
|
||||
}
|
||||
|
||||
@ -101,7 +104,7 @@ class Doctrine_ClassMetadata_Factory
|
||||
$class = $classes[$loadedParentClass];
|
||||
} else {
|
||||
$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);
|
||||
$classes[$rootClassOfHierarchy] = $class;
|
||||
}
|
||||
@ -115,7 +118,7 @@ class Doctrine_ClassMetadata_Factory
|
||||
|
||||
$parent = $class;
|
||||
foreach ($parentClasses as $subclassName) {
|
||||
$subClass = new Doctrine_ClassMetadata($subclassName, $this->_conn);
|
||||
$subClass = new Doctrine_ClassMetadata($subclassName, $this->_em);
|
||||
$subClass->setInheritanceType($parent->getInheritanceType(), $parent->getInheritanceOptions());
|
||||
$this->_addInheritedFields($subClass, $parent);
|
||||
$this->_addInheritedRelations($subClass, $parent);
|
||||
@ -130,14 +133,10 @@ class Doctrine_ClassMetadata_Factory
|
||||
|
||||
protected function _addInheritedFields($subClass, $parentClass)
|
||||
{
|
||||
foreach ($parentClass->getFieldMappings() as $name => $definition) {
|
||||
foreach ($parentClass->getFieldMappings() as $fieldName => $mapping) {
|
||||
$fullName = "$name as " . $parentClass->getFieldName($name);
|
||||
$definition['inherited'] = true;
|
||||
$subClass->mapColumn(
|
||||
$fullName,
|
||||
$definition['type'],
|
||||
$definition['length'],
|
||||
$definition);
|
||||
$mapping['inherited'] = true;
|
||||
$subClass->mapField($mapping);
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,6 +162,7 @@ class Doctrine_ClassMetadata_Factory
|
||||
$names = array();
|
||||
$className = $name;
|
||||
// get parent classes
|
||||
//TODO: Skip Entity types MappedSuperclass/Transient
|
||||
do {
|
||||
if ($className === 'Doctrine_Entity') {
|
||||
break;
|
||||
@ -182,11 +182,13 @@ class Doctrine_ClassMetadata_Factory
|
||||
// load further metadata
|
||||
$this->_driver->loadMetadataForClass($name, $class);
|
||||
|
||||
// set default table name, if necessary
|
||||
$tableName = $class->getTableName();
|
||||
if ( ! isset($tableName)) {
|
||||
$class->setTableName(Doctrine::tableize($class->getClassName()));
|
||||
}
|
||||
|
||||
// complete identifier mapping
|
||||
$this->_initIdentifier($class);
|
||||
|
||||
return $class;
|
||||
@ -199,7 +201,7 @@ class Doctrine_ClassMetadata_Factory
|
||||
*/
|
||||
protected function _initIdentifier(Doctrine_ClassMetadata $class)
|
||||
{
|
||||
switch (count((array)$class->getIdentifier())) {
|
||||
/*switch (count($class->getIdentifier())) {
|
||||
case 0: // No identifier in the class mapping yet
|
||||
|
||||
// If its a subclass, inherit the identifier from the parent.
|
||||
@ -217,7 +219,7 @@ class Doctrine_ClassMetadata_Factory
|
||||
}
|
||||
|
||||
// add all inherited primary keys
|
||||
foreach ((array) $class->getIdentifier() as $id) {
|
||||
foreach ($class->getIdentifier() as $id) {
|
||||
$definition = $rootClass->getDefinitionOf($id);
|
||||
|
||||
// inherited primary keys shouldn't contain autoinc
|
||||
@ -231,16 +233,7 @@ class Doctrine_ClassMetadata_Factory
|
||||
$definition, true);
|
||||
}
|
||||
} else {
|
||||
throw Doctrine_MappingException::identifierRequired($class->getClassName());
|
||||
/* Legacy behavior of auto-adding an id field
|
||||
$definition = array('type' => 'integer',
|
||||
'length' => 20,
|
||||
'autoincrement' => true,
|
||||
'primary' => true);
|
||||
$class->mapColumn('id', $definition['type'], $definition['length'], $definition, true);
|
||||
$class->setIdentifier(array('id'));
|
||||
$class->setIdentifierType(Doctrine::IDENTIFIER_AUTOINC);
|
||||
*/
|
||||
throw Doctrine_MappingException::identifierRequired($class->getClassName());
|
||||
}
|
||||
break;
|
||||
case 1: // A single identifier is in the mapping
|
||||
@ -293,6 +286,36 @@ class Doctrine_ClassMetadata_Factory
|
||||
break;
|
||||
default: // Multiple identifiers are in the mapping so its a composite id
|
||||
$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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,10 +33,10 @@
|
||||
* @since 1.0
|
||||
* @version $Revision$
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @todo Rename to EntityCollection
|
||||
*/
|
||||
class Doctrine_Collection extends Doctrine_Access implements Countable, IteratorAggregate, Serializable
|
||||
{
|
||||
{
|
||||
|
||||
protected $_entityBaseType;
|
||||
|
||||
/**
|
||||
@ -44,42 +44,30 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data = array();
|
||||
|
||||
/**
|
||||
* The mapper object used to map the records of this collection to the database.
|
||||
*
|
||||
* @var Doctrine_Mapper
|
||||
*/
|
||||
protected $_mapper;
|
||||
protected $_data = array();
|
||||
|
||||
/**
|
||||
* A snapshot of the fetched data.
|
||||
* A snapshot of the collection at the moment it was fetched from the database.
|
||||
* This is used to create a diff of the collection at commit time.
|
||||
*
|
||||
* @var array
|
||||
* @var array
|
||||
*/
|
||||
protected $_snapshot = array();
|
||||
|
||||
/**
|
||||
* This record this collection is attached to, if any.
|
||||
* This entity that owns this collection.
|
||||
*
|
||||
* @var Doctrine_Entity
|
||||
* @var Doctrine::ORM::Entity
|
||||
*/
|
||||
protected $_owner;
|
||||
|
||||
/**
|
||||
* The reference field of the collection.
|
||||
* The association mapping the collection belongs to.
|
||||
* This is currently either a OneToManyMapping or a ManyToManyMapping.
|
||||
*
|
||||
* @var string $referenceField
|
||||
* @var Doctrine::ORM::Mapping::AssociationMapping
|
||||
*/
|
||||
protected $referenceField;
|
||||
|
||||
/**
|
||||
* The relation this collection is related to, if any.
|
||||
*
|
||||
* @var Doctrine_Relation
|
||||
*/
|
||||
protected $relation;
|
||||
protected $_associationMapping;
|
||||
|
||||
/**
|
||||
* The name of the column that is used for collection key mapping.
|
||||
@ -101,6 +89,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
* @var EntityManager
|
||||
*/
|
||||
protected $_em;
|
||||
protected $_isDirty = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -113,18 +102,9 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
{
|
||||
$this->_entityBaseType = $entityBaseType;
|
||||
$this->_em = Doctrine_EntityManagerFactory::getManager($entityBaseType);
|
||||
$this->_mapper = $this->_em->getEntityPersister($entityBaseType);
|
||||
|
||||
if ($keyField === null) {
|
||||
$keyField = $this->_mapper->getClassMetadata()->getBoundQueryPart('indexBy');
|
||||
}
|
||||
|
||||
if ($keyField === null) {
|
||||
//$keyField = $mapper->getClassMetadata()->getAttribute(Doctrine::ATTR_COLL_KEY);
|
||||
}
|
||||
|
||||
if ($keyField !== null) {
|
||||
if ( ! $this->_mapper->getClassMetadata()->hasField($keyField)) {
|
||||
if ( ! $this->_em->getClassMetadata($entityBaseType)->hasField($keyField)) {
|
||||
throw new Doctrine_Collection_Exception("Invalid field '$keyField' can't be uses as key.");
|
||||
}
|
||||
$this->_keyField = $keyField;
|
||||
@ -139,7 +119,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*/
|
||||
public function setData(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->_data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -155,14 +135,11 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
$vars = get_object_vars($this);
|
||||
|
||||
unset($vars['reference']);
|
||||
unset($vars['reference_field']);
|
||||
unset($vars['relation']);
|
||||
unset($vars['expandable']);
|
||||
unset($vars['expanded']);
|
||||
unset($vars['generator']);
|
||||
|
||||
$vars['_mapper'] = $vars['_mapper']->getComponentName();
|
||||
|
||||
return serialize($vars);
|
||||
}
|
||||
|
||||
@ -187,12 +164,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
$this->$name = $values;
|
||||
}
|
||||
|
||||
$this->_mapper = $manager->getEntityPersister($this->_entityBaseType);
|
||||
|
||||
$keyColumn = isset($array['keyField']) ? $array['keyField'] : null;
|
||||
if ($keyColumn === null) {
|
||||
$keyColumn = $this->_mapper->getClassMetadata()->getBoundQueryPart('indexBy');
|
||||
}
|
||||
|
||||
if ($keyColumn !== null) {
|
||||
$this->_keyField = $keyColumn;
|
||||
@ -231,7 +203,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
return $this->_data;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -242,7 +214,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*/
|
||||
public function getFirst()
|
||||
{
|
||||
return reset($this->data);
|
||||
return reset($this->_data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -253,7 +225,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*/
|
||||
public function getLast()
|
||||
{
|
||||
return end($this->data);
|
||||
return end($this->_data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -263,7 +235,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*/
|
||||
public function end()
|
||||
{
|
||||
return end($this->data);
|
||||
return end($this->_data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -273,7 +245,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return key($this->data);
|
||||
return key($this->_data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -285,13 +257,13 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
public function setReference(Doctrine_Entity $entity, Doctrine_Relation $relation)
|
||||
{
|
||||
$this->_owner = $entity;
|
||||
$this->relation = $relation;
|
||||
//$this->relation = $relation;
|
||||
|
||||
if ($relation instanceof Doctrine_Relation_ForeignKey ||
|
||||
/*if ($relation instanceof Doctrine_Relation_ForeignKey ||
|
||||
$relation instanceof Doctrine_Relation_LocalKey) {
|
||||
$this->referenceField = $relation->getForeignFieldName();
|
||||
$value = $entity->get($relation->getLocalFieldName());
|
||||
foreach ($this->data as $entity) {
|
||||
foreach ($this->_data as $entity) {
|
||||
if ($value !== null) {
|
||||
$entity->set($this->referenceField, $value, false);
|
||||
} else {
|
||||
@ -300,7 +272,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
}
|
||||
} else if ($relation instanceof Doctrine_Relation_Association) {
|
||||
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/**
|
||||
@ -322,8 +294,8 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*/
|
||||
public function remove($key)
|
||||
{
|
||||
$removed = $this->data[$key];
|
||||
unset($this->data[$key]);
|
||||
$removed = $this->_data[$key];
|
||||
unset($this->_data[$key]);
|
||||
return $removed;
|
||||
}
|
||||
|
||||
@ -336,7 +308,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*/
|
||||
public function contains($key)
|
||||
{
|
||||
return isset($this->data[$key]);
|
||||
return isset($this->_data[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -344,7 +316,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*/
|
||||
public function search(Doctrine_Entity $record)
|
||||
{
|
||||
return array_search($record, $this->data, true);
|
||||
return array_search($record, $this->_data, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -357,8 +329,8 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
if (isset($this->data[$key])) {
|
||||
return $this->data[$key];
|
||||
if (isset($this->_data[$key])) {
|
||||
return $this->_data[$key];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -374,7 +346,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
$list = array();
|
||||
$idFieldNames = (array)$this->_mapper->getClassMetadata()->getIdentifier();
|
||||
|
||||
foreach ($this->data as $record) {
|
||||
foreach ($this->_data as $record) {
|
||||
if (is_array($record)) {
|
||||
if (count($idFieldNames) > 1) {
|
||||
$id = array();
|
||||
@ -406,7 +378,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*/
|
||||
public function getKeys()
|
||||
{
|
||||
return array_keys($this->data);
|
||||
return array_keys($this->_data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -418,7 +390,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->data);
|
||||
return count($this->_data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -429,69 +401,49 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
* @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, $entity)
|
||||
public function set($key, $value)
|
||||
{
|
||||
if ( ! $entity instanceof Doctrine_Entity) {
|
||||
if ( ! $value instanceof Doctrine_Entity) {
|
||||
throw new Doctrine_Collection_Exception('Value variable in set is not an instance of Doctrine_Entity');
|
||||
}
|
||||
|
||||
if (isset($this->referenceField)) {
|
||||
$entity->set($this->referenceField, $this->_owner, false);
|
||||
}
|
||||
$this->data[$key] = $entity;
|
||||
$this->_data[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a record to collection
|
||||
*
|
||||
* @param Doctrine_Entity $record record to be added
|
||||
* @param string $key optional key for the record
|
||||
* @return boolean
|
||||
*/
|
||||
public function add($record, $key = null)
|
||||
public function add($value, $key = null)
|
||||
{
|
||||
/** @TODO Use raw getters/setters */
|
||||
if ( ! $record instanceof Doctrine_Entity) {
|
||||
if ( ! $value instanceof Doctrine_Entity) {
|
||||
throw new Doctrine_Record_Exception('Value variable in set is not an instance of Doctrine_Entity.');
|
||||
}
|
||||
|
||||
if (isset($this->referenceField)) {
|
||||
$value = $this->_owner->get($this->relation->getLocalFieldName());
|
||||
|
||||
if ($value !== null) {
|
||||
$record->set($this->referenceField, $value, false);
|
||||
} else {
|
||||
$record->set($this->referenceField, $this->_owner, false);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 === $record) {
|
||||
foreach ($this->_data as $val) {
|
||||
if ($val === $value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($key)) {
|
||||
if (isset($this->data[$key])) {
|
||||
if (isset($this->_data[$key])) {
|
||||
return false;
|
||||
}
|
||||
$this->data[$key] = $record;
|
||||
$this->_data[$key] = $value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// why is this not checked when the keyColumn is set?
|
||||
if (isset($this->_keyField)) {
|
||||
$value = $record->get($this->_keyField);
|
||||
if ($value === null) {
|
||||
throw new Doctrine_Collection_Exception("Couldn't create collection index. Record field '".$this->_keyField."' was null.");
|
||||
}
|
||||
$this->data[$value] = $record;
|
||||
} else {
|
||||
$this->data[] = $record;
|
||||
}
|
||||
$this->_data[] = $value;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -509,7 +461,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
$query = new Doctrine_Query($this->_mapper->getConnection());
|
||||
|
||||
if ( ! isset($name)) {
|
||||
foreach ($this->data as $record) {
|
||||
foreach ($this->_data as $record) {
|
||||
// FIXME: composite key support
|
||||
$ids = $record->identifier();
|
||||
$value = count($ids) > 0 ? array_pop($ids) : null;
|
||||
@ -528,11 +480,11 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
$rel = $this->_mapper->getTable()->getRelation($name);
|
||||
|
||||
if ($rel instanceof Doctrine_Relation_LocalKey || $rel instanceof Doctrine_Relation_ForeignKey) {
|
||||
foreach ($this->data as $record) {
|
||||
foreach ($this->_data as $record) {
|
||||
$list[] = $record[$rel->getLocal()];
|
||||
}
|
||||
} else {
|
||||
foreach ($this->data as $record) {
|
||||
foreach ($this->_data as $record) {
|
||||
$ids = $record->identifier();
|
||||
$value = count($ids) > 0 ? array_pop($ids) : null;
|
||||
if ($value !== null) {
|
||||
@ -563,15 +515,15 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
$local = $rel->getLocal();
|
||||
|
||||
if ($rel instanceof Doctrine_Relation_LocalKey) {
|
||||
foreach ($this->data as $key => $record) {
|
||||
foreach ($this->_data as $key => $record) {
|
||||
foreach ($coll as $k => $related) {
|
||||
if ($related[$foreign] == $record[$local]) {
|
||||
$this->data[$key]->_setRelated($name, $related);
|
||||
$this->_data[$key]->_setRelated($name, $related);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ($rel instanceof Doctrine_Relation_ForeignKey) {
|
||||
foreach ($this->data as $key => $record) {
|
||||
foreach ($this->_data as $key => $record) {
|
||||
if ( ! $record->exists()) {
|
||||
continue;
|
||||
}
|
||||
@ -584,7 +536,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
}
|
||||
}
|
||||
|
||||
$this->data[$key]->_setRelated($name, $sub);
|
||||
$this->_data[$key]->_setRelated($name, $sub);
|
||||
}
|
||||
} else if ($rel instanceof Doctrine_Relation_Association) {
|
||||
// @TODO composite key support
|
||||
@ -592,7 +544,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
$asf = $rel->getAssociationFactory();
|
||||
$name = $table->getComponentName();
|
||||
|
||||
foreach ($this->data as $key => $record) {
|
||||
foreach ($this->_data as $key => $record) {
|
||||
if ( ! $record->exists()) {
|
||||
continue;
|
||||
}
|
||||
@ -603,7 +555,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
$sub->add($related->get($name));
|
||||
}
|
||||
}
|
||||
$this->data[$key]->_setRelated($name, $sub);
|
||||
$this->_data[$key]->_setRelated($name, $sub);
|
||||
|
||||
}
|
||||
}
|
||||
@ -635,7 +587,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*/
|
||||
public function takeSnapshot()
|
||||
{
|
||||
$this->_snapshot = $this->data;
|
||||
$this->_snapshot = $this->_data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -664,7 +616,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*/
|
||||
public function processDiff()
|
||||
{
|
||||
foreach (array_udiff($this->_snapshot, $this->data, array($this, "_compareRecords")) as $record) {
|
||||
foreach (array_udiff($this->_snapshot, $this->_data, array($this, "_compareRecords")) as $record) {
|
||||
$record->delete();
|
||||
}
|
||||
return $this;
|
||||
@ -686,6 +638,11 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function isEmpty()
|
||||
{
|
||||
return $this->count() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fromArray
|
||||
@ -776,7 +733,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*/
|
||||
public function getDeleteDiff()
|
||||
{
|
||||
return array_udiff($this->_snapshot, $this->data, array($this, "_compareRecords"));
|
||||
return array_udiff($this->_snapshot, $this->_data, array($this, "_compareRecords"));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -786,7 +743,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*/
|
||||
public function getInsertDiff()
|
||||
{
|
||||
return array_udiff($this->data, $this->_snapshot, array($this, "_compareRecords"));
|
||||
return array_udiff($this->_data, $this->_snapshot, array($this, "_compareRecords"));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -803,7 +760,6 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
}
|
||||
|
||||
/**
|
||||
* save
|
||||
* Saves all records of this collection and processes the
|
||||
* difference of the last snapshot and the current data.
|
||||
*
|
||||
@ -869,7 +825,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
}
|
||||
}
|
||||
|
||||
$this->data = array();
|
||||
$this->_data = array();
|
||||
|
||||
if ($this->_owner) {
|
||||
$this->_owner->free($deep);
|
||||
@ -884,7 +840,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
$data = $this->data;
|
||||
$data = $this->_data;
|
||||
return new ArrayIterator($data);
|
||||
}
|
||||
|
||||
@ -907,6 +863,6 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
|
||||
|
||||
public function clear()
|
||||
{
|
||||
$this->data = array();
|
||||
$this->_data = array();
|
||||
}
|
||||
}
|
||||
|
@ -138,8 +138,9 @@ abstract class Doctrine_Connection implements Countable
|
||||
* @var array $properties
|
||||
*/
|
||||
protected $properties = array(
|
||||
'sql_comments' => array(array('start' => '--', 'end' => "\n", 'escape' => false),
|
||||
array('start' => '/*', 'end' => '*/', 'escape' => false)),
|
||||
'sql_comments' => array(
|
||||
array('start' => '--', 'end' => "\n", 'escape' => false),
|
||||
array('start' => '/*', 'end' => '*/', 'escape' => false)),
|
||||
'identifier_quoting' => array('start' => '"', 'end' => '"','escape' => '"'),
|
||||
'string_quoting' => array('start' => "'", 'end' => "'", 'escape' => false,
|
||||
'escape_pattern' => false),
|
||||
@ -154,6 +155,8 @@ abstract class Doctrine_Connection implements Countable
|
||||
|
||||
/**
|
||||
* The parameters used during creation of the Connection.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_params = array();
|
||||
|
||||
@ -364,24 +367,24 @@ abstract class Doctrine_Connection implements Countable
|
||||
//$this->getListener()->preConnect($event);
|
||||
|
||||
// TODO: the extension_loaded check can happen earlier, maybe in the factory
|
||||
if (extension_loaded('pdo')) {
|
||||
$driverOptions = isset($this->_params['driverOptions']) ?
|
||||
$this->_params['driverOptions'] : array();
|
||||
$user = isset($this->_params['user']) ?
|
||||
$this->_params['user'] : null;
|
||||
$password = isset($this->_params['password']) ?
|
||||
$this->_params['password'] : null;
|
||||
$this->_pdo = new PDO(
|
||||
$this->_constructPdoDsn(),
|
||||
$user,
|
||||
$password,
|
||||
$driverOptions
|
||||
);
|
||||
$this->_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$this->_pdo->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
|
||||
} else {
|
||||
if ( ! extension_loaded('pdo')) {
|
||||
throw new Doctrine_Connection_Exception("Couldn't locate driver named " . $e[0]);
|
||||
}
|
||||
|
||||
$driverOptions = isset($this->_params['driverOptions']) ?
|
||||
$this->_params['driverOptions'] : array();
|
||||
$user = isset($this->_params['user']) ?
|
||||
$this->_params['user'] : null;
|
||||
$password = isset($this->_params['password']) ?
|
||||
$this->_params['password'] : null;
|
||||
$this->_pdo = new PDO(
|
||||
$this->_constructPdoDsn(),
|
||||
$user,
|
||||
$password,
|
||||
$driverOptions
|
||||
);
|
||||
$this->_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$this->_pdo->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
|
||||
|
||||
// attach the pending attributes to adapter
|
||||
/*foreach($this->pendingAttributes as $attr => $value) {
|
||||
@ -427,9 +430,8 @@ abstract class Doctrine_Connection implements Countable
|
||||
*/
|
||||
public function supports($feature)
|
||||
{
|
||||
return (isset($this->supported[$feature])
|
||||
&& ($this->supported[$feature] === 'emulated'
|
||||
|| $this->supported[$feature]));
|
||||
return (isset($this->supported[$feature]) &&
|
||||
($this->supported[$feature] === 'emulated' || $this->supported[$feature]));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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 string $type
|
||||
@ -794,10 +795,9 @@ abstract class Doctrine_Connection implements Countable
|
||||
$this->getAttribute(Doctrine::ATTR_LISTENER)->postPrepare($event);
|
||||
|
||||
return new Doctrine_Connection_Statement($this, $stmt);
|
||||
} catch(Doctrine_Adapter_Exception $e) {
|
||||
} catch(PDOException $e) { }
|
||||
|
||||
$this->rethrowException($e, $this);
|
||||
} catch (PDOException $e) {
|
||||
$this->rethrowException($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -859,10 +859,9 @@ abstract class Doctrine_Connection implements Countable
|
||||
|
||||
return $stmt;
|
||||
}
|
||||
} catch (Doctrine_Adapter_Exception $e) {
|
||||
} catch (PDOException $e) { }
|
||||
|
||||
$this->rethrowException($e, $this);
|
||||
} catch (PDOException $e) {
|
||||
$this->rethrowException($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -894,10 +893,9 @@ abstract class Doctrine_Connection implements Countable
|
||||
|
||||
return $count;
|
||||
}
|
||||
} catch (Doctrine_Adapter_Exception $e) {
|
||||
} catch (PDOException $e) { }
|
||||
|
||||
$this->rethrowException($e, $this);
|
||||
} catch (PDOException $e) {
|
||||
$this->rethrowException($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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
|
||||
*/
|
||||
@ -940,9 +938,7 @@ abstract class Doctrine_Connection implements Countable
|
||||
}
|
||||
$exc->processErrorInfo($e->errorInfo);
|
||||
|
||||
if ($this->getAttribute(Doctrine::ATTR_THROW_EXCEPTIONS)) {
|
||||
throw $exc;
|
||||
}
|
||||
throw $exc;
|
||||
|
||||
//$this->getListener()->postError($event);
|
||||
}
|
||||
@ -1178,7 +1174,6 @@ abstract class Doctrine_Connection implements Countable
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getFormatter()
|
||||
{
|
||||
if ( ! $this->modules['formatter']) {
|
||||
@ -1186,4 +1181,25 @@ abstract class Doctrine_Connection implements Countable
|
||||
}
|
||||
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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -37,45 +37,23 @@
|
||||
* @link www.phpdoctrine.org
|
||||
* @since 2.0
|
||||
* @version $Revision: 4342 $
|
||||
* @todo Split up into "Entity" and "ActiveEntity" (extends Entity)
|
||||
* @todo Move entity states into a separate enumeration (EntityStates).
|
||||
* They do not need to be exposed to users in such a way. The states are mainly
|
||||
* for internal use.
|
||||
* @todo Split up into "Entity" and "ActiveEntity" (extends Entity).
|
||||
*/
|
||||
abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
|
||||
{
|
||||
/**
|
||||
* DIRTY STATE
|
||||
* An Entity is in dirty state when its properties are changed.
|
||||
* MANAGED
|
||||
* An Entity is in managed state when it has a primary key/identifier and is
|
||||
* managed by an EntityManager (registered in the identity map).
|
||||
*/
|
||||
const STATE_DIRTY = 1;
|
||||
const STATE_MANAGED_DIRTY = 1;
|
||||
const STATE_MANAGED = 1;
|
||||
|
||||
/**
|
||||
* TDIRTY STATE
|
||||
* An Entity is in transient dirty state when it is created and some of its
|
||||
* fields are modified but it is NOT yet persisted into database.
|
||||
* NEW
|
||||
* An Entity is new if it does not yet have an identifier/primary key
|
||||
* and is not (yet) managed by an EntityManager.
|
||||
*/
|
||||
const STATE_TDIRTY = 2;
|
||||
const STATE_NEW_DIRTY = 2;
|
||||
|
||||
/**
|
||||
* CLEAN STATE
|
||||
* An Entity is in clean state when all of its properties are loaded from the database
|
||||
* and none of its properties are changed.
|
||||
*/
|
||||
const STATE_CLEAN = 3;
|
||||
const STATE_MANAGED_CLEAN = 3;
|
||||
|
||||
/**
|
||||
* NEW TCLEAN
|
||||
* An Entity is in transient clean state when it is created and none of its
|
||||
* fields are modified.
|
||||
* @todo Do we need this state? Just STATE_NEW may be enough without differentiating
|
||||
* clean/dirty. A new entity is always "dirty".
|
||||
*/
|
||||
const STATE_TCLEAN = 5;
|
||||
const STATE_NEW_CLEAN = 5;
|
||||
const STATE_NEW = 2;
|
||||
|
||||
/**
|
||||
* LOCKED STATE
|
||||
@ -93,14 +71,14 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
|
||||
* (or no longer) associated with an EntityManager (and a UnitOfWork).
|
||||
* This means its no longer in the identity map.
|
||||
*/
|
||||
const STATE_DETACHED = 7;
|
||||
const STATE_DETACHED = 3;
|
||||
|
||||
/**
|
||||
* A removed Entity instance is an instance with a persistent identity,
|
||||
* associated with an EntityManager, that is scheduled for removal from the
|
||||
* database.
|
||||
*/
|
||||
const STATE_DELETED = 8;
|
||||
const STATE_DELETED = 4;
|
||||
|
||||
/**
|
||||
* Index used for creating object identifiers (oid's).
|
||||
@ -144,12 +122,6 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
|
||||
*/
|
||||
private $_entityName;
|
||||
|
||||
/**
|
||||
* @var Doctrine_Node_<TreeImpl> node object
|
||||
* @todo Specific to the NestedSet Behavior plugin. Move outta here.
|
||||
*/
|
||||
//protected $_node;
|
||||
|
||||
/**
|
||||
* The values that make up the ID/primary key of the entity.
|
||||
*
|
||||
@ -167,16 +139,16 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
|
||||
/**
|
||||
* The state of the object.
|
||||
*
|
||||
* @var integer
|
||||
* @see STATE_* constants
|
||||
* @var integer
|
||||
*/
|
||||
private $_state;
|
||||
|
||||
/**
|
||||
* The names of fields that have been modified but not yet persisted.
|
||||
* Keys are field names, values oldValue => newValue tuples.
|
||||
*
|
||||
* @var array
|
||||
* @todo Better name? $_modifiedFields?
|
||||
* @var array
|
||||
* @todo Rename to $_changeSet
|
||||
*/
|
||||
private $_modified = array();
|
||||
|
||||
@ -204,6 +176,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Creates a new Entity instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
@ -214,9 +187,9 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
|
||||
$this->_data = $this->_em->_getTmpEntityData();
|
||||
if ($this->_data) {
|
||||
$this->_extractIdentifier();
|
||||
$this->_state = self::STATE_CLEAN;
|
||||
$this->_state = self::STATE_MANAGED;
|
||||
} else {
|
||||
$this->_state = self::STATE_TCLEAN;
|
||||
$this->_state = self::STATE_NEW;
|
||||
}
|
||||
|
||||
// @todo read from attribute the first time and move this initialization elsewhere.
|
||||
@ -265,7 +238,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
|
||||
}*/
|
||||
|
||||
/**
|
||||
* hydrates this object from given array
|
||||
* Hydrates this object from given array
|
||||
*
|
||||
* @param array $data
|
||||
* @return boolean
|
||||
@ -278,33 +251,26 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
|
||||
|
||||
/**
|
||||
* Copies the identifier names and values from _data into _id.
|
||||
*
|
||||
* @param boolean $exists whether or not this record exists in persistent data store
|
||||
* @return void
|
||||
* @todo Looks like its better placed elsewhere (EntityManager?)
|
||||
*/
|
||||
private function _extractIdentifier()
|
||||
{
|
||||
switch ($this->_class->getIdentifierType()) {
|
||||
case Doctrine::IDENTIFIER_AUTOINC:
|
||||
case Doctrine::IDENTIFIER_SEQUENCE:
|
||||
case Doctrine::IDENTIFIER_NATURAL:
|
||||
$name = $this->_class->getIdentifier();
|
||||
$name = $name[0];
|
||||
if (isset($this->_data[$name]) && $this->_data[$name] !== Doctrine_Null::$INSTANCE) {
|
||||
if ( ! $this->_class->isIdentifierComposite()) {
|
||||
// Single field identifier
|
||||
$name = $this->_class->getIdentifier();
|
||||
$name = $name[0];
|
||||
if (isset($this->_data[$name]) && $this->_data[$name] !== Doctrine_Null::$INSTANCE) {
|
||||
$this->_id[$name] = $this->_data[$name];
|
||||
}
|
||||
} else {
|
||||
// Composite identifier
|
||||
$names = $this->_class->getIdentifier();
|
||||
foreach ($names as $name) {
|
||||
if ($this->_data[$name] === Doctrine_Null::$INSTANCE) {
|
||||
$this->_id[$name] = null;
|
||||
} else {
|
||||
$this->_id[$name] = $this->_data[$name];
|
||||
}
|
||||
break;
|
||||
case Doctrine::IDENTIFIER_COMPOSITE:
|
||||
$names = $this->_class->getIdentifier();
|
||||
foreach ($names as $name) {
|
||||
if ($this->_data[$name] === Doctrine_Null::$INSTANCE) {
|
||||
$this->_id[$name] = null;
|
||||
} else {
|
||||
$this->_id[$name] = $this->_data[$name];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -438,22 +404,16 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
|
||||
|
||||
/* TODO: Do we really need this check? This is only for internal use after all. */
|
||||
switch ($state) {
|
||||
case self::STATE_TCLEAN:
|
||||
case self::STATE_CLEAN:
|
||||
case self::STATE_TDIRTY:
|
||||
case self::STATE_DIRTY:
|
||||
case self::STATE_PROXY:
|
||||
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);
|
||||
}
|
||||
|
||||
if ($this->_state === Doctrine_Entity::STATE_TCLEAN ||
|
||||
$this->_state === Doctrine_Entity::STATE_CLEAN) {
|
||||
$this->_modified = array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -878,23 +838,13 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
|
||||
}*/
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
switch ($this->_state) {
|
||||
case Doctrine_Entity::STATE_CLEAN:
|
||||
$this->_state = Doctrine_Entity::STATE_DIRTY;
|
||||
break;
|
||||
case Doctrine_Entity::STATE_TCLEAN:
|
||||
$this->_state = Doctrine_Entity::STATE_TDIRTY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if ($this->_class->hasRelation($fieldName)) {
|
||||
$this->_rawSetReference($fieldName, $value);
|
||||
@ -1253,7 +1203,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
|
||||
*/
|
||||
final public function isNew()
|
||||
{
|
||||
return $this->_state == self::STATE_TCLEAN || $this->_state == self::STATE_TDIRTY;
|
||||
return $this->_state == self::STATE_NEW;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1264,8 +1214,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
|
||||
*/
|
||||
final public function isModified()
|
||||
{
|
||||
return ($this->_state === Doctrine_Entity::STATE_DIRTY ||
|
||||
$this->_state === Doctrine_Entity::STATE_TDIRTY);
|
||||
return count($this->_modified) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1349,44 +1298,33 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
|
||||
|
||||
/**
|
||||
* INTERNAL:
|
||||
* Assigns an identifier to the entity. This is only intended for use by
|
||||
* the EntityPersisters or the UnitOfWork.
|
||||
*
|
||||
* @param integer $id
|
||||
* @return void
|
||||
* @todo Not sure this is the right place here.
|
||||
* @param mixed $id
|
||||
*/
|
||||
final public function assignIdentifier($id = false)
|
||||
final public function _assignIdentifier($id)
|
||||
{
|
||||
if ($id === false) {
|
||||
$this->_id = array();
|
||||
$this->_state = Doctrine_Entity::STATE_TCLEAN;
|
||||
$this->_modified = array();
|
||||
} else if ($id === true) {
|
||||
$this->_extractIdentifier(true);
|
||||
$this->_state = Doctrine_Entity::STATE_CLEAN;
|
||||
$this->_modified = array();
|
||||
} else {
|
||||
if (is_array($id)) {
|
||||
foreach ($id as $fieldName => $value) {
|
||||
$this->_id[$fieldName] = $value;
|
||||
$this->_data[$fieldName] = $value;
|
||||
}
|
||||
} else {
|
||||
$idFieldNames = $this->_class->getIdentifier();
|
||||
$name = $idFieldNames[0];
|
||||
$this->_id[$name] = $id;
|
||||
$this->_data[$name] = $id;
|
||||
if (is_array($id)) {
|
||||
foreach ($id as $fieldName => $value) {
|
||||
$this->_id[$fieldName] = $value;
|
||||
$this->_data[$fieldName] = $value;
|
||||
}
|
||||
$this->_state = self::STATE_CLEAN;
|
||||
$this->_modified = array();
|
||||
} else {
|
||||
$name = $this->_class->getSingleIdentifierFieldName();
|
||||
$this->_id[$name] = $id;
|
||||
$this->_data[$name] = $id;
|
||||
}
|
||||
$this->_modified = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the primary keys of this object
|
||||
* INTERNAL:
|
||||
* Returns the primary keys of the entity (key => value pairs).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
final public function identifier()
|
||||
final public function _identifier()
|
||||
{
|
||||
return $this->_id;
|
||||
}
|
||||
@ -1656,13 +1594,13 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
|
||||
{
|
||||
$this->getNode()->delete();
|
||||
}*/
|
||||
|
||||
|
||||
/**
|
||||
* Gets the ClassMetadata object that describes the entity class.
|
||||
*
|
||||
* @return Doctrine::ORM::Mapping::ClassMetadata
|
||||
*/
|
||||
final public function getClassMetadata()
|
||||
final public function getClass()
|
||||
{
|
||||
return $this->_class;
|
||||
}
|
||||
|
@ -42,6 +42,10 @@
|
||||
*/
|
||||
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
|
||||
* to certain EntityManagers.
|
||||
@ -70,11 +74,11 @@ class Doctrine_EntityManager
|
||||
private static $_flushModes = array(
|
||||
// auto: Flush occurs automatically after each operation that issues database
|
||||
// queries. No operations are queued.
|
||||
'auto',
|
||||
self::FLUSHMODE_AUTO,
|
||||
// commit: Flush occurs automatically at transaction commit.
|
||||
'commit',
|
||||
self::FLUSHMODE_COMMIT,
|
||||
// manual: Flush occurs never automatically.
|
||||
'manual'
|
||||
self::FLUSHMODE_MANUAL
|
||||
);
|
||||
|
||||
/**
|
||||
@ -242,7 +246,7 @@ class Doctrine_EntityManager
|
||||
*/
|
||||
public function detach(Doctrine_Entity $entity)
|
||||
{
|
||||
return $this->_unitOfWork->unregisterIdentity($entity);
|
||||
return $this->_unitOfWork->removeFromIdentityMap($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -287,7 +291,7 @@ class Doctrine_EntityManager
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
$this->_unitOfWork->flush();
|
||||
$this->_unitOfWork->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -386,6 +390,9 @@ class Doctrine_EntityManager
|
||||
public function save(Doctrine_Entity $entity)
|
||||
{
|
||||
$this->_unitOfWork->save($entity);
|
||||
if ($this->_flushMode == self::FLUSHMODE_AUTO) {
|
||||
$this->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -453,7 +460,7 @@ class Doctrine_EntityManager
|
||||
return $entity;
|
||||
} else {
|
||||
$entity = new $className;
|
||||
$this->_unitOfWork->registerIdentity($entity);
|
||||
$this->_unitOfWork->addToIdentityMap($entity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -469,6 +476,17 @@ class Doctrine_EntityManager
|
||||
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:
|
||||
* For internal hydration purposes only.
|
||||
@ -546,7 +564,7 @@ class Doctrine_EntityManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the COnfiguration used by the EntityManager.
|
||||
* Gets the Configuration used by the EntityManager.
|
||||
*
|
||||
* @return Configuration
|
||||
*/
|
||||
@ -555,6 +573,16 @@ class Doctrine_EntityManager
|
||||
return $this->_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UnitOfWork used by the EntityManager to coordinate operations.
|
||||
*
|
||||
* @return Doctrine::ORM::UnitOfWork
|
||||
*/
|
||||
public function getUnitOfWork()
|
||||
{
|
||||
return $this->_unitOfWork;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
* $Id: EventListener.php 4653 2008-07-10 17:17:58Z romanb $
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
@ -20,18 +20,13 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Doctrine_EventListener all event listeners extend this base class
|
||||
* the empty methods allow child classes to only implement the methods they need to implement
|
||||
*
|
||||
* EventSubscriber.
|
||||
*
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @package Doctrine
|
||||
* @subpackage EventListener
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.phpdoctrine.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @todo Remove. The 2.0 event system has no listener interfaces.
|
||||
* @version $Revision: 4653 $
|
||||
*/
|
||||
interface Doctrine_EventSubscriber
|
||||
{
|
@ -31,7 +31,8 @@
|
||||
* @since 1.0
|
||||
* @version $Revision$
|
||||
* @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
|
||||
{
|
||||
|
@ -72,7 +72,7 @@ class Doctrine_Hydrator_RecordDriver
|
||||
public function initRelatedCollection(Doctrine_Entity $entity, $name)
|
||||
{
|
||||
if ( ! isset($this->_initializedRelations[$entity->getOid()][$name])) {
|
||||
$relation = $entity->getClassMetadata()->getRelation($name);
|
||||
$relation = $entity->getClass()->getRelation($name);
|
||||
$relatedClass = $relation->getTable();
|
||||
$coll = $this->getElementCollection($relatedClass->getClassName());
|
||||
$coll->setReference($entity, $relation);
|
||||
|
@ -519,16 +519,12 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
|
||||
* $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(Doctrine_ClassMetadata $class, $fieldName, $value, $typeHint = null)
|
||||
{
|
||||
@ -540,15 +536,17 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
|
||||
$type = is_null($typeHint) ? $class->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 'string':
|
||||
case 'enum':
|
||||
return $class->enumValue($fieldName, $value);
|
||||
break;
|
||||
case 'boolean':
|
||||
return (boolean) $value;
|
||||
// don't do any conversions on primitive types
|
||||
break;
|
||||
//case 'enum':
|
||||
// return $class->enumValue($fieldName, $value);
|
||||
//break;
|
||||
//case 'boolean':
|
||||
// return (boolean) $value;
|
||||
//break;
|
||||
case 'array':
|
||||
case 'object':
|
||||
if (is_string($value)) {
|
||||
|
110
lib/Doctrine/Internal/CommitOrderCalculator.php
Normal file
110
lib/Doctrine/Internal/CommitOrderCalculator.php
Normal file
@ -0,0 +1,110 @@
|
||||
<?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());
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
136
lib/Doctrine/Internal/CommitOrderNode.php
Normal file
136
lib/Doctrine/Internal/CommitOrderNode.php
Normal file
@ -0,0 +1,136 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -12,6 +12,22 @@ class Doctrine_MappingException extends Doctrine_Exception
|
||||
return new self("No identifier specified for Entity '$entityName'."
|
||||
. " 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.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -30,7 +30,7 @@
|
||||
* @since 1.0
|
||||
* @version $Revision$
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @todo Really needed?
|
||||
* @todo Really needed? Remove.
|
||||
*/
|
||||
interface Doctrine_Overloadable {
|
||||
/**
|
||||
|
@ -28,6 +28,7 @@
|
||||
* @since 1.0
|
||||
* @version $Revision$
|
||||
* @author Joe Simms <joe.simms@websites4.com>
|
||||
* @todo Move to NestedSet behavior.
|
||||
*/
|
||||
class Doctrine_Tree
|
||||
{
|
||||
|
@ -33,6 +33,7 @@
|
||||
* @version $Revision$
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @todo Move to validator package.
|
||||
*/
|
||||
class Doctrine_Validator
|
||||
{
|
||||
|
74
tests/Orm/Associations/CascadeTest.php
Normal file
74
tests/Orm/Associations/CascadeTest.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
36
tests/Orm/Associations/OneToOneMappingTest.php
Normal file
36
tests/Orm/Associations/OneToOneMappingTest.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
@ -106,17 +106,17 @@ class Orm_Component_CollectionTest extends Doctrine_OrmTestCase
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function shouldSetKeyColumnWhenAddingNewRowAsArray()
|
||||
/*public function shouldSetKeyColumnWhenAddingNewRowAsArray()
|
||||
{
|
||||
$this->assertTrue(isset($this->cmsColl['test']));
|
||||
$this->assertEquals($this->cmsUser, $this->cmsColl['test']);
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function shouldSerializeAndUnserializeCollectionWithData()
|
||||
/*public function shouldSerializeAndUnserializeCollectionWithData()
|
||||
{
|
||||
$serialized = serialize($this->cmsColl);
|
||||
$coll = unserialize($serialized);
|
||||
@ -126,6 +126,6 @@ class Orm_Component_CollectionTest extends Doctrine_OrmTestCase
|
||||
$user = $coll['test'];
|
||||
$this->assertTrue($user instanceOf CmsUser);
|
||||
$this->assertEquals('test', $user['username']);
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
|
@ -22,12 +22,21 @@ class Orm_Entity_AccessorTest extends Doctrine_OrmTestCase
|
||||
|
||||
class CustomAccessorMutatorTestEntity extends Doctrine_Entity
|
||||
{
|
||||
public static function initMetadata($class)
|
||||
public static function initMetadata($mapping)
|
||||
{
|
||||
$class->mapColumn('id', 'integer', 4, array('primary'));
|
||||
$class->mapColumn('username', 'string', 50, array(
|
||||
'accessor' => 'getUsernameCustom',
|
||||
'mutator' => 'setUsernameCustom'));
|
||||
$mapping->mapField(array(
|
||||
'fieldName' => 'id',
|
||||
'type' => 'integer',
|
||||
'length' => 4,
|
||||
'id' => true
|
||||
));
|
||||
$mapping->mapField(array(
|
||||
'fieldName' => 'username',
|
||||
'type' => 'string',
|
||||
'length' => 50,
|
||||
'accessor' => 'getUsernameCustom',
|
||||
'mutator' => 'setUsernameCustom'
|
||||
));
|
||||
}
|
||||
|
||||
public function getUsernameCustom()
|
||||
@ -43,10 +52,19 @@ class CustomAccessorMutatorTestEntity 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'));
|
||||
$class->mapColumn('username', 'string', 50, array());
|
||||
$mapping->mapField(array(
|
||||
'fieldName' => 'id',
|
||||
'type' => 'integer',
|
||||
'length' => 4,
|
||||
'id' => true
|
||||
));
|
||||
$mapping->mapField(array(
|
||||
'fieldName' => 'username',
|
||||
'type' => 'string',
|
||||
'length' => 50
|
||||
));
|
||||
}
|
||||
|
||||
public function getUsername()
|
||||
|
@ -22,10 +22,19 @@ class ConstructorTestEntity1 extends Doctrine_Entity
|
||||
}
|
||||
|
||||
/* The mapping definition */
|
||||
public static function initMetadata($class)
|
||||
public static function initMetadata($mapping)
|
||||
{
|
||||
$class->mapColumn('id', 'integer', 4, array('primary'));
|
||||
$class->mapColumn('username', 'string', 50, array());
|
||||
$mapping->mapField(array(
|
||||
'fieldName' => 'id',
|
||||
'type' => 'integer',
|
||||
'length' => 4,
|
||||
'id' => true
|
||||
));
|
||||
$mapping->mapField(array(
|
||||
'fieldName' => 'username',
|
||||
'type' => 'string',
|
||||
'length' => 50
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,59 +1,160 @@
|
||||
<?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
|
||||
{
|
||||
private $_unitOfWork;
|
||||
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() {
|
||||
parent::setUp();
|
||||
|
||||
$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() {
|
||||
$this->_user->free();
|
||||
}
|
||||
|
||||
/* Basic registration tests */
|
||||
|
||||
public function testRegisterNew()
|
||||
{
|
||||
$this->_user->username = 'romanb';
|
||||
$this->_user->id = 1;
|
||||
// registerNew() is normally called in save()/persist()
|
||||
$this->_unitOfWork->registerNew($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->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()
|
||||
{
|
||||
$this->_user->username = 'romanb';
|
||||
$this->_user->id = 1;
|
||||
$this->assertEquals(Doctrine_Entity::STATE_TDIRTY, $this->_user->_state());
|
||||
$this->assertFalse($this->_unitOfWork->contains($this->_user));
|
||||
$this->assertEquals(Doctrine_Entity::STATE_NEW, $this->_user->_state());
|
||||
$this->assertFalse($this->_unitOfWork->isInIdentityMap($this->_user));
|
||||
$this->_unitOfWork->registerDirty($this->_user);
|
||||
$this->assertTrue($this->_unitOfWork->isRegisteredDirty($this->_user));
|
||||
$this->assertFalse($this->_unitOfWork->isRegisteredNew($this->_user));
|
||||
$this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user));
|
||||
}
|
||||
|
||||
public function testRegisterRemovedOnTransientEntityIsIgnored()
|
||||
public function testRegisterRemovedOnNewEntityIsIgnored()
|
||||
{
|
||||
$this->_user->username = 'romanb';
|
||||
$this->_user->id = 1;
|
||||
$this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user));
|
||||
$this->_unitOfWork->registerRemoved($this->_user);
|
||||
$this->_unitOfWork->registerDeleted($this->_user);
|
||||
$this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user));
|
||||
}
|
||||
|
||||
/*public function testSavedEntityHasIdentityAndIsManaged()
|
||||
|
||||
/* Operational tests */
|
||||
|
||||
public function testSavingSingleEntityWithIdentityColumnForcesInsert()
|
||||
{
|
||||
$this->_user->username = 'romanb';
|
||||
$this->_user->save();
|
||||
$this->assertTrue($this->_unitOfWork->hasIdentity($this->_user));
|
||||
$this->assertTrue($this->_unitOfWork->isManaged($this->_user));
|
||||
}*/
|
||||
$this->assertEquals(Doctrine_Entity::STATE_NEW, $this->_user->_state());
|
||||
|
||||
$this->_unitOfWork->save($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()
|
||||
{
|
||||
//...
|
||||
}
|
||||
|
||||
}
|
20
tests/lib/mocks/Doctrine_ConnectionMock.php
Normal file
20
tests/lib/mocks/Doctrine_ConnectionMock.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
25
tests/lib/mocks/Doctrine_EntityManagerMock.php
Normal file
25
tests/lib/mocks/Doctrine_EntityManagerMock.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
56
tests/lib/mocks/Doctrine_EntityPersisterMock.php
Normal file
56
tests/lib/mocks/Doctrine_EntityPersisterMock.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
37
tests/lib/mocks/Doctrine_SequenceMock.php
Normal file
37
tests/lib/mocks/Doctrine_SequenceMock.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -1,13 +1,40 @@
|
||||
<?php
|
||||
|
||||
#namespace Doctrine::Tests::ORM::Models::CMS;
|
||||
|
||||
#use Doctrine::ORM::Entity;
|
||||
|
||||
class CmsArticle extends Doctrine_Entity
|
||||
{
|
||||
public static function initMetadata($class)
|
||||
{
|
||||
$class->mapColumn('id', 'integer', 4, array('primary' => true, 'autoincrement' => true));
|
||||
$class->mapColumn('topic', 'string', 255);
|
||||
$class->mapColumn('text', 'string');
|
||||
$class->mapColumn('user_id', 'integer', 4);
|
||||
$class->hasMany('CmsComment as comments', array(
|
||||
'local' => 'id', 'foreign' => 'article_id'));
|
||||
}
|
||||
#protected $id;
|
||||
#protected $topic;
|
||||
#protected $text;
|
||||
#protected $user_id;
|
||||
|
||||
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' => 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'));
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,38 @@
|
||||
<?php
|
||||
|
||||
#namespace Doctrine::Tests::ORM::Models::CMS;
|
||||
|
||||
#use Doctrine::ORM::Entity;
|
||||
|
||||
class CmsComment extends Doctrine_Entity
|
||||
{
|
||||
public static function initMetadata($class)
|
||||
{
|
||||
$class->mapColumn('id', 'integer', 4, array('primary' => true, 'autoincrement' => true));
|
||||
$class->mapColumn('topic', 'string', 255);
|
||||
$class->mapColumn('text', 'string');
|
||||
$class->mapColumn('article_id', 'integer', 4);
|
||||
}
|
||||
#protected $id;
|
||||
#protected $topic;
|
||||
#protected $text;
|
||||
#protected $article_id;
|
||||
|
||||
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' => 255
|
||||
));
|
||||
$mapping->mapField(array(
|
||||
'fieldName' => 'text',
|
||||
'type' => 'string'
|
||||
));
|
||||
$mapping->mapField(array(
|
||||
'fieldName' => 'article_id',
|
||||
'type' => 'integer',
|
||||
'length' => 4
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,21 @@
|
||||
<?php
|
||||
class CmsPhonenumber extends Doctrine_Entity
|
||||
{
|
||||
public static function initMetadata($class)
|
||||
{
|
||||
$class->mapColumn('user_id', 'integer', 4);
|
||||
$class->mapColumn('phonenumber', 'string', 50, array('primary' => true));
|
||||
}
|
||||
#protected $user_id;
|
||||
#protected $phonenumber;
|
||||
|
||||
public static function initMetadata($mapping)
|
||||
{
|
||||
$mapping->mapField(array(
|
||||
'fieldName' => 'user_id',
|
||||
'type' => 'integer',
|
||||
'length' => 4
|
||||
));
|
||||
$mapping->mapField(array(
|
||||
'fieldName' => 'phonenumber',
|
||||
'type' => 'string',
|
||||
'length' => 50,
|
||||
'id' => true
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,44 @@
|
||||
<?php
|
||||
|
||||
#namespace Doctrine::Test::ORM::Models;
|
||||
|
||||
#use Doctrine::ORM::Entity;
|
||||
|
||||
class CmsUser extends Doctrine_Entity
|
||||
{
|
||||
public static function initMetadata($class)
|
||||
{
|
||||
$class->mapColumn('id', 'integer', 4, array('primary' => true, 'autoincrement' => true));
|
||||
$class->mapColumn('status', 'string', 50);
|
||||
$class->mapColumn('username', 'string', 255);
|
||||
$class->mapColumn('name', 'string', 255);
|
||||
|
||||
$class->hasMany('CmsPhonenumber as phonenumbers', array(
|
||||
#protected $id;
|
||||
#protected $status;
|
||||
#protected $username;
|
||||
#protected $name;
|
||||
|
||||
public static function initMetadata($mapping)
|
||||
{
|
||||
$mapping->mapField(array(
|
||||
'fieldName' => 'id',
|
||||
'type' => 'integer',
|
||||
'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
|
||||
));
|
||||
|
||||
$mapping->hasMany('CmsPhonenumber as phonenumbers', array(
|
||||
'local' => 'id', 'foreign' => 'user_id'));
|
||||
$class->hasMany('CmsArticle as articles', array(
|
||||
$mapping->hasMany('CmsArticle as articles', array(
|
||||
'local' => 'id', 'foreign' => 'user_id'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,14 @@
|
||||
|
||||
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) {}
|
||||
|
@ -1,6 +1,8 @@
|
||||
<?php
|
||||
class ForumBoard extends Doctrine_Entity {
|
||||
public static function initMetadata($metadata) {
|
||||
class ForumBoard extends Doctrine_Entity
|
||||
{
|
||||
public static function initMetadata($mapping)
|
||||
{
|
||||
/*$metadata->mapField(array(
|
||||
'fieldName' => 'id',
|
||||
'id' => true,
|
||||
@ -8,10 +10,22 @@ class ForumBoard extends Doctrine_Entity {
|
||||
'length' => 4
|
||||
));
|
||||
*/
|
||||
$metadata->mapColumn('id', 'integer', 4, array('primary'));
|
||||
$metadata->mapColumn('position', 'integer');
|
||||
$metadata->mapColumn('category_id', 'integer');
|
||||
$metadata->hasOne('ForumCategory as category',
|
||||
$mapping->mapField(array(
|
||||
'fieldName' => 'id',
|
||||
'type' => 'integer',
|
||||
'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'));
|
||||
/*
|
||||
$metadata->mapOneToOne(array(
|
||||
|
@ -1,10 +1,26 @@
|
||||
<?php
|
||||
class ForumCategory extends Doctrine_Entity {
|
||||
public static function initMetadata($class) {
|
||||
$class->mapColumn('id', 'integer', 4, array('primary'));
|
||||
$class->mapColumn('position', 'integer');
|
||||
$class->mapColumn('name', 'string', 255);
|
||||
$class->hasMany('ForumBoard as boards', array(
|
||||
class ForumCategory extends Doctrine_Entity
|
||||
{
|
||||
|
||||
public static function initMetadata($mapping)
|
||||
{
|
||||
$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'));
|
||||
}
|
||||
}
|
||||
|
32
tests/models/forum/ForumEntry.php
Normal file
32
tests/models/forum/ForumEntry.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?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
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -1,26 +1,46 @@
|
||||
<?php
|
||||
|
||||
#namespace Doctrine::Tests::ORM::Models::Forum;
|
||||
|
||||
#use Doctrine::ORM::Entity;
|
||||
|
||||
class ForumUser extends Doctrine_Entity
|
||||
{
|
||||
public static function initMetadata($class)
|
||||
#protected $dtype;
|
||||
#protected $id;
|
||||
#protected $username;
|
||||
|
||||
public static function initMetadata($mapping)
|
||||
{
|
||||
// inheritance mapping
|
||||
$class->setInheritanceType(Doctrine::INHERITANCE_TYPE_JOINED, array(
|
||||
$mapping->setInheritanceType('joined', array(
|
||||
'discriminatorColumn' => 'dtype',
|
||||
'discriminatorMap' => array(
|
||||
'user' => 'ForumUser',
|
||||
'admin' => 'ForumAdministrator')
|
||||
));
|
||||
// register subclasses
|
||||
$class->setSubclasses(array('ForumAdministrator'));
|
||||
$mapping->setSubclasses(array('ForumAdministrator'));
|
||||
// the discriminator column
|
||||
$class->mapColumn('dtype', 'string', 50);
|
||||
$mapping->mapField(array(
|
||||
'fieldName' => 'dtype',
|
||||
'type' => 'string',
|
||||
'length' => 50
|
||||
));
|
||||
|
||||
// column-to-field mapping
|
||||
$class->mapColumn('id', 'integer', 4, array(
|
||||
'primary' => true,
|
||||
'autoincrement' => true));
|
||||
$class->mapColumn('username', 'string', 50, array());
|
||||
$mapping->mapField(array(
|
||||
'fieldName' => 'id',
|
||||
'type' => 'integer',
|
||||
'length' => 4,
|
||||
'id' => true,
|
||||
'generatorType' => 'auto'
|
||||
));
|
||||
$mapping->mapField(array(
|
||||
'fieldName' => 'username',
|
||||
'type' => 'string',
|
||||
'length' => 50
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user