Commit 2eb4a16d authored by romanb's avatar romanb

[2.0] More progress on the UnitOfWork and collections. First basic functional many-many test.

parent deb095f2
<?php
#namespace Doctrine\Common;
#use \ArrayAccess;
/**
* Base class for classes that use the virtual property system.
*
* @author robo
*/
class Doctrine_Common_VirtualPropertyObject implements ArrayAccess
{
protected $_data = array();
protected $_entityName;
/**
* Initializes a new instance of a class derived from VirtualPropertyObject.
*/
public function __construct()
{
$this->_entityName = get_class($this);
if ( ! Doctrine_Common_VirtualPropertySystem::isInitialized($this->_entityName)) {
Doctrine_Common_VirtualPropertySystem::initialize($this->_entityName);
}
}
/**
* Generic getter for virtual properties.
*
* @param string $fieldName Name of the field.
* @return mixed
*/
final public function get($fieldName)
{
if ( ! Doctrine_Common_VirtualPropertySystem::hasProperty($this->_entityName, $fieldName)) {
throw new Doctrine_Exception("Access of undefined property '$fieldName'.");
}
$getter = $this->_getCustomAccessor($fieldName);
if ($getter) {
return $this->$getter();
}
return $this->_get($fieldName);
}
/**
* Generic setter for virtual properties.
*
* @param string $name The name of the field to set.
* @param mixed $value The value of the field.
*/
final public function set($fieldName, $value)
{
if ( ! Doctrine_Common_VirtualPropertySystem::hasProperty($this->_entityName, $fieldName)) {
throw new Doctrine_Exception("Access of undefined property '$fieldName'.");
}
if (Doctrine_Common_VirtualPropertySystem::isTypeCheckEnabled()) {
$this->_checkType($fieldName, $value);
}
$setter = $this->_getCustomMutator($fieldName);
if ($setter) {
return $this->$setter($value);
}
$this->_set($fieldName, $value);
}
/**
* Checks the type of a virtual property.
*
* @param <type> $fieldName
* @param <type> $value
*/
protected function _checkType($fieldName, $value)
{
$type = Doctrine_Common_VirtualPropertySystem::getType($this->_entityName, $fieldName);
if (Doctrine_Common_VirtualPropertySystem::isSimplePHPType($type)) {
$is_type = "is_$type";
if ( ! $is_type($value)) {
throw new Doctrine_Exception("'$value' is of an invalid type. Expected: $type.");
}
} else if ($type == 'array') {
if ( ! is_array($value)) {
throw new Doctrine_Exception("'$value' is of an invalid type. Expected: array.");
}
} else {
if ( ! $value instanceof $type) {
throw new Doctrine_Exception("'$value' is of an invalid type. Expected: $type.");
}
}
}
protected function _get($fieldName)
{
return isset($this->_data[$fieldName]) ? $this->_data[$fieldName] : null;
}
protected function _set($fieldName, $value)
{
$this->_data[$fieldName] = $value;
}
/**
* Gets the custom mutator method for a virtual property, if it exists.
*
* @param string $fieldName The field name.
* @return mixed The name of the custom mutator or FALSE, if the field does
* not have a custom mutator.
*/
private function _getCustomMutator($fieldName)
{
if (Doctrine_Common_VirtualPropertySystem::getMutator($this->_entityName, $fieldName) === null) {
if (Doctrine_Common_VirtualPropertySystem::isAutoAccessorOverride()) {
$setterMethod = 'set' . Doctrine::classify($fieldName);
if ( ! method_exists($this, $setterMethod)) {
$setterMethod = false;
}
Doctrine_Common_VirtualPropertySystem::setMutator(
$this->_entityName, $fieldName, $setterMethod);
} else {
Doctrine_Common_VirtualPropertySystem::setMutator(
$this->_entityName, $fieldName, false);
}
}
return Doctrine_Common_VirtualPropertySystem::getMutator($this->_entityName, $fieldName);
}
/**
* Gets the custom accessor method of a virtual property, if it exists.
*
* @param string $fieldName The field name.
* @return mixed The name of the custom accessor method, or FALSE if the
* field does not have a custom accessor.
*/
private function _getCustomAccessor($fieldName)
{
if (Doctrine_Common_VirtualPropertySystem::getAccessor($this->_entityName, $fieldName) === null) {
if (Doctrine_Common_VirtualPropertySystem::isAutoAccessorOverride()) {
$getterMethod = 'get' . Doctrine::classify($fieldName);
if ( ! method_exists($this, $getterMethod)) {
$getterMethod = false;
}
Doctrine_Common_VirtualPropertySystem::setAccessor(
$this->_entityName, $fieldName, $getterMethod);
} else {
Doctrine_Common_VirtualPropertySystem::setAccessor(
$this->_entityName, $fieldName, false);
}
}
return Doctrine_Common_VirtualPropertySystem::getAccessor($this->_entityName, $fieldName);
}
protected function _contains($fieldName)
{
return isset($this->_data[$fieldName]);
}
protected function _unset($fieldName)
{
unset($this->_data[$fieldName]);
}
/**
* Intercepts mutating calls for virtual properties.
*
* @see set, offsetSet
* @param $name
* @param $value
* @since 1.0
* @return void
*/
public function __set($name, $value)
{
$this->set($name, $value);
}
/**
* Intercepts accessing calls for virtual properties.
*
* @see get, offsetGet
* @param mixed $name
* @return mixed
*/
public function __get($name)
{
return $this->get($name);
}
/**
* Intercepts isset() calls for virtual properties.
*
* @param string $name
* @return boolean whether or not this object contains $name
*/
public function __isset($name)
{
return $this->_contains($name);
}
/**
* Intercepts unset() calls for virtual properties.
*
* @param string $name
* @return void
*/
public function __unset($name)
{
return $this->_unset($name);
}
/* ArrayAccess implementation */
/**
* Check if an offsetExists.
*
* @param mixed $offset
* @return boolean whether or not this object contains $offset
*/
public function offsetExists($offset)
{
return $this->_contains($offset);
}
/**
* offsetGet an alias of get()
*
* @see get, __get
* @param mixed $offset
* @return mixed
*/
public function offsetGet($offset)
{
return $this->get($offset);
}
/**
* sets $offset to $value
* @see set, __set
* @param mixed $offset
* @param mixed $value
* @return void
*/
public function offsetSet($offset, $value)
{
return $this->set($offset, $value);
}
/**
* unset a given offset
* @see set, offsetSet, __set
* @param mixed $offset
*/
public function offsetUnset($offset)
{
return $this->_unset($offset);
}
/* END of ArrayAccess implementation */
}
?>
<?php
/**
* The VirtualPropertySystem class is a class consisting solely of static methods and
* serves as a generic virtual property registry system.
* Classes register their (virtual) properties with the property system, optionally specifying
* property features/options. These can then be evaluated by other code.
*
* @author robo
* @since 2.0
*/
class Doctrine_Common_VirtualPropertySystem {
private static $_properties = array();
private static $_callback = 'construct';
private static $_checkTypes = false;
private static $_useAutoAccessorOverride = true;
private static $_simplePHPTypes = array(
'int' => true,
'string' => true,
'bool' => true,
'double' => true
);
/** Private constructor. This class cannot be instantiated. */
private function __construct() {}
/**
* Gets all properties of a class that are registered with the VirtualPropertySystem.
*
* @param string $class
* @return array
*/
public static function getProperties($class)
{
if ( ! self::isInitialized($class)) {
self::initialize($class);
}
return self::$_properties[$class];
}
/**
* Gets whether automatic accessor overrides are enabled.
*
* @return boolean
*/
public static function isAutoAccessorOverride()
{
return self::$_useAutoAccessorOverride;
}
/**
* Sets whether automatic accessor overrides are enabled.
*
* @param boolean $bool
*/
public static function setAutoAccessorOverride($bool)
{
self::$_useAutoAccessorOverride = (bool)$bool;
}
/**
* Prepopulates the property system.
*
* @param array $properties
*/
public static function populate(array $properties)
{
self::$_properties = $properties;
}
/**
* Checks whether the given type is a simple PHP type.
* Simple php types are: int, string, bool, double.
*
* @param string $type The type to check.
* @return boolean
*/
public static function isSimplePHPType($type)
{
return isset(self::$_simplePHPTypes[$type]);
}
/**
* Gets whether type checks are enabled.
*
* @return boolean
*/
public static function isTypeCheckEnabled()
{
return self::$_checkTypes;
}
/**
* Sets whether type checks are enabled.
*
* @param boolean $bool
*/
public static function setTypeCheckEnabled($bool)
{
self::$_checkTypes = (bool)$bool;
}
/**
* Sets the name of the callback method to use for initializing the virtual
* properties of a class. The callback must be static and public.
*
* @param string $callback
*/
public static function setCallback($callback)
{
self::$_callback = $callback;
}
/**
* Registers a virtual property for a class.
*
* @param string $class
* @param string $propName
* @param string $type
* @param string $accessor
* @param string $mutator
*/
public static function register($class, $propName, $type, $accessor = null, $mutator = null)
{
self::$_properties[$class][$propName] = array(
'type' => $type, 'accessor' => $accessor, 'mutator' => $mutator
);
}
/**
* Gets whether a class has already been initialized by the virtual property system.
*
* @param string $class
* @return boolean
*/
public static function isInitialized($class)
{
return isset(self::$_properties[$class]);
}
/**
* Initializes a class with the virtual property system.
*
* @param <type> $class
*/
public static function initialize($class)
{
if (method_exists($class, self::$_callback)) {
call_user_func(array($class, self::$_callback));
} else {
self::$_properties[$class] = false;
}
}
/**
* Gets whether a class has a virtual property with a certain name.
*
* @param string $class
* @param string $propName
* @return boolean
*/
public static function hasProperty($class, $propName)
{
return isset(self::$_properties[$class][$propName]);
}
/**
* Gets the accessor for a virtual property.
*
* @param string $class
* @param string $propName
* @return string|null
*/
public static function getAccessor($class, $propName)
{
return isset(self::$_properties[$class][$propName]['accessor']) ?
self::$_properties[$class][$propName]['accessor'] : null;
}
/**
* Sets the accessor method for a virtual property.
*
* @param <type> $class
* @param <type> $propName
* @param <type> $accessor
*/
public static function setAccessor($class, $propName, $accessor)
{
self::$_properties[$class][$propName]['accessor'] = $accessor;
}
/**
* Gets the mutator method for a virtual property.
*
* @param <type> $class
* @param <type> $propName
* @return <type>
*/
public static function getMutator($class, $propName)
{
return isset(self::$_properties[$class][$propName]['mutator']) ?
self::$_properties[$class][$propName]['mutator'] : null;
}
/**
* Sets the mutator method for a virtual property.
*
* @param <type> $class
* @param <type> $propName
* @param <type> $mutator
*/
public static function setMutator($class, $propName, $mutator)
{
self::$_properties[$class][$propName]['mutator'] = $mutator;
}
/**
* Gets the type of a virtual property.
*
* @param <type> $class
* @param <type> $propName
* @return <type>
*/
public static function getType($class, $propName)
{
return isset(self::$_properties[$class][$propName]['type']) ?
self::$_properties[$class][$propName]['type'] : null;
}
/**
* Sets the type of a virtual property.
*
* @param <type> $class
* @param <type> $propName
* @param <type> $type
*/
public static function setType($class, $propName, $type)
{
self::$_properties[$class][$propName]['type'] = $type;
}
}
?>
......@@ -542,8 +542,9 @@ class Connection
public function exec($query, array $params = array()) {
$this->connect();
try {
echo $query . PHP_EOL;
if ( ! empty($params)) {
//var_dump($params);
var_dump($params);
$stmt = $this->prepare($query);
$stmt->execute($params);
return $stmt->rowCount();
......
This diff is collapsed.
<?php
/*
* $Id: Reporter.php 3882 2008-02-22 18:11:35Z jwage $
*
* 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>.
*/
/**
* Doctrine_Export_Reporter
*
* @package Doctrine
* @subpackage Export
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.org
* @since 1.0
* @version $Revision: 3882 $
*/
class Doctrine_Export_Reporter implements IteratorAggregate {
protected $messages = array();
public function add($code, $message) {
$this->messages[] = array($code, $message);
}
public function pop() {
return array_pop($this->messages);
}
public function getIterator() {
return new ArrayIterator($this->messages);
}
}
\ No newline at end of file
......@@ -31,9 +31,11 @@ if ( ! class_exists('\Addendum', false)) {
require __DIR__ . '/DoctrineAnnotations.php';
/**
* The AnnotationDriver reads the mapping metadata from docblock annotations.
* The AnnotationDriver reads the mapping metadata from docblock annotations
* with the help of the Addendum reflection extensions.
*
* @author robo
* @since 2.0
*/
class AnnotationDriver
{
......
......@@ -51,6 +51,11 @@ class ManyToManyMapping extends AssociationMapping
* Maps the columns in the target table to the columns in the relation table.
*/
private $_targetToRelationKeyColumns = array();
/**
* The columns on the join table.
*/
private $_joinTableColumns = array();
/**
* Initializes a new ManyToManyMapping.
......@@ -76,25 +81,34 @@ class ManyToManyMapping extends AssociationMapping
if ( ! isset($mapping['joinTable'])) {
throw MappingException::joinTableRequired($mapping['fieldName']);
}
// owning side MUST specify joinColumns
if ( ! isset($mapping['joinTable']['joinColumns'])) {
throw MappingException::invalidMapping($this->_sourceFieldName);
}
foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
$this->_sourceToRelationKeyColumns[$joinColumn['referencedColumnName']] = $joinColumn['name'];
$this->_joinTableColumns[] = $joinColumn['name'];
}
$this->_sourceKeyColumns = array_keys($this->_sourceToRelationKeyColumns);
// owning side MUST specify inverseJoinColumns
if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
throw MappingException::invalidMapping($this->_sourceFieldName);
}
foreach ($mapping['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) {
$this->_targetToRelationKeyColumns[$inverseJoinColumn['referencedColumnName']] = $inverseJoinColumn['name'];
$this->_joinTableColumns[] = $inverseJoinColumn['name'];
}
$this->_targetKeyColumns = array_keys($this->_targetToRelationKeyColumns);
}
}
public function getJoinTableColumns()
{
return $this->_joinTableColumns;
}
public function getSourceToRelationKeyColumns()
{
return $this->_sourceToRelationKeyColumns;
......
......@@ -27,11 +27,11 @@ use Doctrine\ORM\Mapping\AssociationMapping;
* A PersistentCollection represents a collection of elements that have persistent state.
* Collections of entities represent only the associations (links) to those entities.
* That means, if the collection is part of a many-many mapping and you remove
* entities from the collection, only the links in the xref table are removed (on flush).
* entities from the collection, only the links in the relation table are removed (on flush).
* Similarly, if you remove entities from a collection that is part of a one-many
* mapping this will only result in the nulling out of the foreign keys on flush
* (or removal of the links in the xref table if the one-many is mapped through an
* xref table). If you want entities in a one-many collection to be removed when
* (or removal of the links in the relation table if the one-many is mapped through a
* relation table). If you want entities in a one-many collection to be removed when
* they're removed from the collection, use deleteOrphans => true on the one-many
* mapping.
*
......
......@@ -55,54 +55,54 @@ abstract class AbstractCollectionPersister
}
//...
}
/**
* Deletes the persistent state represented by the given collection.
*
* @param PersistentCollection $coll
*/
public function delete(PersistentCollection $coll)
{
if ($coll->getRelation()->isInverseSide()) {
return;
if ($coll->getMapping()->isInverseSide()) {
return; // ignore inverse side
}
//...
$sql = $this->_getDeleteSql($coll);
$this->_conn->exec($sql, $this->_getDeleteSqlParameters($coll));
}
abstract protected function _getDeleteSql(PersistentCollection $coll);
abstract protected function _getDeleteSqlParameters(PersistentCollection $coll);
public function update(PersistentCollection $coll)
{
if ($coll->getMapping()->isInverseSide()) {
return; // ignore inverse side
}
$this->deleteRows($coll);
$this->updateRows($coll);
//$this->updateRows($coll);
$this->insertRows($coll);
}
/* collection update actions */
public function deleteRows(PersistentCollection $coll)
{
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, $this->_getDeleteRowSqlParameters($element));
$this->_conn->exec($sql, $this->_getDeleteRowSqlParameters($coll, $element));
}
}
public function updateRows(PersistentCollection $coll)
{
}
{}
public function insertRows(PersistentCollection $coll)
{
if ($coll->getMapping()->isInverseSide()) {
return; // ignore inverse side
}
$insertDiff = $coll->getInsertDiff();
$sql = $this->_getInsertRowSql($coll);
$uow = $this->_em->getUnitOfWork();
foreach ($insertDiff as $element) {
$this->_conn->exec($sql/*, $uow->getEntityIdentifier($element)*/);
$this->_conn->exec($sql, $this->_getInsertRowSqlParameters($coll, $element));
}
}
......@@ -120,13 +120,15 @@ abstract class AbstractCollectionPersister
*
* @param PersistentCollection $coll
*/
abstract protected function _getUpdateRowSql();
abstract protected function _getUpdateRowSql(PersistentCollection $coll);
/**
* Gets the SQL statement used for inserting a row from to the collection.
*
* @param PersistentCollection $coll
*/
abstract protected function _getInsertRowSql();
abstract protected function _getInsertRowSql(PersistentCollection $coll);
abstract protected function _getInsertRowSqlParameters(PersistentCollection $coll, $element);
}
......@@ -171,13 +171,8 @@ abstract class AbstractEntityPersister
protected function _prepareData($entity, array &$result, $isInsert = false)
{
foreach ($this->_em->getUnitOfWork()->getEntityChangeSet($entity) as $field => $change) {
if (is_array($change)) {
$oldVal = $change[0];
$newVal = $change[1];
} else {
$oldVal = null;
$newVal = $change;
}
$oldVal = $change[0];
$newVal = $change[1];
$type = $this->_classMetadata->getTypeOfField($field);
$columnName = $this->_classMetadata->getColumnName($field);
......
......@@ -21,40 +21,112 @@
namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\PersistentCollection;
/**
* Persister for many-to-many collections.
*
* @author robo
* @since 2.0
*/
class ManyToManyPersister extends AbstractCollectionPersister
{
/**
* {@inheritdoc}
*
* @param <type> $coll
* @override
* @todo Identifier quoting.
* @see _getDeleteRowSqlParameters()
*/
protected function _getDeleteRowSql(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
$joinTable = $mapping->getJoinTable();
$columns = array_merge($mapping->getSourceKeyColumns(), $mapping->getTargetKeyColumns());
return "DELETE FROM $joinTable WHERE " . implode(' = ?, ', $columns) . ' = ?';
$columns = $mapping->getJoinTableColumns();
return "DELETE FROM {$joinTable['name']} WHERE " . implode(' = ? AND ', $columns) . ' = ?';
}
/**
* {@inheritdoc}
*
* @param <type> $element
* @override
* @see _getDeleteRowSql()
*/
protected function _getDeleteRowSqlParameters(PersistentCollection $coll, $element)
{
$owner = $coll->getOwner();
$params = array_merge(
$this->_uow->getEntityIdentifier($coll->getOwner()),
$this->_uow->getEntityIdentifier($element)
);
//var_dump($params);
return $params;
}
/**
* {@inheritdoc}
*
* @override
*/
protected function _getUpdateRowSql(PersistentCollection $coll)
{
}
/**
* {@inheritdoc}
*
* @override
*/
protected function _getInsertRowSql(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
$joinTable = $mapping->getJoinTable();
$columns = $mapping->getJoinTableColumns();
return "INSERT INTO {$joinTable['name']} (" . implode(', ', $columns) . ")"
. " VALUES (" . implode(', ', array_fill(0, count($columns), '?')) . ')';
}
/**
* {@inheritdoc}
*
* @override
*/
protected function _getInsertRowSqlParameters(PersistentCollection $coll, $element)
{
//FIXME: This is still problematic for composite keys because we silently
// rely on a specific ordering of the columns.
$params = array_merge(
$this->_uow->getEntityIdentifier($coll->getOwner()),
$this->_uow->getEntityIdentifier($element)
);
var_dump($params);
return $params;
}
/**
* {@inheritdoc}
*
* @override
*/
protected function _getDeleteSql(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
$joinTable = $mapping->getJoinTable();
$whereClause = '';
foreach ($mapping->getSourceToRelationKeyColumns() as $relationColumn) {
if ($whereClause !== '') $whereClause .= ' AND ';
$whereClause .= "$relationColumn = ?";
}
return "DELETE FROM {$joinTable['name']} WHERE $whereClause";
}
/**
* {@inheritdoc}
*
* @override
*/
protected function _getDeleteSqlParameters(PersistentCollection $coll)
{
//FIXME: This is still problematic for composite keys because we silently
// rely on a specific ordering of the columns.
return $this->_uow->getEntityIdentifier($coll->getOwner());
}
}
......@@ -25,8 +25,11 @@ use Doctrine\ORM\PersistentCollection;
/**
* Persister for one-to-many collections.
*
* This persister is only used for uni-directional one-to-many mappings.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
*/
class OneToManyPersister extends AbstractCollectionPersister
{
......@@ -57,19 +60,7 @@ class OneToManyPersister extends AbstractCollectionPersister
$whereClause .= "$idColumn = ?";
}
return "UPDATE $table SET $setClause WHERE $whereClause";
}
/**
* {@inheritdoc}
*
* @param <type> $element
* @return <type>
* @override
*/
protected function _getDeleteRowSqlParameters(PersistentCollection $coll, $element)
{
return $this->_uow->getEntityIdentifier($element);
return array("UPDATE $table SET $setClause WHERE $whereClause", $this->_uow->getEntityIdentifier($element));
}
protected function _getInsertRowSql()
......
This diff is collapsed.
......@@ -6,8 +6,7 @@ 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;
use Doctrine\Tests\Models\CMS\CmsGroup;
require_once __DIR__ . '/../../TestInit.php';
......@@ -25,7 +24,8 @@ class BasicCRUDTest extends \Doctrine\Tests\OrmFunctionalTestCase {
$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\CmsAddress')
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'),
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup')
));
// Create
......@@ -48,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\PersistentCollection);
//$this->assertTrue($user->phonenumbers instanceof \Doctrine\ORM\PersistentCollection);
// Update name
$user->name = 'guilherme';
......@@ -90,7 +90,7 @@ class BasicCRUDTest extends \Doctrine\Tests\OrmFunctionalTestCase {
$this->_em->save($user);
$this->_em->flush();
$this->assertTrue($user->phonenumbers instanceof \Doctrine\ORM\PersistentCollection);
//$this->assertTrue($user->phonenumbers instanceof \Doctrine\ORM\PersistentCollection);
// Remove the first element from the collection
unset($user->phonenumbers[0]);
......@@ -125,5 +125,63 @@ class BasicCRUDTest extends \Doctrine\Tests\OrmFunctionalTestCase {
array($address->id))->fetchColumn();
$this->assertTrue(is_numeric($userId));
}
public function testBasicManyToMany()
{
$user = new CmsUser;
$user->name = 'Guilherme';
$user->username = 'gblanco';
$user->status = 'developer';
$group = new CmsGroup;
$group->name = 'Developers';
$user->groups[] = $group;
$group->users[] = $user;
$this->_em->save($user);
$this->_em->save($group);
$this->_em->flush();
unset($group->users[0]); // inverse side
unset($user->groups[0]); // owning side!
$this->_em->flush();
// Check that the link in the association table has been deleted
$count = $this->_em->getConnection()->execute("SELECT COUNT(*) FROM cms_users_groups",
array())->fetchColumn();
$this->assertEquals(0, $count);
}
public function testManyToManyCollectionClearing()
{
$user = new CmsUser;
$user->name = 'Guilherme';
$user->username = 'gblanco';
$user->status = 'developer';
for ($i=0; $i<10; ++$i) {
$group = new CmsGroup;
$group->name = 'Developers_' . $i;
$user->groups[] = $group;
$group->users[] = $user;
}
$this->_em->save($user); // Saves the user, cause of post-insert ID
$this->_em->flush(); // Saves the groups, cause they're attached to a persistent entity ($user)
//$user->groups->clear();
unset($user->groups);
$this->_em->flush();
// Check that the links in the association table have been deleted
$count = $this->_em->getConnection()->execute("SELECT COUNT(*) FROM cms_users_groups",
array())->fetchColumn();
$this->assertEquals(0, $count);
}
}
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