Commit e202cb1c authored by romanb's avatar romanb

[2.0] Progress on UnitOfWork, persisters and basic functional tests.

parent 36763dad
......@@ -23,7 +23,6 @@ namespace Doctrine\DBAL;
use Doctrine\Common\EventManager;
use Doctrine\Common\DoctrineException;
#use Doctrine\DBAL\Exceptions\ConnectionException;
/**
* A wrapper around a Doctrine\DBAL\Driver\Connection that adds features like
......@@ -251,7 +250,6 @@ class Connection
/**
* Deletes table row(s) matching the specified identifier.
*
* @throws Doctrine_Connection_Exception if something went wrong at the database level
* @param string $table The table to delete data from
* @param array $identifier An associateve array containing identifier fieldname-value pairs.
* @return integer The number of affected rows
......@@ -289,12 +287,7 @@ class Connection
$set = array();
foreach ($data as $columnName => $value) {
if ($value instanceof Doctrine_Expression) {
$set[] = $this->quoteIdentifier($columnName) . ' = ' . $value->getSql();
unset($data[$columnName]);
} else {
$set[] = $this->quoteIdentifier($columnName) . ' = ?';
}
$set[] = $this->quoteIdentifier($columnName) . ' = ?';
}
$params = array_merge(array_values($data), array_values($identifier));
......@@ -328,12 +321,7 @@ class Connection
$a = array();
foreach ($data as $columnName => $value) {
$cols[] = $this->quoteIdentifier($columnName);
if ($value instanceof Doctrine_DBAL_Expression) {
$a[] = $value->getSql();
unset($data[$columnName]);
} else {
$a[] = '?';
}
$a[] = '?';
}
$query = 'INSERT INTO ' . $this->quoteIdentifier($tableName)
......@@ -495,13 +483,8 @@ class Connection
*/
public function prepare($statement)
{
echo $statement . PHP_EOL;
$this->connect();
try {
return $this->_conn->prepare($statement);
} catch (PDOException $e) {
$this->rethrowException($e, $this);
}
return $this->_conn->prepare($statement);
}
/**
......@@ -571,6 +554,7 @@ class Connection
return $count;
}
} catch (PDOException $e) {
//TODO: Wrap
throw $e;
}
}
......@@ -688,7 +672,7 @@ class Connection
{
$this->connect();
if ($this->_transactionNestingLevel == 0) {
return $this->_conn->beginTransaction();
$this->_conn->beginTransaction();
}
++$this->_transactionNestingLevel;
return true;
......@@ -710,7 +694,7 @@ class Connection
$this->connect();
if ($this->_transactionNestingLevel == 1) {
return $this->_conn->commit();
$this->_conn->commit();
}
--$this->_transactionNestingLevel;
......@@ -740,7 +724,7 @@ class Connection
if ($this->_transactionNestingLevel == 1) {
$this->_transactionNestingLevel = 0;
return $this->_conn->rollback();
$this->_conn->rollback();
}
--$this->_transactionNestingLevel;
......@@ -807,7 +791,7 @@ class Connection
protected function _getSequenceName($sqn)
{
return sprintf($this->conn->getAttribute(Doctrine::ATTR_SEQNAME_FORMAT),
preg_replace('/[^a-z0-9_\$.]/i', '_', $sqn));
preg_replace('/[^a-z0-9_\$.]/i', '_', $sqn));
}
/**
......@@ -819,7 +803,7 @@ class Connection
protected function _getIndexName($idx)
{
return sprintf($this->conn->getAttribute(Doctrine::ATTR_IDXNAME_FORMAT),
preg_replace('/[^a-z0-9_\$]/i', '_', $idx));
preg_replace('/[^a-z0-9_\$]/i', '_', $idx));
}
/**
......@@ -835,15 +819,6 @@ class Connection
return sprintf($this->conn->getAttribute(Doctrine::ATTR_TBLNAME_FORMAT),
$table);*/
}
/**
* returns a string representation of this object
* @return string
*/
public function __toString()
{
return Doctrine_Lib::getConnectionAsString($this);
}
/**
* Gets the wrapped driver connection.
......
<?php
#namespace Doctrine\DBAL\Driver\PDOMySql;
namespace Doctrine\DBAL\Driver\PDOMySql;
#use Doctrine\DBAL\Driver;
class Doctrine_DBAL_Driver_PDOMySql_Driver implements Doctrine_DBAL_Driver
class Driver implements \Doctrine\DBAL\Driver
{
/**
* Attempts to establish a connection with the underlying driver.
......@@ -17,12 +15,12 @@ class Doctrine_DBAL_Driver_PDOMySql_Driver implements Doctrine_DBAL_Driver
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = array())
{
$conn = new Doctrine_DBAL_Driver_PDOConnection(
$conn = new \Doctrine\DBAL\Driver\PDOConnection(
$this->_constructPdoDsn($params),
$username,
$password,
$driverOptions);
$conn->setAttribute(PDO::ATTR_AUTOCOMMIT, false);
$conn->setAttribute(\PDO::ATTR_AUTOCOMMIT, false);
return $conn;
}
......@@ -54,12 +52,12 @@ class Doctrine_DBAL_Driver_PDOMySql_Driver implements Doctrine_DBAL_Driver
public function getDatabasePlatform()
{
return new Doctrine_DBAL_Platforms_MySqlPlatform();
return new \Doctrine\DBAL\Platforms\MySqlPlatform();
}
public function getSchemaManager(Doctrine_DBAL_Connection $conn)
public function getSchemaManager(\Doctrine\DBAL\Connection $conn)
{
return new Doctrine_DBAL_Schema_MySqlSchemaManager($conn);
return new \Doctrine\DBAL\Schema\MySqlSchemaManager($conn);
}
}
......
......@@ -400,56 +400,42 @@ class SqlitePlatform extends AbstractPlatform
/** @override */
public function getIntegerTypeDeclarationSql(array $field)
{
return 'INT ' . $this->_getCommonIntegerTypeDeclarationSql($field);
return $this->_getCommonIntegerTypeDeclarationSql($field);
}
/** @override */
public function getBigIntTypeDeclarationSql(array $field)
{
return 'BIGINT ' . $this->_getCommonIntegerTypeDeclarationSql($field);
return $this->_getCommonIntegerTypeDeclarationSql($field);
}
/** @override */
public function getTinyIntTypeDeclarationSql(array $field)
{
return 'TINYINT ' . $this->_getCommonIntegerTypeDeclarationSql($field);
return $this->_getCommonIntegerTypeDeclarationSql($field);
}
/** @override */
public function getSmallIntTypeDeclarationSql(array $field)
{
return 'SMALLINT ' . $this->_getCommonIntegerTypeDeclarationSql($field);
return $this->_getCommonIntegerTypeDeclarationSql($field);
}
/** @override */
public function getMediumIntTypeDeclarationSql(array $field)
{
return 'MEDIUMINT ' . $this->_getCommonIntegerTypeDeclarationSql($field);
return $this->_getCommonIntegerTypeDeclarationSql($field);
}
/** @override */
protected function _getCommonIntegerTypeDeclarationSql(array $columnDef)
{
$default = $autoinc = '';
if ( ! empty($columnDef['autoincrement'])) {
$autoinc = ' AUTO_INCREMENT';
} else if (array_key_exists('default', $columnDef)) {
if ($field['default'] === '') {
$field['default'] = empty($columnDef['notnull']) ? null : 0;
}
if (is_null($columnDef['default'])) {
$default = ' DEFAULT NULL';
} else {
$default = ' DEFAULT ' . $this->quote($columnDef['default']);
}
} else if (empty($columnDef['notnull'])) {
$default = ' DEFAULT NULL';
}
$autoinc = ! empty($columnDef['autoincrement']) ? 'AUTOINCREMENT' : '';
$pk = ! empty($columnDef['primary']) ? 'PRIMARY KEY' : '';
$notnull = (isset($columnDef['notnull']) && $columnDef['notnull']) ? ' NOT NULL' : '';
$unsigned = (isset($columnDef['unsigned']) && $columnDef['unsigned']) ? ' UNSIGNED' : '';
//$unsigned = (isset($columnDef['unsigned']) && $columnDef['unsigned']) ? ' UNSIGNED' : '';
return $unsigned . $default . $notnull . $autoinc;
return "INTEGER $pk $autoinc";
}
/**
......@@ -494,14 +480,13 @@ class SqlitePlatform extends AbstractPlatform
$autoinc = false;
foreach($fields as $field) {
if (isset($field['autoincrement']) && $field['autoincrement'] ||
(isset($field['autoinc']) && $field['autoinc'])) {
if (isset($field['autoincrement']) && $field['autoincrement']) {
$autoinc = true;
break;
}
}
if (isset($options['primary']) && ! empty($options['primary'])) {
if ( ! $autoinc && isset($options['primary']) && ! empty($options['primary'])) {
$keyColumns = array_values($options['primary']);
$keyColumns = array_map(array($this, 'quoteIdentifier'), $keyColumns);
$queryFields.= ', PRIMARY KEY('.implode(', ', $keyColumns).')';
......
......@@ -269,7 +269,7 @@ abstract class AbstractSchemaManager
if ($count == 0) {
$options['primary'] = array();
}
$count++;
++$count;
$options['primary'][] = $columnName;
}
}
......
......@@ -589,4 +589,3 @@ class SqliteSchemaManager extends AbstractSchemaManager
}
}
?>
\ No newline at end of file
......@@ -21,12 +21,12 @@
namespace Doctrine\ORM;
#use Doctrine\Common\Configuration;
use Doctrine\Common\EventManager;
use Doctrine\Common\DoctrineException;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\Exceptions\EntityManagerException;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
/**
* The EntityManager is the central access point to ORM functionality.
......@@ -96,13 +96,6 @@ class EntityManager
*/
private $_metadataFactory;
/**
* The EntityPersister instances used to persist entity instances.
*
* @var array
*/
private $_persisters = array();
/**
* The EntityRepository instances.
*
......@@ -167,7 +160,7 @@ class EntityManager
$this->_name = $name;
$this->_config = $config;
$this->_eventManager = $eventManager;
$this->_metadataFactory = new \Doctrine\ORM\Mapping\ClassMetadataFactory(
$this->_metadataFactory = new ClassMetadataFactory(
$this->_config->getMetadataDriverImpl(),
$this->_conn->getDatabasePlatform());
$this->_metadataFactory->setCacheDriver($this->_config->getMetadataCacheImpl());
......@@ -285,28 +278,6 @@ class EntityManager
return $query;
}
/**
* Gets the EntityPersister for an Entity.
*
* This is usually not of interest for users, mainly for internal use.
*
* @param string $entityName The name of the Entity.
* @return Doctrine\ORM\Persister\AbstractEntityPersister
*/
public function getEntityPersister($entityName)
{
if ( ! isset($this->_persisters[$entityName])) {
$class = $this->getClassMetadata($entityName);
if ($class->isInheritanceTypeJoined()) {
$persister = new \Doctrine\ORM\Persisters\JoinedSubclassPersister($this, $class);
} else {
$persister = new \Doctrine\ORM\Persisters\StandardEntityPersister($this, $class);
}
$this->_persisters[$entityName] = $persister;
}
return $this->_persisters[$entityName];
}
/**
* Detaches an entity from the manager. It's lifecycle is no longer managed.
*
......
......@@ -69,6 +69,7 @@ class ClassExporter
$column['name'] = $mapping['columnName'];
$column['type'] = $mapping['type'];
$column['length'] = $mapping['length'];
$column['notnull'] = ! $mapping['nullable'];
if ($class->isIdentifier($fieldName)) {
$column['primary'] = true;
......
......@@ -2,6 +2,8 @@
namespace Doctrine\ORM\Id;
use Doctrine\Common\DoctrineException;
/**
* Special generator for application-assigned identifiers (doesnt really generate anything).
*
......@@ -20,6 +22,7 @@ class Assigned extends AbstractIdGenerator
public function generate($entity)
{
$class = $this->_em->getClassMetadata(get_class($entity));
$identifier = null;
if ($class->isIdentifierComposite()) {
$identifier = array();
$idFields = $class->getIdentifierFieldNames();
......@@ -39,7 +42,7 @@ class Assigned extends AbstractIdGenerator
}
if ( ! $identifier) {
throw new Doctrine_Exception("Entity '$entity' is missing an assigned ID.");
throw new DoctrineException("Entity of type '" . get_class($entity) . "' is missing an assigned ID.");
}
return $identifier;
......
......@@ -66,7 +66,6 @@ class ObjectHydrator extends AbstractHydrator
foreach ($this->_collections as $coll) {
$coll->_takeSnapshot();
$coll->_setHydrationFlag(false);
//$this->_uow->addManagedCollection($coll);
}
// Clean up
......@@ -116,7 +115,7 @@ class ObjectHydrator extends AbstractHydrator
private function getCollection($component)
{
$coll = new \Doctrine\ORM\Collection($this->_em, $component);
$coll = new \Doctrine\ORM\PersistentCollection($this->_em, $component);
$this->_collections[] = $coll;
return $coll;
}
......@@ -338,7 +337,7 @@ class ObjectHydrator extends AbstractHydrator
->getValue($baseElement));
}
} else if ( ! $this->isFieldSet($baseElement, $relationAlias)) {
$coll = new \Doctrine\ORM\Collection($this->_em, $entityName);
$coll = new \Doctrine\ORM\PersistentCollection($this->_em, $entityName);
$this->_collections[] = $coll;
$this->setRelatedElement($baseElement, $relationAlias, $coll);
}
......
......@@ -62,6 +62,7 @@ class AnnotationDriver
}
$mapping['type'] = $columnAnnot->type;
$mapping['length'] = $columnAnnot->length;
$mapping['nullable'] = $columnAnnot->nullable;
if ($idAnnot = $property->getAnnotation('DoctrineId')) {
$mapping['id'] = true;
}
......
......@@ -32,8 +32,8 @@ final class DoctrineJoinColumn extends \Addendum\Annotation {
final class DoctrineColumn extends \Addendum\Annotation {
public $type;
public $length;
public $unique;
public $nullable;
public $unique = false;
public $nullable = false;
}
final class DoctrineOneToOne extends \Addendum\Annotation {
public $targetEntity;
......
......@@ -128,12 +128,10 @@ class OneToOneMapping extends AssociationMapping
$sourceClass = $entityManager->getClassMetadata($this->_sourceEntityName);
$targetClass = $entityManager->getClassMetadata($this->_targetEntityName);
$dql = 'SELECT t.* FROM ' . $targetClass->getClassName() . ' t WHERE ';
$dql = 'SELECT t FROM ' . $targetClass->getClassName() . ' t WHERE ';
$params = array();
foreach ($this->_sourceToTargetKeyFields as $sourceKeyField => $targetKeyField) {
if ($params) {
$dql .= " AND ";
}
if ($params) $dql .= " AND ";
$dql .= "t.$targetKeyField = ?";
$params[] = $sourceClass->getReflectionProperty($sourceKeyField)->getValue($entity);
}
......
......@@ -42,9 +42,8 @@ use Doctrine\ORM\Mapping\AssociationMapping;
* @version $Revision: 4930 $
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
* @todo Rename to PersistentCollection
*/
final class Collection extends \Doctrine\Common\Collections\Collection
final class PersistentCollection extends \Doctrine\Common\Collections\Collection
{
/**
* The base type of the collection.
......@@ -111,6 +110,8 @@ final class Collection extends \Doctrine\Common\Collections\Collection
*/
private $_ownerClass;
private $_isDirty = false;
/**
* Creates a new persistent collection.
*/
......@@ -122,7 +123,7 @@ final class Collection extends \Doctrine\Common\Collections\Collection
$this->_ownerClass = $em->getClassMetadata($entityBaseType);
if ($keyField !== null) {
if ( ! $this->_ownerClass->hasField($keyField)) {
throw new Doctrine_Exception("Invalid field '$keyField' can't be uses as key.");
throw new DoctrineException("Invalid field '$keyField' can't be used as key.");
}
$this->_keyField = $keyField;
}
......@@ -155,21 +156,21 @@ final class Collection extends \Doctrine\Common\Collections\Collection
* Sets the collection owner. Used (only?) during hydration.
*
* @param object $entity
* @param AssociationMapping $relation
* @param AssociationMapping $assoc
*/
public function _setOwner($entity, AssociationMapping $relation)
public function _setOwner($entity, AssociationMapping $assoc)
{
$this->_owner = $entity;
$this->_association = $relation;
if ($relation->isInverseSide()) {
$this->_association = $assoc;
if ($assoc->isInverseSide()) {
// for sure bidirectional
$this->_backRefFieldName = $relation->getMappedByFieldName();
$this->_backRefFieldName = $assoc->getMappedByFieldName();
} else {
$targetClass = $this->_em->getClassMetadata($relation->getTargetEntityName());
if ($targetClass->hasInverseAssociationMapping($relation->getSourceFieldName())) {
$targetClass = $this->_em->getClassMetadata($assoc->getTargetEntityName());
if ($targetClass->hasInverseAssociationMapping($assoc->getSourceFieldName())) {
// bidirectional
$this->_backRefFieldName = $targetClass->getInverseAssociationMapping(
$relation->getSourceFieldName())->getSourceFieldName();
$assoc->getSourceFieldName())->getSourceFieldName();
}
}
}
......@@ -327,7 +328,7 @@ final class Collection extends \Doctrine\Common\Collections\Collection
*
* @return integer
*/
protected function _compareRecords($a, $b)
private function _compareRecords($a, $b)
{
if ($a === $b) {
return 0;
......@@ -342,7 +343,7 @@ final class Collection extends \Doctrine\Common\Collections\Collection
*/
public function getMapping()
{
return $this->relation;
return $this->_association;
}
/**
......@@ -362,10 +363,21 @@ final class Collection extends \Doctrine\Common\Collections\Collection
private function _changed()
{
if ( ! $this->_em->getUnitOfWork()->isCollectionScheduledForUpdate($this)) {
$this->_isDirty = true;
/*if ( ! $this->_em->getUnitOfWork()->isCollectionScheduledForUpdate($this)) {
//var_dump(get_class($this->_snapshot[0]));
//echo "NOT!";
//$this->_em->getUnitOfWork()->scheduleCollectionUpdate($this);
}
}*/
}
public function isDirty()
{
return $this->_isDirty;
}
public function setDirty($dirty)
{
$this->_isDirty = $dirty;
}
}
......@@ -2,12 +2,21 @@
namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\Collection;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\PersistentCollection;
class AbstractCollectionPersister
abstract class AbstractCollectionPersister
{
public function recreate(Doctrine_Collection $coll)
protected $_em;
protected $_conn;
public function __construct(EntityManager $em)
{
$this->_em = $em;
$this->_conn = $em->getConnection();
}
public function recreate(PersistentCollection $coll)
{
if ($coll->getRelation()->isInverseSide()) {
return;
......@@ -15,44 +24,75 @@ class AbstractCollectionPersister
//...
}
public function delete(Doctrine_Collection $coll)
public function delete(PersistentCollection $coll)
{
if ($coll->getRelation()->isInverseSide()) {
return;
}
//...
}
public function update(PersistentCollection $coll)
{
$this->deleteRows($coll);
$this->updateRows($coll);
$this->insertRows($coll);
}
/* collection update actions */
public function deleteRows(Collection $coll)
public function deleteRows(PersistentCollection $coll)
{
//$collection->getDeleteDiff();
if ($coll->getMapping()->isInverseSide()) {
return; // ignore inverse side
}
$deleteDiff = $coll->getDeleteDiff();
$sql = $this->_getDeleteRowSql($coll);
$uow = $this->_em->getUnitOfWork();
foreach ($deleteDiff as $element) {
$this->_conn->exec($sql, $uow->getEntityIdentifier($element));
}
}
public function updateRows(Collection $coll)
public function updateRows(PersistentCollection $coll)
{
}
public function insertRows(Collection $coll)
public function insertRows(PersistentCollection $coll)
{
//$collection->getInsertDiff();
}
if ($coll->getMapping()->isInverseSide()) {
return; // ignore inverse side
}
protected function _getDeleteRowSql()
{
$insertDiff = $coll->getInsertDiff();
$sql = $this->_getInsertRowSql($coll);
$uow = $this->_em->getUnitOfWork();
foreach ($insertDiff as $element) {
$this->_conn->exec($sql/*, $uow->getEntityIdentifier($element)*/);
}
}
protected function _getUpdateRowSql()
{
}
/**
* Gets the SQL statement used for deleting a row from the collection.
*
* @param PersistentCollection $coll
*/
abstract protected function _getDeleteRowSql(PersistentCollection $coll);
protected function _getDeleteRowSql()
{
}
/**
* Gets the SQL statement used for updating a row in the collection.
*
* @param PersistentCollection $coll
*/
abstract protected function _getUpdateRowSql();
/**
* Gets the SQL statement used for inserting a row from to the collection.
*
* @param PersistentCollection $coll
*/
abstract protected function _getInsertRowSql();
}
......@@ -172,7 +172,8 @@ abstract class AbstractEntityPersister
{
foreach ($this->_em->getUnitOfWork()->getEntityChangeSet($entity) as $field => $change) {
if (is_array($change)) {
list ($oldVal, $newVal) = each($change);
$oldVal = $change[0];
$newVal = $change[1];
} else {
$oldVal = null;
$newVal = $change;
......@@ -182,20 +183,21 @@ abstract class AbstractEntityPersister
$columnName = $this->_classMetadata->getColumnName($field);
if ($this->_classMetadata->hasAssociation($field)) {
if ($newVal !== null) {
$assocMapping = $this->_classMetadata->getAssociationMapping($field);
if ( ! $assocMapping->isOneToOne() || $assocMapping->isInverseSide()) {
//echo "NOT TO-ONE OR INVERSE!";
continue;
}
foreach ($assocMapping->getSourceToTargetKeyColumns() as $sourceColumn => $targetColumn) {
//TODO: throw exc if field not set
$otherClass = $this->_em->getClassMetadata($assocMapping->getTargetEntityName());
$assocMapping = $this->_classMetadata->getAssociationMapping($field);
if ( ! $assocMapping->isOneToOne() || $assocMapping->isInverseSide()) {
//echo "NOT TO-ONE OR INVERSE!";
continue;
}
foreach ($assocMapping->getSourceToTargetKeyColumns() as $sourceColumn => $targetColumn) {
//TODO: throw exc if field not set
$otherClass = $this->_em->getClassMetadata($assocMapping->getTargetEntityName());
if (is_null($newVal)) {
$result[$sourceColumn] = null;
} else {
$result[$sourceColumn] = $otherClass->getReflectionProperty(
$otherClass->getFieldName($targetColumn))->getValue($newVal);
$otherClass->getFieldName($targetColumn))->getValue($newVal);
}
} else if ( ! $isInsert) {
echo "NO INSERT AND NEWVAL NULL ON 1-1 ASSOC, OWNING SIDE";
}
} else if (is_null($newVal)) {
$result[$columnName] = null;
......
......@@ -19,6 +19,8 @@
* <http://www.phpdoctrine.org>.
*/
namespace Doctrine\ORM\Persisters;
/**
* The joined subclass persister maps a single entity instance to several tables in the
* database as it is defined by <tt>Class Table Inheritance</tt>.
......@@ -26,19 +28,19 @@
* @author Roman Borschel <roman@code-factory.org>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @version $Revision$
* @link www.phpdoctrine.org
* @link www.doctrine-project.org
* @since 2.0
*/
class Doctrine_ORM_Persisters_JoinedSubclassPersister extends Doctrine_ORM_Persisters_AbstractEntityPersister
class JoinedSubclassPersister extends AbstractEntityPersister
{
/**
* Inserts an entity that is part of a Class Table Inheritance hierarchy.
*
* @param Doctrine_Entity $record record to be inserted
* @param object $record record to be inserted
* @return boolean
* @override
*/
public function insert(Doctrine_ORM_Entity $entity)
public function insert($entity)
{
$class = $entity->getClass();
......@@ -88,7 +90,7 @@ class Doctrine_ORM_Persisters_JoinedSubclassPersister extends Doctrine_ORM_Persi
* @param Doctrine_Entity $record record to be updated
* @return boolean whether or not the update was successful
*/
protected function _doUpdate(Doctrine_ORM_Entity $record)
protected function _doUpdate($entity)
{
$conn = $this->_conn;
$classMetadata = $this->_classMetadata;
......
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
namespace Doctrine\ORM\Persisters;
/**
* Persister for many-to-many collections.
*
* @author robo
*/
class ManyToManyPersister extends AbstractCollectionPersister
{
/**
*
* @param <type> $coll
* @override
*/
protected function _getDeleteRowSql(PersistentCollection $coll)
{
}
}
<?php
class Doctrine_ORM_Persisters_OneToManyPersister extends Doctrine_ORM_Persisters_AbstractCollectionPersister
namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\PersistentCollection;
/**
* Persister for one-to-many collections.
*/
class OneToManyPersister extends AbstractCollectionPersister
{
protected function _getDeleteRowSql(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
$targetClass = $this->_em->getClassMetadata($mapping->getTargetEntityName());
$table = $targetClass->getTableName();
$ownerMapping = $targetClass->getAssociationMapping($mapping->getMappedByFieldName());
$setClause = '';
foreach ($ownerMapping->getSourceToTargetKeyColumns() as $sourceCol => $targetCol) {
if ($setClause != '') $setClause .= ', ';
$setClause .= "$sourceCol = NULL";
}
$whereClause = '';
foreach ($targetClass->getIdentifierColumnNames() as $idColumn) {
if ($whereClause != '') $whereClause .= ' AND ';
$whereClause .= "$idColumn = ?";
}
return "UPDATE $table SET $setClause WHERE $whereClause";
}
protected function _getInsertRowSql()
{
return "UPDATE xxx SET foreign_key = yyy WHERE foreign_key = zzz";
}
/* Not used for OneToManyPersister */
protected function _getUpdateRowSql()
{
return;
}
}
?>
\ No newline at end of file
......@@ -508,14 +508,38 @@ class Query extends AbstractQuery
/**
* Gets the list of results for the query.
*
* Alias for execute(array(), $hydrationMode).
* Alias for execute(array(), HYDRATE_OBJECT).
*
* @param integer $hydrationMode
* @return mixed
* @return Collection
*/
public function getResultList()
{
return $this->execute(array(), self::HYDRATE_OBJECT);
}
/**
* Gets the array of results for the query.
* Object graphs are represented as nested array structures.
*
* Alias for execute(array(), HYDRATE_ARRAY).
*
* @return array
*/
public function getResultList($hydrationMode = null)
public function getResultArray()
{
return $this->execute(array(), $hydrationMode);
return $this->execute(array(), self::HYDRATE_ARRAY);
}
/**
* Gets the scalar results for the query.
*
* Alias for execute(array(), HYDRATE_SCALAR).
*
* @return array
*/
public function getScalarResult()
{
return $this->execute(array(), self::HYDRATE_SCALAR);
}
/**
......@@ -532,8 +556,7 @@ class Query extends AbstractQuery
$result = $this->execute(array(), $hydrationMode);
if (count($result) > 1) {
throw QueryException::nonUniqueResult();
}
}
return is_array($result) ? array_shift($result) : $result->getFirst();
}
......
This diff is collapsed.
......@@ -7,18 +7,8 @@ namespace Doctrine\Tests\Mocks;
*/
class EntityManagerMock extends \Doctrine\ORM\EntityManager
{
private $_persisterMock;
private $_uowMock;
private $_idGenerators = array();
/**
* @override
*/
public function getEntityPersister($entityName)
{
return isset($this->_persisterMock[$entityName]) ?
$this->_persisterMock[$entityName] : parent::getEntityPersister($entityName);
}
/**
* @override
......@@ -39,18 +29,6 @@ class EntityManagerMock extends \Doctrine\ORM\EntityManager
{
$this->_uowMock = $uow;
}
/**
* Sets a (mock) persister for an entity class that will be returned when
* getEntityPersister() is invoked for that class.
*
* @param <type> $entityName
* @param <type> $persister
*/
public function setEntityPersister($entityName, $persister)
{
$this->_persisterMock[$entityName] = $persister;
}
/**
* Mock factory method to create an EntityManager.
......
......@@ -9,6 +9,16 @@ namespace Doctrine\Tests\Mocks;
*/
class UnitOfWorkMock extends \Doctrine\ORM\UnitOfWork {
private $_mockDataChangeSets = array();
private $_persisterMock;
/**
* @override
*/
public function getEntityPersister($entityName)
{
return isset($this->_persisterMock[$entityName]) ?
$this->_persisterMock[$entityName] : parent::getEntityPersister($entityName);
}
/**
* @param <type> $entity
......@@ -22,6 +32,18 @@ class UnitOfWorkMock extends \Doctrine\ORM\UnitOfWork {
/* MOCK API */
/**
* Sets a (mock) persister for an entity class that will be returned when
* getEntityPersister() is invoked for that class.
*
* @param <type> $entityName
* @param <type> $persister
*/
public function setEntityPersister($entityName, $persister)
{
$this->_persisterMock[$entityName] = $persister;
}
public function setDataChangeSet($entity, array $mockChangeSet) {
$this->_mockDataChangeSets[spl_object_hash($entity)] = $mockChangeSet;
}
......
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
namespace Doctrine\Tests\Models\CMS;
/**
* Description of CmsAddress
*
* @author robo
* @DoctrineEntity(tableName="cms_addresses")
*/
class CmsAddress
{
/**
* @DoctrineColumn(type="integer")
* @DoctrineId
* @DoctrineIdGenerator("auto")
*/
public $id;
/**
* @DoctrineColumn(type="varchar", length=50)
*/
public $country;
/**
* @DoctrineColumn(type="varchar", length=50)
*/
public $zip;
/**
* @DoctrineColumn(type="varchar", length=50)
*/
public $city;
/**
* @DoctrineOneToOne(
targetEntity="Doctrine\Tests\Models\CMS\CmsUser",
joinColumns={"user_id" = "id"})
*/
public $user;
}
......@@ -34,11 +34,16 @@ class CmsUser
* @DoctrineOneToMany(targetEntity="Doctrine\Tests\Models\CMS\CmsArticle", mappedBy="user")
*/
public $articles;
/**
* @DoctrineOneToOne(targetEntity="Doctrine\Tests\Models\CMS\CmsAddress", mappedBy="user",
cascade={"save"})
*/
public $address;
/**
* Adds a phonenumber to the user.
*
* @param <type> $phone
* @param CmsPhonenumber $phone
*/
public function addPhonenumber(CmsPhonenumber $phone) {
$this->phonenumbers[] = $phone;
......
......@@ -50,8 +50,8 @@ class EntityPersisterTest extends \Doctrine\Tests\OrmTestCase
$user->avatar = new ForumAvatar();
$this->_uowMock->setDataChangeSet($user, array(
'username' => array('' => 'romanb'),
'avatar' => array('' => $user->avatar)));
'username' => array('', 'romanb'),
'avatar' => array('', $user->avatar)));
//insert
......
......@@ -5,6 +5,9 @@ namespace Doctrine\Tests\ORM\Functional;
use Doctrine\ORM\Export\ClassExporter;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
use Doctrine\Tests\Models\CMS\CmsAddress;
use Doctrine\Tests\Models\Forum\ForumUser;
use Doctrine\Tests\Models\Forum\ForumAvatar;
require_once __DIR__ . '/../../TestInit.php';
......@@ -15,18 +18,21 @@ require_once __DIR__ . '/../../TestInit.php';
*/
class BasicCRUDTest extends \Doctrine\Tests\OrmFunctionalTestCase {
public function testSingleEntityCRUD() {
public function testBasicUnitsOfWorkWithOneToManyAssociation() {
$em = $this->_em;
$exporter = new ClassExporter($this->_em);
$exporter->exportClasses(array(
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'),
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber')
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber'),
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress')
));
// Create
$user = new CmsUser;
$user->name = 'romanb';
$user->name = 'Roman';
$user->username = 'romanb';
$user->status = 'developer';
$em->save($user);
$this->assertTrue(is_numeric($user->id));
$this->assertTrue($em->contains($user));
......@@ -42,7 +48,7 @@ class BasicCRUDTest extends \Doctrine\Tests\OrmFunctionalTestCase {
$em->flush();
$this->assertTrue($em->contains($ph));
$this->assertTrue($em->contains($user));
$this->assertTrue($user->phonenumbers instanceof \Doctrine\ORM\Collection);
$this->assertTrue($user->phonenumbers instanceof \Doctrine\ORM\PersistentCollection);
// Update name
$user->name = 'guilherme';
......@@ -67,14 +73,57 @@ class BasicCRUDTest extends \Doctrine\Tests\OrmFunctionalTestCase {
$this->assertFalse($em->getUnitOfWork()->isRegisteredRemoved($ph2));
}
/*public function testMore() {
public function testOneToManyAssociationModification() {
$user = new CmsUser;
$user->name = 'Roman';
$user->username = 'romanb';
$user->status = 'developer';
$ph = new CmsPhonenumber;
$ph->phonenumber = 123456;
$ph1 = new CmsPhonenumber;
$ph1->phonenumber = "0301234";
$ph2 = new CmsPhonenumber;
$ph2->phonenumber = "987654321";
$user->addPhonenumber($ph1);
$user->addPhonenumber($ph2);
$this->_em->save($user);
$this->_em->flush();
$this->assertTrue($user->phonenumbers instanceof \Doctrine\ORM\PersistentCollection);
// Remove the first element from the collection
unset($user->phonenumbers[0]);
$ph1->user = null; // owning side!
$this->_em->save($ph);
$this->_em->flush();
$this->assertEquals(1, count($user->phonenumbers));
$this->assertNull($ph1->user);
}
public function testBasicOneToOne()
{
$user = new CmsUser;
$user->name = 'Roman';
$user->username = 'romanb';
$user->status = 'developer';
$address = new CmsAddress;
$address->country = 'Germany';
$address->city = 'Berlin';
$address->zip = '12345';
$user->address = $address; // inverse side
$address->user = $user; // owning side!
$this->_em->save($user);
$this->_em->flush();
}*/
// Check that the foreign key has been set
$userId = $this->_em->getConnection()->execute("SELECT user_id FROM cms_addresses WHERE id=?",
array($address->id))->fetchColumn();
$this->assertTrue(is_numeric($userId));
}
}
......@@ -128,11 +128,11 @@ class ObjectHydratorTest extends HydrationTest
$this->assertTrue(is_array($result[1]));
$this->assertTrue($result[0][0] instanceof \Doctrine\Tests\Models\CMS\CmsUser);
$this->assertTrue($result[0][0]->phonenumbers instanceof \Doctrine\ORM\Collection);
$this->assertTrue($result[0][0]->phonenumbers instanceof \Doctrine\ORM\PersistentCollection);
$this->assertTrue($result[0][0]->phonenumbers[0] instanceof \Doctrine\Tests\Models\CMS\CmsPhonenumber);
$this->assertTrue($result[0][0]->phonenumbers[1] instanceof \Doctrine\Tests\Models\CMS\CmsPhonenumber);
$this->assertTrue($result[1][0] instanceof \Doctrine\Tests\Models\CMS\CmsUser);
$this->assertTrue($result[1][0]->phonenumbers instanceof \Doctrine\ORM\Collection);
$this->assertTrue($result[1][0]->phonenumbers instanceof \Doctrine\ORM\PersistentCollection);
// first user => 2 phonenumbers
$this->assertEquals(2, count($result[0][0]->phonenumbers));
......@@ -284,7 +284,7 @@ class ObjectHydratorTest extends HydrationTest
$this->assertTrue($result[0]['1'] instanceof \Doctrine\Tests\Models\CMS\CmsUser);
$this->assertTrue($result[1]['2'] instanceof \Doctrine\Tests\Models\CMS\CmsUser);
$this->assertTrue($result[0]['1']->phonenumbers instanceof \Doctrine\ORM\Collection);
$this->assertTrue($result[0]['1']->phonenumbers instanceof \Doctrine\ORM\PersistentCollection);
// first user => 2 phonenumbers. notice the custom indexing by user id
$this->assertEquals(2, count($result[0]['1']->phonenumbers));
// second user => 1 phonenumber. notice the custom indexing by user id
......@@ -403,14 +403,14 @@ class ObjectHydratorTest extends HydrationTest
$this->assertTrue(is_array($result[1]));
$this->assertTrue($result[0][0] instanceof \Doctrine\Tests\Models\CMS\CmsUser);
$this->assertTrue($result[0][0]->phonenumbers instanceof \Doctrine\ORM\Collection);
$this->assertTrue($result[0][0]->phonenumbers instanceof \Doctrine\ORM\PersistentCollection);
$this->assertTrue($result[0][0]->phonenumbers[0] instanceof \Doctrine\Tests\Models\CMS\CmsPhonenumber);
$this->assertTrue($result[0][0]->phonenumbers[1] instanceof \Doctrine\Tests\Models\CMS\CmsPhonenumber);
$this->assertTrue($result[0][0]->articles instanceof \Doctrine\ORM\Collection);
$this->assertTrue($result[0][0]->articles instanceof \Doctrine\ORM\PersistentCollection);
$this->assertTrue($result[0][0]->articles[0] instanceof \Doctrine\Tests\Models\CMS\CmsArticle);
$this->assertTrue($result[0][0]->articles[1] instanceof \Doctrine\Tests\Models\CMS\CmsArticle);
$this->assertTrue($result[1][0] instanceof \Doctrine\Tests\Models\CMS\CmsUser);
$this->assertTrue($result[1][0]->phonenumbers instanceof \Doctrine\ORM\Collection);
$this->assertTrue($result[1][0]->phonenumbers instanceof \Doctrine\ORM\PersistentCollection);
$this->assertTrue($result[1][0]->phonenumbers[0] instanceof \Doctrine\Tests\Models\CMS\CmsPhonenumber);
$this->assertTrue($result[1][0]->articles[0] instanceof \Doctrine\Tests\Models\CMS\CmsArticle);
$this->assertTrue($result[1][0]->articles[1] instanceof \Doctrine\Tests\Models\CMS\CmsArticle);
......@@ -549,26 +549,26 @@ class ObjectHydratorTest extends HydrationTest
$this->assertTrue($result[0][0] instanceof \Doctrine\Tests\Models\CMS\CmsUser);
$this->assertTrue($result[1][0] instanceof \Doctrine\Tests\Models\CMS\CmsUser);
// phonenumbers
$this->assertTrue($result[0][0]->phonenumbers instanceof \Doctrine\ORM\Collection);
$this->assertTrue($result[0][0]->phonenumbers instanceof \Doctrine\ORM\PersistentCollection);
$this->assertTrue($result[0][0]->phonenumbers[0] instanceof \Doctrine\Tests\Models\CMS\CmsPhonenumber);
$this->assertTrue($result[0][0]->phonenumbers[1] instanceof \Doctrine\Tests\Models\CMS\CmsPhonenumber);
$this->assertTrue($result[1][0]->phonenumbers instanceof \Doctrine\ORM\Collection);
$this->assertTrue($result[1][0]->phonenumbers instanceof \Doctrine\ORM\PersistentCollection);
$this->assertTrue($result[1][0]->phonenumbers[0] instanceof \Doctrine\Tests\Models\CMS\CmsPhonenumber);
// articles
$this->assertTrue($result[0][0]->articles instanceof \Doctrine\ORM\Collection);
$this->assertTrue($result[0][0]->articles instanceof \Doctrine\ORM\PersistentCollection);
$this->assertTrue($result[0][0]->articles[0] instanceof \Doctrine\Tests\Models\CMS\CmsArticle);
$this->assertTrue($result[0][0]->articles[1] instanceof \Doctrine\Tests\Models\CMS\CmsArticle);
$this->assertTrue($result[1][0]->articles[0] instanceof \Doctrine\Tests\Models\CMS\CmsArticle);
$this->assertTrue($result[1][0]->articles[1] instanceof \Doctrine\Tests\Models\CMS\CmsArticle);
// article comments
$this->assertTrue($result[0][0]->articles[0]->comments instanceof \Doctrine\ORM\Collection);
$this->assertTrue($result[0][0]->articles[0]->comments instanceof \Doctrine\ORM\PersistentCollection);
$this->assertTrue($result[0][0]->articles[0]->comments[0] instanceof \Doctrine\Tests\Models\CMS\CmsComment);
// empty comment collections
$this->assertTrue($result[0][0]->articles[1]->comments instanceof \Doctrine\ORM\Collection);
$this->assertTrue($result[0][0]->articles[1]->comments instanceof \Doctrine\ORM\PersistentCollection);
$this->assertEquals(0, count($result[0][0]->articles[1]->comments));
$this->assertTrue($result[1][0]->articles[0]->comments instanceof \Doctrine\ORM\Collection);
$this->assertTrue($result[1][0]->articles[0]->comments instanceof \Doctrine\ORM\PersistentCollection);
$this->assertEquals(0, count($result[1][0]->articles[0]->comments));
$this->assertTrue($result[1][0]->articles[1]->comments instanceof \Doctrine\ORM\Collection);
$this->assertTrue($result[1][0]->articles[1]->comments instanceof \Doctrine\ORM\PersistentCollection);
$this->assertEquals(0, count($result[1][0]->articles[1]->comments));
}
......
......@@ -58,7 +58,7 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase
{
// Setup fake persister and id generator for identity generation
$userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\Models\Forum\ForumUser"));
$this->_emMock->setEntityPersister('Doctrine\Tests\Models\Forum\ForumUser', $userPersister);
$this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\Forum\ForumUser', $userPersister);
$idGeneratorMock = new IdentityIdGeneratorMock($this->_emMock);
$this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumUser', $idGeneratorMock);
$userPersister->setMockIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_IDENTITY);
......@@ -99,13 +99,13 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase
// Setup fake persister and id generator for identity generation
//ForumUser
$userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\Models\Forum\ForumUser"));
$this->_emMock->setEntityPersister('Doctrine\Tests\Models\Forum\ForumUser', $userPersister);
$this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\Forum\ForumUser', $userPersister);
$userIdGeneratorMock = new IdentityIdGeneratorMock($this->_emMock);
$this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumUser', $userIdGeneratorMock);
$userPersister->setMockIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_IDENTITY);
// ForumAvatar
$avatarPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\Models\Forum\ForumAvatar"));
$this->_emMock->setEntityPersister('Doctrine\Tests\Models\Forum\ForumAvatar', $avatarPersister);
$this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\Forum\ForumAvatar', $avatarPersister);
$avatarIdGeneratorMock = new IdentityIdGeneratorMock($this->_emMock);
$this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumAvatar', $avatarIdGeneratorMock);
$avatarPersister->setMockIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_IDENTITY);
......@@ -129,7 +129,7 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals(0, count($avatarPersister->getDeletes()));
}
public function testComputeEntityChangeSets()
/*public function testComputeEntityChangeSets()
{
// We need an ID generator for ForumAvatar, because we attach a NEW ForumAvatar
// to a (faked) MANAGED instance. During changeset computation this will result
......@@ -176,7 +176,7 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase
$this->assertTrue(isset($user2ChangeSet['username']));
$this->assertEquals(array('jon' => 'jwage'), $user2ChangeSet['username']);
}
*/
/*
public function testSavingSingleEntityWithSequenceIdGeneratorSchedulesInsert()
{
......
......@@ -111,7 +111,7 @@ class OrmFunctionalTestCase extends OrmTestCase
protected function setUp()
{
if ( ! isset($this->sharedFixture['conn'])) {
echo " --- CREATE CONNECTION ----";
echo PHP_EOL . " --- CREATE CONNECTION ----" . PHP_EOL;
$this->sharedFixture['conn'] = TestUtil::getConnection();
}
if ( ! $this->_em) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment