diff --git a/lib/Doctrine/Common/Collections/Collection.php b/lib/Doctrine/Common/Collections/Collection.php index a17e08fe8..a4d7fef30 100644 --- a/lib/Doctrine/Common/Collections/Collection.php +++ b/lib/Doctrine/Common/Collections/Collection.php @@ -1,7 +1,22 @@ . */ namespace Doctrine\Common\Collections; @@ -12,10 +27,11 @@ use \ArrayAccess; use \ArrayIterator; /** - * A Collection is a wrapper around a php array and just like a php array a - * collection instance can be a list, a set or a map, depending on how it is used. + * A Collection is a thin wrapper around a php array. Think of it as an OO version + * of a plain array. * * @author robo + * @since 2.0 */ class Collection implements Countable, IteratorAggregate, ArrayAccess { @@ -25,7 +41,7 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess * * @var array */ - protected $_data = array(); + protected $_elements = array(); /** * @@ -33,7 +49,7 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess */ public function __construct(array $elements = array()) { - $this->_data = $elements; + $this->_elements = $elements; } /** @@ -43,49 +59,49 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess */ public function unwrap() { - return $this->_data; + return $this->_elements; } /** - * returns the first entry in the collection + * Gets the first element in the collection. * * @return mixed */ public function first() { - return reset($this->_data); + return reset($this->_elements); } /** - * returns the last record in the collection + * Gets the last element in the collection. * * @return mixed */ public function last() { - return end($this->_data); + return end($this->_elements); } /** - * returns the current key + * Gets the current key. * * @return mixed */ public function key() { - return key($this->_data); + return key($this->_elements); } /** - * Removes an entry with a specific key from the collection. + * Removes an element with a specific key from the collection. * * @param mixed $key * @return mixed */ public function remove($key) { - $removed = $this->_data[$key]; - unset($this->_data[$key]); + $removed = $this->_elements[$key]; + unset($this->_elements[$key]); return $removed; } @@ -97,9 +113,9 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess */ public function removeElement($element) { - $key = array_search($element, $this->_data, true); + $key = array_search($element, $this->_elements, true); if ($key !== false) { - unset($this->_data[$key]); + unset($this->_elements[$key]); return true; } return false; @@ -169,7 +185,7 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess */ public function containsKey($key) { - return isset($this->_data[$key]); + return isset($this->_elements[$key]); } /** @@ -184,24 +200,23 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess */ public function contains($element) { - return in_array($element, $this->_data, true); + return in_array($element, $this->_elements, true); } /** * Tests for the existance of an element that satisfies the given predicate. * - * @param function $func + * @param Closure $p The predicate. * @return boolean TRUE if the predicate is TRUE for at least one element, FALSE otherwise. */ - public function exists(Closure $func) { - foreach ($this->_data as $key => $element) - if ($func($key, $element)) - return true; + public function exists(Closure $p) { + foreach ($this->_elements as $key => $element) + if ($p($key, $element)) return true; return false; } /** - * Enter description here... + * TODO * * @param unknown_type $otherColl * @todo Impl @@ -222,7 +237,7 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess */ public function search($element) { - return array_search($element, $this->_data, true); + return array_search($element, $this->_elements, true); } /** @@ -233,20 +248,20 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess */ public function get($key) { - if (isset($this->_data[$key])) { - return $this->_data[$key]; + if (isset($this->_elements[$key])) { + return $this->_elements[$key]; } return null; } /** - * Gets all keys/indexes. + * Gets all keys/indexes of the collection elements. * * @return array */ public function getKeys() { - return array_keys($this->_data); + return array_keys($this->_elements); } /** @@ -256,7 +271,7 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess */ public function getElements() { - return array_values($this->_data); + return array_values($this->_elements); } /** @@ -268,7 +283,7 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess */ public function count() { - return count($this->_data); + return count($this->_elements); } /** @@ -277,24 +292,23 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess * When the collection is a Map this is like put(key,value)/add(key,value). * When the collection is a List this is like add(position,value). * - * @param integer $key + * @param mixed $key * @param mixed $value */ public function set($key, $value) { - $this->_data[$key] = $value; + $this->_elements[$key] = $value; } /** * Adds an element to the collection. * * @param mixed $value - * @param string $key - * @return boolean Always returns TRUE. + * @return boolean Always TRUE. */ public function add($value) { - $this->_data[] = $value; + $this->_elements[] = $value; return true; } @@ -317,45 +331,80 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess public function isEmpty() { // Note: Little "trick". Empty arrays evaluate to FALSE. No need to count(). - return ! (bool)$this->_data; + return ! (bool)$this->_elements; } /** - * Gets an iterator that enables foreach() iteration over the elements in - * the collection. + * Gets an iterator for iterating over the elements in the collection. * * @return ArrayIterator */ public function getIterator() { - $data = $this->_data; - return new ArrayIterator($data); + return new ArrayIterator($this->_elements); } /** * Applies the given function to each element in the collection and returns - * a new collection with the modified values. + * a new collection with the elements returned by the function. * - * @param function $func + * @param Closure $func */ public function map(Closure $func) { - return new Collection(array_map($func, $this->_data)); + return new Collection(array_map($func, $this->_elements)); } /** - * Applies the given function to each element in the collection and returns - * a new collection with the new values. + * Returns all the elements of this collection that satisfy the predicate p. + * The order of the elements is preserved. * - * @param function $func + * @param Closure $p The predicate used for filtering. + * @return Collection A collection with the results of the filter operation. */ - public function filter(Closure $func) + public function filter(Closure $p) { - return new Collection(array_filter($this->_data, $func)); + return new Collection(array_filter($this->_elements, $p)); } /** - * returns a string representation of this object + * Applies the given predicate p to all elements of this collection, + * returning true, if the predicate yields true for all elements. + * + * @param Closure $p The predicate. + * @return boolean TRUE, if the predicate yields TRUE for all elements, FALSE otherwise. + */ + public function forall(Closure $p) + { + foreach ($this->_elements as $key => $element) + if ( ! $p($key, $element)) return false; + return true; + } + + /** + * Partitions this collection in two collections according to a predicate. + * Keys are preserved in the resulting collections. + * + * @param Closure $p The predicate on which to partition. + * @return array An array with two elements. The first element contains the collection + * of elements where the predicate returned TRUE, the second element + * contains the collection of elements where the predicate returned FALSE. + */ + public function partition(Closure $p) + { + $coll1 = $coll2 = array(); + foreach ($this->_elements as $key => $element) { + if ($p($key, $element)) { + $coll1[$key] = $element; + } else { + $coll2[$key] = $element; + } + } + return array(new Collection($coll1), new Collection($coll2)); + } + + /** + * Returns a string representation of this object. */ public function __toString() { @@ -364,12 +413,10 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess /** * Clears the collection. - * - * @return void */ public function clear() { - $this->_data = array(); + $this->_elements = array(); } } diff --git a/lib/Doctrine/Common/Events/Event.php b/lib/Doctrine/Common/EventArgs.php similarity index 61% rename from lib/Doctrine/Common/Events/Event.php rename to lib/Doctrine/Common/EventArgs.php index 88dc75603..7353a54bf 100644 --- a/lib/Doctrine/Common/Events/Event.php +++ b/lib/Doctrine/Common/EventArgs.php @@ -16,60 +16,51 @@ * * This software consists of voluntary contributions made by many individuals * and is licensed under the LGPL. For more information, see - * . + * . */ -namespace Doctrine\Common\Events; +namespace Doctrine\Common; /** - * Doctrine_Event + * EventArgs is the base class for classes containing event data. + * + * This class contains no event data and cannot be instantiated. + * It is used by events that do not pass state information to an event handler + * when an event is raised. The single empty EventArgs instance can be obtained + * through {@link getEmptyInstance()}. * * @author Konsta Vesterinen + * @author Roman Borschel * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.phpdoctrine.org + * @link www.doctrine-project.org * @since 2.0 * @version $Revision$ */ -class Event +class EventArgs { - /* Event callback constants */ - const preDelete = 'preDelete'; - const postDelete = 'postDelete'; - //...more + private static $_emptyEventArgsInstance; + private $_defaultPrevented; - protected $_type; - protected $_target; - protected $_defaultPrevented; - - - public function __construct($type, $target = null) + protected function __construct() { - $this->_type = $type; - $this->_target = $target; $this->_defaultPrevented = false; } - - public function getType() - { - return $this->_type; - } - - public function preventDefault() { $this->_defaultPrevented = true; } - public function getDefaultPrevented() { return $this->_defaultPrevented; } - - public function getTarget() + public static function getEmptyInstance() { - return $this->_target; + if ( ! self::$_emptyEventArgsInstance) { + self::$_emptyEventArgsInstance = new EventArgs; + } + return self::$_emptyEventArgsInstance; } } diff --git a/lib/Doctrine/Common/EventManager.php b/lib/Doctrine/Common/EventManager.php index 4b43d98c5..10a14d602 100644 --- a/lib/Doctrine/Common/EventManager.php +++ b/lib/Doctrine/Common/EventManager.php @@ -16,7 +16,7 @@ * * This software consists of voluntary contributions made by many individuals * and is licensed under the LGPL. For more information, see - * . + * . */ namespace Doctrine\Common; @@ -45,20 +45,20 @@ class EventManager /** * Dispatches an event to all registered listeners. * - * @param string|Event $event The name of the event or the event object. + * @param string $eventName The name of the event to dispatch. The name of the event is + * the name of the method that is invoked on listeners. + * @param EventArgs $eventArgs The event arguments to pass to the event handlers/listeners. + * If not supplied, the single empty EventArgs instance is used. * @return boolean */ - public function dispatchEvent($event) + public function dispatchEvent($eventName, EventArgs $eventArgs = null) { - $argIsCallback = is_string($event); - $callback = $argIsCallback ? $event : $event->getType(); - - if (isset($this->_listeners[$callback])) { - $event = $argIsCallback ? new Event($event) : $event; - foreach ($this->_listeners[$callback] as $listener) { - $listener->$callback($event); + if (isset($this->_listeners[$eventName])) { + $eventArgs = is_null($eventArgs) ? EventArgs::getEmptyInstance() : $eventArgs; + foreach ($this->_listeners[$eventName] as $listener) { + $listener->$eventName($eventArgs); } - return ! $event->getDefaultPrevented(); + return ! $eventArgs->getDefaultPrevented(); } return true; } @@ -95,7 +95,7 @@ class EventManager { // TODO: maybe check for duplicate registrations? foreach ((array)$events as $event) { - $this->_listeners[$event] = $listener; + $this->_listeners[$event][] = $listener; } } @@ -109,5 +109,4 @@ class EventManager { $this->addEventListener($subscriber->getSubscribedEvents(), $subscriber); } -} - +} \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Connection.php b/lib/Doctrine/DBAL/Connection.php index 6fa541e46..f08ce7d92 100644 --- a/lib/Doctrine/DBAL/Connection.php +++ b/lib/Doctrine/DBAL/Connection.php @@ -154,9 +154,11 @@ class Connection * Initializes a new instance of the Connection class. * * @param array $params The connection parameters. + * @param Driver $driver + * @param Configuration $config + * @param EventManager $eventManager */ - public function __construct(array $params, Driver $driver, - Configuration $config = null, + public function __construct(array $params, Driver $driver, Configuration $config = null, EventManager $eventManager = null) { $this->_driver = $driver; @@ -182,7 +184,7 @@ class Connection /** * Gets the Configuration used by the Connection. * - * @return Configuration + * @return Doctrine\DBAL\Configuration */ public function getConfiguration() { @@ -389,23 +391,23 @@ class Connection } /** - * fetchAll + * Convenience method for PDO::query("...") followed by $stmt->fetchAll(PDO::FETCH_ASSOC). * - * @param string $statement sql query to be executed - * @param array $params prepared statement params + * @param string $sql The SQL query. + * @param array $params The query parameters. * @return array */ - public function fetchAll($statement, array $params = array()) + public function fetchAll($sql, array $params = array()) { - return $this->execute($statement, $params)->fetchAll(PDO::FETCH_ASSOC); + return $this->execute($sql, $params)->fetchAll(PDO::FETCH_ASSOC); } /** - * fetchOne + * Convenience method for PDO::query("...") followed by $stmt->fetchColumn(). * - * @param string $statement sql query to be executed - * @param array $params prepared statement params - * @param int $colnum 0-indexed column number to retrieve + * @param string $statement The SQL query. + * @param array $params The query parameters. + * @param int $colnum 0-indexed column number to retrieve * @return mixed */ public function fetchOne($statement, array $params = array(), $colnum = 0) @@ -414,10 +416,10 @@ class Connection } /** - * fetchRow + * Convenience method for PDO::query("...") followed by $stmt->fetch(PDO::FETCH_ASSOC). * - * @param string $statement sql query to be executed - * @param array $params prepared statement params + * @param string $statement The SQL query. + * @param array $params The query parameters. * @return array */ public function fetchRow($statement, array $params = array()) @@ -426,7 +428,7 @@ class Connection } /** - * fetchArray + * Convenience method for PDO::query("...") followed by $stmt->fetch(PDO::FETCH_NUM). * * @param string $statement sql query to be executed * @param array $params prepared statement params @@ -438,7 +440,7 @@ class Connection } /** - * fetchColumn + * Convenience method for PDO::query("...") followed by $stmt->fetchAll(PDO::FETCH_COLUMN, ...). * * @param string $statement sql query to be executed * @param array $params prepared statement params @@ -451,19 +453,7 @@ class Connection } /** - * fetchAssoc - * - * @param string $statement sql query to be executed - * @param array $params prepared statement params - * @return array - */ - public function fetchAssoc($statement, array $params = array()) - { - return $this->execute($statement, $params)->fetchAll(PDO::FETCH_ASSOC); - } - - /** - * fetchBoth + * Convenience method for PDO::query("...") followed by $stmt->fetchAll(PDO::FETCH_BOTH). * * @param string $statement sql query to be executed * @param array $params prepared statement params @@ -478,7 +468,7 @@ class Connection * Prepares an SQL statement. * * @param string $statement - * @return Doctrine::DBAL::Statement + * @return PDOStatement */ public function prepare($statement) { @@ -487,13 +477,13 @@ class Connection } /** - * Queries the database with limit and offset - * added to the query and returns a Doctrine_Connection_Statement object + * Queries the database with limit and offset added to the query and returns + * a Statement object. * * @param string $query * @param integer $limit * @param integer $offset - * @return Doctrine_Connection_Statement + * @return Statement */ public function select($query, $limit = 0, $offset = 0) { diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index f642b96b2..8f122e1e7 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -17,23 +17,21 @@ * * This software consists of voluntary contributions made by many individuals * and is licensed under the LGPL. For more information, see - * . + * . */ namespace Doctrine\ORM; /** - * Doctrine_ORM_Query_Abstract + * Base class for Query and NativeQuery. * - * @package Doctrine - * @subpackage Query * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.phpdoctrine.com + * @link www.doctrine-project.com * @since 1.0 * @version $Revision: 1393 $ * @author Guilherme Blanco * @author Konsta Vesterinen - * @todo See {@link Doctrine_ORM_Query} + * @author Roman Borschel */ abstract class AbstractQuery { @@ -101,7 +99,7 @@ abstract class AbstractQuery /** * @var integer $type Query type. * - * @see Doctrine_ORM_Query::* constants + * @see Query::* constants */ protected $_type = self::SELECT; @@ -112,19 +110,19 @@ abstract class AbstractQuery /** * @var array $params Parameters of this query. - * @see Doctrine_ORM_Query::free that initializes this property + * @see Query::free that initializes this property */ protected $_params = array(); /** * @var array $_enumParams Array containing the keys of the parameters that should be enumerated. - * @see Doctrine_ORM_Query::free that initializes this property + * @see Query::free that initializes this property */ protected $_enumParams = array(); /** * @var array $_dqlParts An array containing all DQL query parts. - * @see Doctrine_ORM_Query::free that initializes this property + * @see Query::free that initializes this property */ protected $_dqlParts = array(); diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 7be4db1fb..101b97d5d 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -24,7 +24,6 @@ namespace Doctrine\ORM; 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; @@ -90,7 +89,7 @@ class EntityManager private $_conn; /** - * The metadata factory, used to retrieve the metadata of entity classes. + * The metadata factory, used to retrieve the ORM metadata of entity classes. * * @var Doctrine\ORM\Mapping\ClassMetadataFactory */ @@ -330,7 +329,7 @@ class EntityManager */ public function flush() { - $this->_errorIfNotActiveOrClosed(); + $this->_errorIfClosed(); $this->_unitOfWork->commit(); } @@ -412,7 +411,7 @@ class EntityManager */ public function save($object) { - $this->_errorIfNotActiveOrClosed(); + $this->_errorIfClosed(); $this->_unitOfWork->save($object); if ($this->_flushMode == self::FLUSHMODE_IMMEDIATE) { $this->flush(); @@ -426,7 +425,7 @@ class EntityManager */ public function delete($entity) { - $this->_errorIfNotActiveOrClosed(); + $this->_errorIfClosed(); $this->_unitOfWork->delete($entity); if ($this->_flushMode == self::FLUSHMODE_IMMEDIATE) { $this->flush(); @@ -459,7 +458,7 @@ class EntityManager } /** - * Gets the repository for an Entity. + * Gets the repository for an entity class. * * @param string $entityName The name of the Entity. * @return Doctrine\ORM\EntityRepository The repository. @@ -520,7 +519,7 @@ class EntityManager * * @throws EntityManagerException If the EntityManager is closed or not active. */ - private function _errorIfNotActiveOrClosed() + private function _errorIfClosed() { if ($this->_closed) { throw EntityManagerException::notActiveOrClosed($this->_name); diff --git a/lib/Doctrine/ORM/Exceptions/EntityManagerException.php b/lib/Doctrine/ORM/EntityManagerException.php similarity index 94% rename from lib/Doctrine/ORM/Exceptions/EntityManagerException.php rename to lib/Doctrine/ORM/EntityManagerException.php index 0d1255f19..576918d1d 100644 --- a/lib/Doctrine/ORM/Exceptions/EntityManagerException.php +++ b/lib/Doctrine/ORM/EntityManagerException.php @@ -16,10 +16,10 @@ * * This software consists of voluntary contributions made by many individuals * and is licensed under the LGPL. For more information, see - * . + * . */ -namespace Doctrine\ORM\Exceptions; +namespace Doctrine\ORM; /** * Doctrine_EntityManager_Exception @@ -27,7 +27,7 @@ namespace Doctrine\ORM\Exceptions; * @author Konsta Vesterinen * @author Roman Borschel * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.phpdoctrine.org + * @link www.doctrine-project.org * @since 2.0 * @version $Revision$ */ diff --git a/lib/Doctrine/ORM/Exceptions/ORMException.php b/lib/Doctrine/ORM/Events.php similarity index 61% rename from lib/Doctrine/ORM/Exceptions/ORMException.php rename to lib/Doctrine/ORM/Events.php index f340d27a7..4b21c188b 100644 --- a/lib/Doctrine/ORM/Exceptions/ORMException.php +++ b/lib/Doctrine/ORM/Events.php @@ -1,37 +1,42 @@ -. - */ +. + */ + +namespace Doctrine\ORM; + +/** + * Container for all ORM events. + * + * This class cannot be instantiated. + * + * @author robo + * @since 2.0 + */ +final class Events +{ + private function __construct() {} + + const preDelete = 'preDelete'; + const postDelete = 'postDelete'; + const preSave = 'preSave'; + const postSave = 'postSave'; + +} -#namespace Doctrine::ORM::Exceptions; - -/** - * Doctrine_Exception - * - * @package Doctrine - * @subpackage Exception - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.phpdoctrine.org - * @since 1.0 - * @version $Revision: 4776 $ - * @author Konsta Vesterinen - * @author Roman Borschel - */ -class Doctrine_ORM_Exceptions_ORMException extends Doctrine_Common_Exceptions_DoctrineException -{} diff --git a/lib/Doctrine/ORM/Exceptions/EntityException.php b/lib/Doctrine/ORM/Exceptions/EntityException.php deleted file mode 100644 index 19617b115..000000000 --- a/lib/Doctrine/ORM/Exceptions/EntityException.php +++ /dev/null @@ -1,65 +0,0 @@ -. - */ - -#namespace Doctrine::ORM::Exceptions; - -/** - * Doctrine_Entity_Exception - * - * @package Doctrine - * @subpackage Entity - * @author Konsta Vesterinen - * @author Roman Borschel - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.phpdoctrine.org - * @since 2.0 - * @version $Revision$ - */ -class Doctrine_ORM_Exceptions_EntityException extends Doctrine_ORM_Exceptions_ORMException -{ - public static function unknownField($field) - { - return new self("Undefined field: '$field'."); - } - - public static function invalidValueForOneToManyReference() - { - return new self("Invalid value. The value of a reference in a OneToMany " - . "association must be a Collection."); - } - - public static function invalidValueForOneToOneReference() - { - return new self("Invalid value. The value of a reference in a OneToOne " - . "association must be an Entity."); - } - - public static function invalidValueForManyToManyReference() - { - return new self("Invalid value. The value of a reference in a ManyToMany " - . "association must be a Collection."); - } - - public static function invalidField($field) - { - return new self("Invalid field: '$field'."); - } -} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Exceptions/HydrationException.php b/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php similarity index 83% rename from lib/Doctrine/ORM/Exceptions/HydrationException.php rename to lib/Doctrine/ORM/Internal/Hydration/HydrationException.php index a743b26cd..19c6c2e07 100644 --- a/lib/Doctrine/ORM/Exceptions/HydrationException.php +++ b/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php @@ -1,6 +1,6 @@ . + */ namespace Doctrine\ORM\Internal\Hydration; use \PDO; /** - * Description of ObjectHydrator + * The ObjectHydrator constructs an object graph out of an SQL result set. * * @author robo + * @since 2.0 */ class ObjectHydrator extends AbstractHydrator { @@ -64,8 +84,8 @@ class ObjectHydrator extends AbstractHydrator // Take snapshots from all initialized collections foreach ($this->_collections as $coll) { - $coll->_takeSnapshot(); - $coll->_setHydrationFlag(false); + $coll->takeSnapshot(); + $coll->setHydrationFlag(false); } // Clean up @@ -128,8 +148,8 @@ class ObjectHydrator extends AbstractHydrator $relation = $classMetadata->getAssociationMapping($name); $relatedClass = $this->_em->getClassMetadata($relation->getTargetEntityName()); $coll = $this->getCollection($relatedClass->getClassName()); - $coll->_setOwner($entity, $relation); - $coll->_setHydrationFlag(true); + $coll->setOwner($entity, $relation); + $coll->setHydrationFlag(true); $classMetadata->getReflectionProperty($name)->setValue($entity, $coll); $this->_initializedRelations[$oid][$name] = true; $this->_uow->setOriginalEntityProperty($oid, $name, $coll); diff --git a/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php index a69176848..f968f1db9 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php @@ -7,7 +7,6 @@ namespace Doctrine\ORM\Internal\Hydration; use \PDO; -use Doctrine\ORM\Exceptions\HydrationException; /** * Description of SingleScalarHydrator diff --git a/lib/Doctrine/ORM/Mapping/AssociationMapping.php b/lib/Doctrine/ORM/Mapping/AssociationMapping.php index b40d68af4..4a7211963 100644 --- a/lib/Doctrine/ORM/Mapping/AssociationMapping.php +++ b/lib/Doctrine/ORM/Mapping/AssociationMapping.php @@ -21,8 +21,6 @@ namespace Doctrine\ORM\Mapping; -use Doctrine\ORM\Exceptions\MappingException; - /** * Base class for association mappings. * diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index 016638110..65df407b4 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -16,19 +16,17 @@ * * This software consists of voluntary contributions made by many individuals * and is licensed under the LGPL. For more information, see - * . + * . */ namespace Doctrine\ORM\Mapping; use \ReflectionClass; use Doctrine\Common\DoctrineException; -use Doctrine\ORM\Exceptions\MappingException; /** - * A ClassMetadata instance holds all the information (metadata) of an entity and - * it's associations and how they're mapped to a relational database. - * It is the backbone of Doctrine's metadata mapping. + * A ClassMetadata instance holds all the ORM metadata of an entity and + * it's associations. It is the backbone of Doctrine's metadata mapping. * * @author Roman Borschel * @since 2.0 @@ -176,8 +174,8 @@ class ClassMetadata * - fieldName (string) * The name of the field in the Entity. * - * - type (object Doctrine::DBAL::Types::* or custom type) - * The database type of the column. Can be one of Doctrine's portable types + * - type (object Doctrine\DBAL\Types\* or custom type) + * The type of the column. Can be one of Doctrine's portable types * or a custom type. * * - columnName (string, optional) @@ -198,7 +196,7 @@ class ClassMetadata * therefore cant be used as a generator name! * * - nullable (boolean, optional) - * Whether the column is nullable. Defaults to TRUE. + * Whether the column is nullable. Defaults to FALSE. * * - columnDefinition (string, optional, schema-only) * The SQL fragment that is used when generating the DDL for the column. @@ -226,7 +224,7 @@ class ClassMetadata protected $_fieldMappings = array(); /** - * An array of field names. used to look up field names from column names. + * An array of field names. Used to look up field names from column names. * Keys are column names and values are field names. * This is the reverse lookup map of $_columnNames. * @@ -244,7 +242,7 @@ class ClassMetadata protected $_columnNames = array(); /** - * Map that maps lowercased column names to field names. + * Map that maps lowercased column names (keys) to field names (values). * Mainly used during hydration because Doctrine enforces PDO_CASE_LOWER * for portability. * @@ -254,7 +252,8 @@ class ClassMetadata /** * Whether to automatically OUTER JOIN subtypes when a basetype is queried. - * This does only apply to the JOINED inheritance mapping strategy. + * + * This does only apply to the JOINED inheritance mapping strategy. * * @var boolean */ @@ -262,8 +261,9 @@ class ClassMetadata /** * A map that maps discriminator values to class names. - * This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies - * where a discriminator column is used. + * + * This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies + * where a discriminator column is used. * * @var array * @see _discriminatorColumn @@ -299,14 +299,14 @@ class ClassMetadata protected $_lifecycleListenerInstances = array(); /** - * The registered lifecycle callbacks for Entities of this class. + * The registered lifecycle callbacks for entities of this class. * * @var array */ protected $_lifecycleCallbacks = array(); /** - * The registered lifecycle listeners for Entities of this class. + * The registered lifecycle listeners for entities of this class. * * @var array */ @@ -394,8 +394,10 @@ class ClassMetadata } /** + * Gets the ReflectionProperty for the single identifier field. * - * @return + * @return ReflectionProperty + * @throws DoctrineException If the class has a composite identifier. */ public function getSingleIdReflectionProperty() { @@ -478,7 +480,7 @@ class ClassMetadata { $mapping = $this->getFieldMapping($fieldName); if ($mapping !== false) { - return isset($mapping['notnull']) && $mapping['notnull'] == true; + return isset($mapping['nullable']) && $mapping['nullable'] == false; } return false; } @@ -502,7 +504,7 @@ class ClassMetadata * reference to another object. * * @param string $fieldName The field name. - * @return array The mapping. + * @return array The field mapping. */ public function getFieldMapping($fieldName) { @@ -522,7 +524,7 @@ class ClassMetadata public function getAssociationMapping($fieldName) { if ( ! isset($this->_associationMappings[$fieldName])) { - throw new Doctrine_Exception("Mapping not found: $fieldName"); + throw MappingException::mappingNotFound($fieldName); } return $this->_associationMappings[$fieldName]; } @@ -536,7 +538,7 @@ class ClassMetadata public function getInverseAssociationMapping($mappedByFieldName) { if ( ! isset($this->_inverseMappings[$mappedByFieldName])) { - throw new DoctrineException("Mapping not found: " . $mappedByFieldName); + throw MappingException::mappingNotFound($mappedByFieldName); } return $this->_inverseMappings[$mappedByFieldName]; } @@ -564,6 +566,7 @@ class ClassMetadata /** * Gets all association mappings of the class. + * * Alias for getAssociationMappings(). * * @return array @@ -590,9 +593,8 @@ class ClassMetadata * Gets the field name for a completely lowercased column name. * Mainly used during hydration. * - * @param string $lcColumnName - * @return string - * @todo Better name. + * @param string $lcColumnName The all-lowercase column name. + * @return string The field name. */ public function getFieldNameForLowerColumnName($lcColumnName) { @@ -612,7 +614,7 @@ class ClassMetadata } /** - * Validates & completes the field mapping. + * Validates & completes the given field mapping. * * @param array $mapping The field mapping to validated & complete. * @return array The validated and completed field mapping. @@ -668,11 +670,6 @@ class ClassMetadata $refProp->setAccessible(true); $this->_reflectionProperties[$mapping['fieldName']] = $refProp; } - - private function _validateAndCompleteClassMapping(array &$mapping) - { - - } /** * @todo Implementation of Optimistic Locking. @@ -760,7 +757,6 @@ class ClassMetadata * Gets all field mappings. * * @return array - * @deprecated */ public function getFieldMappings() { @@ -792,7 +788,7 @@ class ClassMetadata */ public function getIdentifierColumnNames() { - return $this->getColumnNames((array) $this->getIdentifier()); + return $this->getColumnNames((array)$this->getIdentifierFieldNames()); } /** @@ -920,18 +916,18 @@ class ClassMetadata * Gets the type of a field. * * @param string $fieldName - * @return string + * @return Doctrine\DBAL\Types\Type */ public function getTypeOfField($fieldName) { return isset($this->_fieldMappings[$fieldName]) ? - $this->_fieldMappings[$fieldName]['type'] : false; + $this->_fieldMappings[$fieldName]['type'] : null; } /** - * getTypeOfColumn + * Gets the type of a column. * - * @return mixed The column type or FALSE if the type cant be determined. + * @return Doctrine\DBAL\Types\Type */ public function getTypeOfColumn($columnName) { @@ -985,8 +981,9 @@ class ClassMetadata /** * Sets the subclasses of the mapped class. - * All entity classes that participate in a hierarchy and have subclasses - * need to declare them this way. + * + * All entity classes that participate in a hierarchy and have subclasses + * need to declare them this way. * * @param array $subclasses The names of all subclasses. */ @@ -1055,11 +1052,6 @@ class ClassMetadata */ public function setInheritanceType($type) { - if ($parentClassNames = $this->getParentClasses()) { - throw new MappingException("All classes in an inheritance hierarchy" - . " must share the same inheritance mapping type and this type must be set" - . " in the root class of the hierarchy."); - } if ( ! $this->_isInheritanceType($type)) { throw MappingException::invalidInheritanceType($type); } @@ -1067,31 +1059,7 @@ class ClassMetadata } /** - * exports this class to the database based on its mapping. - * - * @throws Doctrine_Connection_Exception If some error other than Doctrine::ERR_ALREADY_EXISTS - * occurred during the create table operation. - * @return boolean Whether or not the export operation was successful - * false if table already existed in the database. - * @todo Reimpl. & Placement. - */ - public function export() - { - } - - /** - * Returns an array with all the information needed to create the main database table - * for the class. - * - * @return array - * @todo Reimpl. & placement. - */ - public function getExportableFormat($parseForeignKeys = true) - { - } - - /** - * Checks whether a persistent field is inherited from a superclass. + * Checks whether a mapped field is inherited from a superclass. * * @return boolean TRUE if the field is inherited, FALSE otherwise. */ @@ -1104,6 +1072,7 @@ class ClassMetadata * Sets the name of the primary table the class is mapped to. * * @param string $tableName The table name. + * @deprecated */ public function setTableName($tableName) { @@ -1208,6 +1177,13 @@ class ClassMetadata $this->_fieldMappings[$mapping['fieldName']] = $mapping; } + /** + * INTERNAL: + * Adds an association mapping without completing/validating it. + * This is mainly used to add inherited association mappings to derived classes. + * + * @param AssociationMapping $mapping + */ public function addAssociationMapping(AssociationMapping $mapping) { $this->_storeAssociationMapping($mapping); @@ -1320,7 +1296,7 @@ class ClassMetadata * class is queried in a class hierarchy that uses the JOINED inheritance mapping * strategy. * - * This options does only apply to the JOINED inheritance mapping strategy. + * This options does only apply to the JOINED inheritance mapping strategy. * * @param boolean $bool * @see getJoinSubClasses() @@ -1415,7 +1391,7 @@ class ClassMetadata } /** - * Adds a lifecycle callback for Entities of this class. + * Adds a lifecycle callback for entities of this class. * * Note: If the same callback is registered more than once, the old one * will be overridden. @@ -1444,16 +1420,6 @@ class ClassMetadata $this->_discriminatorColumn = $columnDef; } - /** - * - * @param $fieldName - * @param $mapping - */ - /*public function addFieldMapping($fieldName, array $mapping) - { - $this->_fieldMappings[$fieldName] = $mapping; - }*/ - /** * Gets the discriminator column definition. * diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index 58627c6ec..3d5ce3061 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -22,7 +22,7 @@ namespace Doctrine\ORM\Mapping\Driver; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Exceptions\MappingException; +use Doctrine\ORM\Mapping\MappingException; /* Addendum annotation reflection extensions */ if ( ! class_exists('\Addendum', false)) { @@ -134,6 +134,7 @@ class AnnotationDriver $metadata->mapOneToMany($mapping); } else if ($manyToOneAnnot = $property->getAnnotation('DoctrineManyToOne')) { $mapping['joinColumns'] = $joinColumns; + $mapping['cascade'] = $manyToOneAnnot->cascade; $mapping['targetEntity'] = $manyToOneAnnot->targetEntity; $metadata->mapManyToOne($mapping); } else if ($manyToManyAnnot = $property->getAnnotation('DoctrineManyToMany')) { @@ -150,6 +151,7 @@ class AnnotationDriver $mapping['joinTable'] = $joinTable; $mapping['targetEntity'] = $manyToManyAnnot->targetEntity; $mapping['mappedBy'] = $manyToManyAnnot->mappedBy; + $mapping['cascade'] = $manyToManyAnnot->cascade; $metadata->mapManyToMany($mapping); } diff --git a/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php b/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php index d1c8d86e6..521d54801 100644 --- a/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php +++ b/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php @@ -21,8 +21,6 @@ namespace Doctrine\ORM\Mapping; -use Doctrine\ORM\Exceptions\MappingException; - /** * A many-to-many mapping describes the mapping between two collections of * entities. diff --git a/lib/Doctrine/ORM/Exceptions/MappingException.php b/lib/Doctrine/ORM/Mapping/MappingException.php similarity index 98% rename from lib/Doctrine/ORM/Exceptions/MappingException.php rename to lib/Doctrine/ORM/Mapping/MappingException.php index 3c709b4c2..a581f76ed 100644 --- a/lib/Doctrine/ORM/Exceptions/MappingException.php +++ b/lib/Doctrine/ORM/Mapping/MappingException.php @@ -19,7 +19,7 @@ * . */ -namespace Doctrine\ORM\Exceptions; +namespace Doctrine\ORM\Mapping; /** * A MappingException indicates that something is wrong with the mapping setup. diff --git a/lib/Doctrine/ORM/Mapping/OneToManyMapping.php b/lib/Doctrine/ORM/Mapping/OneToManyMapping.php index 35e654170..01bad0734 100644 --- a/lib/Doctrine/ORM/Mapping/OneToManyMapping.php +++ b/lib/Doctrine/ORM/Mapping/OneToManyMapping.php @@ -21,8 +21,6 @@ namespace Doctrine\ORM\Mapping; -use Doctrine\ORM\Exceptions\MappingException; - /** * Represents a one-to-many mapping. * @@ -53,7 +51,7 @@ class OneToManyMapping extends AssociationMapping //protected $_sourceKeysToTargetForeignKeys; /** Whether to delete orphaned elements (removed from the collection) */ - protected $_deleteOrphans = false; + private $_deleteOrphans = false; /** * Initializes a new OneToManyMapping. @@ -96,9 +94,8 @@ class OneToManyMapping extends AssociationMapping } /** - * Whether the association is one-to-many. + * {@inheritdoc} * - * @return boolean TRUE if the association is one-to-many, FALSE otherwise. * @override */ public function isOneToMany() diff --git a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php index 5adb7029d..eb9f20eb5 100644 --- a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php +++ b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php @@ -21,8 +21,6 @@ namespace Doctrine\ORM\Mapping; -use Doctrine\ORM\Exceptions\MappingException; - /** * A one-to-one mapping describes a uni-directional mapping from one entity * to another entity. diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index 6c4fba6db..9a3b341d5 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -48,7 +48,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection * * @var string */ - private $_entityBaseType; + private $_type; /** * A snapshot of the collection at the moment it was fetched from the database. @@ -99,7 +99,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection * Hydration flag. * * @var boolean - * @see _setHydrationFlag() + * @see setHydrationFlag() */ private $_hydrationFlag; @@ -119,12 +119,12 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection /** * Creates a new persistent collection. */ - public function __construct(EntityManager $em, $entityBaseType, array $data = array(), $keyField = null) + public function __construct(EntityManager $em, $type, array $data = array(), $keyField = null) { parent::__construct($data); - $this->_entityBaseType = $entityBaseType; + $this->_type = $type; $this->_em = $em; - $this->_ownerClass = $em->getClassMetadata($entityBaseType); + $this->_ownerClass = $em->getClassMetadata($type); if ($keyField !== null) { if ( ! $this->_ownerClass->hasField($keyField)) { throw new DoctrineException("Invalid field '$keyField' can't be used as key."); @@ -162,7 +162,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection * @param object $entity * @param AssociationMapping $assoc */ - public function _setOwner($entity, AssociationMapping $assoc) + public function setOwner($entity, AssociationMapping $assoc) { $this->_owner = $entity; $this->_association = $assoc; @@ -289,7 +289,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection * * @param boolean $bool */ - public function _setHydrationFlag($bool) + public function setHydrationFlag($bool) { $this->_hydrationFlag = $bool; } @@ -301,9 +301,9 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection * when a fetched collection has three elements, then two of those * are being removed the diff would contain one element. */ - public function _takeSnapshot() + public function takeSnapshot() { - $this->_snapshot = $this->_data; + $this->_snapshot = $this->_elements; } /** @@ -312,7 +312,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection * * @return array The last snapshot of the elements. */ - public function _getSnapshot() + public function getSnapshot() { return $this->_snapshot; } @@ -325,7 +325,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection */ public function getDeleteDiff() { - return array_udiff($this->_snapshot, $this->_data, array($this, '_compareRecords')); + return array_udiff($this->_snapshot, $this->_elements, array($this, '_compareRecords')); } /** @@ -335,7 +335,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection */ public function getInsertDiff() { - return array_udiff($this->_data, $this->_snapshot, array($this, '_compareRecords')); + return array_udiff($this->_elements, $this->_snapshot, array($this, '_compareRecords')); } /** diff --git a/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php b/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php index 27047fd72..60703727e 100644 --- a/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php +++ b/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php @@ -66,20 +66,36 @@ abstract class AbstractCollectionPersister if ($coll->getMapping()->isInverseSide()) { return; // ignore inverse side } - $sql = $this->_getDeleteSql($coll); $this->_conn->exec($sql, $this->_getDeleteSqlParameters($coll)); } + /** + * Gets the SQL statement for deleting the given collection. + * + * @param PersistentCollection $coll + */ abstract protected function _getDeleteSql(PersistentCollection $coll); + + /** + * Gets the SQL parameters for the corresponding SQL statement to delete + * the given collection. + * + * @param PersistentCollection $coll + */ abstract protected function _getDeleteSqlParameters(PersistentCollection $coll); + /** + * Updates the given collection, synchronizing it's state with the database + * by inserting, updating and deleting individual elements. + * + * @param PersistentCollection $coll + */ public function update(PersistentCollection $coll) { if ($coll->getMapping()->isInverseSide()) { return; // ignore inverse side } - $this->deleteRows($coll); //$this->updateRows($coll); $this->insertRows($coll); @@ -113,6 +129,13 @@ abstract class AbstractCollectionPersister */ abstract protected function _getDeleteRowSql(PersistentCollection $coll); + /** + * Gets the SQL parameters for the corresponding SQL statement to delete the given + * element from the given collection. + * + * @param PersistentCollection $coll + * @param mixed $element + */ abstract protected function _getDeleteRowSqlParameters(PersistentCollection $coll, $element); /** @@ -128,7 +151,14 @@ abstract class AbstractCollectionPersister * @param PersistentCollection $coll */ abstract protected function _getInsertRowSql(PersistentCollection $coll); - + + /** + * Gets the SQL parameters for the corresponding SQL statement to insert the given + * element of the given collection into the database. + * + * @param PersistentCollection $coll + * @param mixed $element + */ abstract protected function _getInsertRowSqlParameters(PersistentCollection $coll, $element); } diff --git a/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php b/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php index f32bae99c..378044a08 100644 --- a/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php @@ -21,6 +21,9 @@ namespace Doctrine\ORM\Persisters; +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\Mapping\ClassMetadata; + /** * Base class for all EntityPersisters. * @@ -65,7 +68,7 @@ abstract class AbstractEntityPersister * that uses the given EntityManager and persists instances of the class described * by the given class metadata descriptor. */ - public function __construct(\Doctrine\ORM\EntityManager $em, \Doctrine\ORM\Mapping\ClassMetadata $classMetadata) + public function __construct(EntityManager $em, ClassMetadata $classMetadata) { $this->_em = $em; $this->_entityName = $classMetadata->getClassName(); @@ -112,8 +115,10 @@ abstract class AbstractEntityPersister */ public function delete($entity) { - $id = array_combine($this->_classMetadata->getIdentifierFieldNames(), - $this->_em->getUnitOfWork()->getEntityIdentifier($entity)); + $id = array_combine( + $this->_classMetadata->getIdentifierFieldNames(), + $this->_em->getUnitOfWork()->getEntityIdentifier($entity) + ); $this->_conn->delete($this->_classMetadata->getTableName(), $id); } @@ -132,7 +137,7 @@ abstract class AbstractEntityPersister * * @param string $fieldName * @return string - * @todo Consider using 'inherited' => 'ClassName' to make the lookup simpler. + * @todo Move to ClassMetadata? */ public function getOwningClass($fieldName) { @@ -180,11 +185,9 @@ abstract class AbstractEntityPersister if ($this->_classMetadata->hasAssociation($field)) { $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; @@ -192,7 +195,6 @@ abstract class AbstractEntityPersister $result[$sourceColumn] = $otherClass->getReflectionProperty( $otherClass->getFieldName($targetColumn))->getValue($newVal); } - } } else if (is_null($newVal)) { $result[$columnName] = null; diff --git a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php index 1164cbe31..9f69c098d 100644 --- a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php @@ -65,9 +65,7 @@ class ManyToManyPersister extends AbstractCollectionPersister * @override */ protected function _getUpdateRowSql(PersistentCollection $coll) - { - - } + {} /** * {@inheritdoc} @@ -96,7 +94,7 @@ class ManyToManyPersister extends AbstractCollectionPersister $this->_uow->getEntityIdentifier($coll->getOwner()), $this->_uow->getEntityIdentifier($element) ); - var_dump($params); + //var_dump($params); return $params; } diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 825317773..5905b5010 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -23,7 +23,6 @@ namespace Doctrine\ORM\Query; use Doctrine\ORM\Query\AST; -use Doctrine\ORM\Exceptions\QueryException; use Doctrine\ORM\Query\Exec; /** diff --git a/lib/Doctrine/ORM/Exceptions/QueryException.php b/lib/Doctrine/ORM/Query/QueryException.php similarity index 87% rename from lib/Doctrine/ORM/Exceptions/QueryException.php rename to lib/Doctrine/ORM/Query/QueryException.php index 8632845ab..13cf3e6e9 100644 --- a/lib/Doctrine/ORM/Exceptions/QueryException.php +++ b/lib/Doctrine/ORM/Query/QueryException.php @@ -4,7 +4,7 @@ * and open the template in the editor. */ -namespace Doctrine\ORM\Exceptions; +namespace Doctrine\ORM\Query; /** * Description of QueryException diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 3402550a0..4e5563a13 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -120,41 +120,35 @@ class UnitOfWork private $_scheduledForDirtyCheck = array(); /** - * A list of all new entities that need to be INSERTed. + * A list of all pending entity insertions. * * @var array - * @todo Index by class name. - * @todo Rename to _inserts? */ - private $_newEntities = array(); + private $_entityInsertions = array(); /** - * A list of all dirty entities that need to be UPDATEd. + * A list of all pending entity updates. * * @var array - * @todo Rename to _updates? */ - private $_dirtyEntities = array(); + private $_entityUpdates = array(); /** - * A list of all deleted entities. - * Removed entities are entities that are "scheduled for removal" but have - * not yet been removed from the database. + * A list of all pending entity deletions. * * @var array - * @todo Rename to _deletions? */ - private $_deletedEntities = array(); + private $_entityDeletions = array(); /** - * All collection deletions. + * All pending collection deletions. * * @var array */ private $_collectionDeletions = array(); /** - * All collection creations. + * All pending collection creations. * * @var array */ @@ -226,9 +220,9 @@ class UnitOfWork // Compute changes done since last commit $this->computeChangeSets(); - if (empty($this->_newEntities) && - empty($this->_deletedEntities) && - empty($this->_dirtyEntities) && + if (empty($this->_entityInsertions) && + empty($this->_entityDeletions) && + empty($this->_entityUpdates) && empty($this->_collectionUpdates) && empty($this->_collectionDeletions)) { return; // Nothing to do. @@ -267,13 +261,13 @@ class UnitOfWork // Take new snapshots from visited collections foreach ($this->_visitedCollections as $coll) { - $coll->_takeSnapshot(); + $coll->takeSnapshot(); } // Clear up - $this->_newEntities = array(); - $this->_dirtyEntities = array(); - $this->_deletedEntities = array(); + $this->_entityInsertions = array(); + $this->_entityUpdates = array(); + $this->_entityDeletions = array(); $this->_entityChangeSets = array(); $this->_collectionUpdates = array(); $this->_collectionDeletions = array(); @@ -341,15 +335,14 @@ class UnitOfWork && ! ($actualData[$name] instanceof PersistentCollection)) { //TODO: If $actualData[$name] is Collection then unwrap the array $assoc = $class->getAssociationMapping($name); - if ($assoc->isOwningSide()) { - // Inject PersistentCollection - $coll = new PersistentCollection($this->_em, $assoc->getTargetEntityName(), - $actualData[$name] ? $actualData[$name] : array()); - $coll->_setOwner($entity, $assoc); - if ( ! $coll->isEmpty()) $coll->setDirty(true); - $class->getReflectionProperty($name)->setValue($entity, $coll); - $actualData[$name] = $coll; - } + echo PHP_EOL . "INJECTING PCOLL into $name" . PHP_EOL; + // Inject PersistentCollection + $coll = new PersistentCollection($this->_em, $assoc->getTargetEntityName(), + $actualData[$name] ? $actualData[$name] : array()); + $coll->setOwner($entity, $assoc); + if ( ! $coll->isEmpty()) $coll->setDirty(true); + $class->getReflectionProperty($name)->setValue($entity, $coll); + $actualData[$name] = $coll; } } @@ -381,7 +374,7 @@ class UnitOfWork $assoc = $class->getAssociationMapping($propName); if ($assoc->isOneToOne() && $assoc->isOwningSide()) { $entityIsDirty = true; - } else if (/*is_null($actualValue) && */$orgValue instanceof PersistentCollection) { + } else if ($orgValue instanceof PersistentCollection) { // A PersistentCollection was de-referenced, so delete it. if ( ! in_array($orgValue, $this->_collectionDeletions, true)) { $this->_collectionDeletions[] = $orgValue; @@ -394,7 +387,7 @@ class UnitOfWork } if ($changeSet) { if ($entityIsDirty) { - $this->_dirtyEntities[$oid] = $entity; + $this->_entityUpdates[$oid] = $entity; } $this->_entityChangeSets[$oid] = $changeSet; $this->_originalEntityData[$oid] = $actualData; @@ -418,19 +411,28 @@ class UnitOfWork /** * Computes the changes of an association. * - * @param $assoc - * @param $value + * @param AssociationMapping $assoc + * @param mixed $value The value of the association. */ private function _computeAssociationChanges($assoc, $value) { - /*if ( ! $assoc->isCascadeSave()) { - return; // "Persistence by reachability" only if save cascade enabled - }*/ + if ($value instanceof PersistentCollection && $value->isDirty()) { + if ($assoc->isOwningSide()) { + $this->_collectionUpdates[] = $value; + } + $this->_visitedCollections[] = $value; + } + if ( ! $assoc->isCascadeSave()) { + echo "NOT CASCADING INTO " . $assoc->getSourceFieldName() . PHP_EOL; + return; // "Persistence by reachability" only if save cascade specified + } + + // Look through the entities, and in any of their associations, for transient + // enities, recursively. ("Persistence by reachability") if ($assoc->isOneToOne()) { $value = array($value); } - $targetClass = $this->_em->getClassMetadata($assoc->getTargetEntityName()); foreach ($value as $entry) { $state = $this->getEntityState($entry); @@ -450,28 +452,21 @@ class UnitOfWork $this->addToIdentityMap($entry); } - // NEW entities are INSERTed within the current unit of work. + // Collect the original data and changeset, recursing into associations. $data = array(); $changeSet = array(); foreach ($targetClass->getReflectionProperties() as $name => $refProp) { $data[$name] = $refProp->getValue($entry); $changeSet[$name] = array(null, $data[$name]); - // -- - /*if ($targetClass->isCollectionValuedAssociation($name) && ! ($data[$name] instanceof PersistentCollection)) { - // Inject PersistentCollection - //TODO: If $actualData[$name] is Collection then unwrap the array - $assoc = $targetClass->getAssociationMapping($name); - $coll = new PersistentCollection($this->_em, $assoc->getTargetEntityName(), - $data[$name] ? $data[$name] : array()); - $coll->_setOwner($entry, $assoc); - if ( ! $coll->isEmpty()) $coll->setDirty(true); - $targetClass->getReflectionProperty($name)->setValue($entry, $coll); - $data[$name] = $coll; - }*/ - //-- + if ($targetClass->hasAssociation($name)) { + //echo "RECURSING INTO $name" . PHP_EOL; + //TODO: Prevent infinite recursion + $this->_computeAssociationChanges($targetClass->getAssociationMapping($name), $data[$name]); + } } - $this->_newEntities[$oid] = $entry; + // NEW entities are INSERTed within the current unit of work. + $this->_entityInsertions[$oid] = $entry; $this->_entityChangeSets[$oid] = $changeSet; $this->_originalEntityData[$oid] = $data; } else if ($state == self::STATE_DELETED) { @@ -480,13 +475,6 @@ class UnitOfWork // MANAGED associated entities are already taken into account // during changeset calculation anyway, since they are in the identity map. } - - if ($value instanceof PersistentCollection && $value->isDirty()) { - if ($assoc->isOwningSide()) { - $this->_collectionUpdates[] = $value; - } - $this->_visitedCollections[] = $value; - } } /** @@ -502,7 +490,7 @@ class UnitOfWork // Same for update/delete. $className = $class->getClassName(); $persister = $this->getEntityPersister($className); - foreach ($this->_newEntities as $entity) { + foreach ($this->_entityInsertions as $entity) { if (get_class($entity) == $className) { $returnVal = $persister->insert($entity); if ( ! is_null($returnVal)) { @@ -528,7 +516,7 @@ class UnitOfWork { $className = $class->getClassName(); $persister = $this->getEntityPersister($className); - foreach ($this->_dirtyEntities as $entity) { + foreach ($this->_entityUpdates as $entity) { if (get_class($entity) == $className) { $persister->update($entity); } @@ -544,7 +532,7 @@ class UnitOfWork { $className = $class->getClassName(); $persister = $this->getEntityPersister($className); - foreach ($this->_deletedEntities as $entity) { + foreach ($this->_entityDeletions as $entity) { if (get_class($entity) == $className) { $persister->delete($entity); } @@ -557,12 +545,12 @@ class UnitOfWork * @return array */ private function _getCommitOrder(array $entityChangeSet = null) - { + { if (is_null($entityChangeSet)) { $entityChangeSet = array_merge( - $this->_newEntities, - $this->_dirtyEntities, - $this->_deletedEntities); + $this->_entityInsertions, + $this->_entityUpdates, + $this->_entityDeletions); } // TODO: We can cache computed commit orders in the metadata cache! @@ -590,7 +578,7 @@ class UnitOfWork if ($assocMapping->isOwningSide()) { $targetClass = $this->_em->getClassMetadata($assocMapping->getTargetEntityName()); $targetClassName = $targetClass->getClassName(); - // if the target class does not yet have a node, create it + // If the target class does not yet have a node, create it if ( ! $this->_commitOrderCalculator->hasNodeWithKey($targetClassName)) { $this->_commitOrderCalculator->addNodeWithItem( $targetClassName, // index/key @@ -616,17 +604,17 @@ class UnitOfWork { $oid = spl_object_hash($entity); - if (isset($this->_dirtyEntities[$oid])) { + if (isset($this->_entityUpdates[$oid])) { throw new DoctrineException("Dirty object can't be registered as new."); } - if (isset($this->_deletedEntities[$oid])) { + if (isset($this->_entityDeletions[$oid])) { throw new DoctrineException("Removed object can't be registered as new."); } - if (isset($this->_newEntities[$oid])) { + if (isset($this->_entityInsertions[$oid])) { throw new DoctrineException("Object already registered as new. Can't register twice."); } - $this->_newEntities[$oid] = $entity; + $this->_entityInsertions[$oid] = $entity; if (isset($this->_entityIdentifiers[$oid])) { $this->addToIdentityMap($entity); } @@ -641,7 +629,7 @@ class UnitOfWork */ public function isRegisteredNew($entity) { - return isset($this->_newEntities[spl_object_hash($entity)]); + return isset($this->_entityInsertions[spl_object_hash($entity)]); } /** @@ -657,12 +645,12 @@ class UnitOfWork throw new DoctrineException("Entity without identity " . "can't be registered as dirty."); } - if (isset($this->_deletedEntities[$oid])) { + if (isset($this->_entityDeletions[$oid])) { throw new DoctrineException("Removed object can't be registered as dirty."); } - if ( ! isset($this->_dirtyEntities[$oid]) && ! isset($this->_newEntities[$oid])) { - $this->_dirtyEntities[$oid] = $entity; + if ( ! isset($this->_entityUpdates[$oid]) && ! isset($this->_entityInsertions[$oid])) { + $this->_entityUpdates[$oid] = $entity; } } @@ -677,7 +665,7 @@ class UnitOfWork */ public function isRegisteredDirty($entity) { - return isset($this->_dirtyEntities[spl_object_hash($entity)]); + return isset($this->_entityUpdates[spl_object_hash($entity)]); } /** @@ -695,16 +683,16 @@ class UnitOfWork $this->removeFromIdentityMap($entity); $className = get_class($entity); - if (isset($this->_newEntities[$oid])) { - unset($this->_newEntities[$oid]); + if (isset($this->_entityInsertions[$oid])) { + unset($this->_entityInsertions[$oid]); return; // entity has not been persisted yet, so nothing more to do. } - if (isset($this->_dirtyEntities[$oid])) { - unset($this->_dirtyEntities[$oid]); + if (isset($this->_entityUpdates[$oid])) { + unset($this->_entityUpdates[$oid]); } - if ( ! isset($this->_deletedEntities[$oid])) { - $this->_deletedEntities[$oid] = $entity; + if ( ! isset($this->_entityDeletions[$oid])) { + $this->_entityDeletions[$oid] = $entity; } } @@ -718,7 +706,7 @@ class UnitOfWork */ public function isRegisteredRemoved($entity) { - return isset($this->_deletedEntities[spl_object_hash($entity)]); + return isset($this->_entityDeletions[spl_object_hash($entity)]); } /** @@ -732,8 +720,8 @@ class UnitOfWork { $oid = spl_object_hash($entity); $this->removeFromIdentityMap($entity); - unset($this->_newEntities[$oid], $this->_dirtyEntities[$oid], - $this->_deletedEntities[$oid], $this->_entityIdentifiers[$oid], + unset($this->_entityInsertions[$oid], $this->_entityUpdates[$oid], + $this->_entityDeletions[$oid], $this->_entityIdentifiers[$oid], $this->_entityStates[$oid]); } @@ -747,9 +735,9 @@ class UnitOfWork public function isEntityRegistered($entity) { $oid = spl_object_hash($entity); - return isset($this->_newEntities[$oid]) || - isset($this->_dirtyEntities[$oid]) || - isset($this->_deletedEntities[$oid]); + return isset($this->_entityInsertions[$oid]) || + isset($this->_entityUpdates[$oid]) || + isset($this->_entityDeletions[$oid]); } /** @@ -774,9 +762,9 @@ class UnitOfWork } else { $numDetached = count($this->_identityMap); $this->_identityMap = array(); - $this->_newEntities = array(); - $this->_dirtyEntities = array(); - $this->_deletedEntities = array(); + $this->_entityInsertions = array(); + $this->_entityUpdates = array(); + $this->_entityDeletions = array(); } return $numDetached; @@ -817,7 +805,7 @@ class UnitOfWork { $oid = spl_object_hash($entity); if ( ! isset($this->_entityStates[$oid])) { - if (isset($this->_entityIdentifiers[$oid]) && ! isset($this->_newEntities[$oid])) { + if (isset($this->_entityIdentifiers[$oid]) && ! isset($this->_entityInsertions[$oid])) { $this->_entityStates[$oid] = self::STATE_DETACHED; } else { $this->_entityStates[$oid] = self::STATE_NEW; @@ -951,8 +939,8 @@ class UnitOfWork foreach ($commitOrder as $class) { $this->_executeInserts($class); } - // remove them from _newEntities and _entityChangeSets - $this->_newEntities = array_diff_key($this->_newEntities, $insertNow); + // remove them from _entityInsertions and _entityChangeSets + $this->_entityInsertions = array_diff_key($this->_entityInsertions, $insertNow); $this->_entityChangeSets = array_diff_key($this->_entityChangeSets, $insertNow); } } @@ -1007,7 +995,7 @@ class UnitOfWork // entity becomes managed again if ($this->isRegisteredRemoved($entity)) { //TODO: better a method for this? - unset($this->_deletedEntities[$oid]); + unset($this->_entityDeletions[$oid]); } else { //FIXME: There's more to think of here... $this->registerNew($entity); @@ -1384,8 +1372,4 @@ class UnitOfWork } return $this->_collectionPersisters[$type]; } -} - - - - +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/VirtualProxy.php b/lib/Doctrine/ORM/VirtualProxy.php index 87732dcbb..cd90ff877 100644 --- a/lib/Doctrine/ORM/VirtualProxy.php +++ b/lib/Doctrine/ORM/VirtualProxy.php @@ -1,5 +1,27 @@ . + */ + +namespace Doctrine\ORM; + +use Doctrine\ORM\Mapping\AssociationMapping; /** * Represents a virtual proxy that is used for lazy to-one associations. @@ -7,7 +29,7 @@ * @author robo * @since 2.0 */ -class Doctrine_ORM_VirtualProxy +class VirtualProxy { private $_assoc; private $_refProp; @@ -22,7 +44,7 @@ class Doctrine_ORM_VirtualProxy * @param $assoc * @param $refProp */ - public function __construct($owner, Doctrine_ORM_Mapping_AssociationMapping $assoc, ReflectionProperty $refProp) + public function __construct($owner, AssociationMapping $assoc, \ReflectionProperty $refProp) { $this->_owner = $owner; $this->_assoc = $assoc; diff --git a/tests/Doctrine/Tests/Common/AllTests.php b/tests/Doctrine/Tests/Common/AllTests.php index 0279b62b3..52f1886d5 100644 --- a/tests/Doctrine/Tests/Common/AllTests.php +++ b/tests/Doctrine/Tests/Common/AllTests.php @@ -10,9 +10,6 @@ if (!defined('PHPUnit_MAIN_METHOD')) { require_once __DIR__ . '/../TestInit.php'; -// Suites -#require_once 'Common/Collections/AllTests.php'; - class AllTests { public static function main() @@ -24,6 +21,8 @@ class AllTests { $suite = new \Doctrine\Tests\DoctrineTestSuite('Doctrine Common Tests'); + $suite->addTestSuite('Doctrine\Tests\Common\EventManagerTest'); + $suite->addTest(Collections\AllTests::suite()); return $suite; diff --git a/tests/Doctrine/Tests/Common/EventManagerTest.php b/tests/Doctrine/Tests/Common/EventManagerTest.php new file mode 100644 index 000000000..f1deddd87 --- /dev/null +++ b/tests/Doctrine/Tests/Common/EventManagerTest.php @@ -0,0 +1,72 @@ +_eventManager = new EventManager; + $this->_preFooInvoked = false; + $this->_postFooInvoked = false; + } + + public function testInitialState() + { + $this->assertEquals(array(), $this->_eventManager->getListeners()); + $this->assertFalse($this->_eventManager->hasListeners(self::preFoo)); + $this->assertFalse($this->_eventManager->hasListeners(self::postFoo)); + } + + public function testAddEventListener() + { + $this->_eventManager->addEventListener(array('preFoo', 'postFoo'), $this); + $this->assertTrue($this->_eventManager->hasListeners(self::preFoo)); + $this->assertTrue($this->_eventManager->hasListeners(self::postFoo)); + $this->assertEquals(1, count($this->_eventManager->getListeners(self::preFoo))); + $this->assertEquals(1, count($this->_eventManager->getListeners(self::postFoo))); + $this->assertEquals(2, count($this->_eventManager->getListeners())); + } + + public function testDispatchEvent() + { + $this->_eventManager->addEventListener(array('preFoo', 'postFoo'), $this); + $this->_eventManager->dispatchEvent(self::preFoo); + $this->assertTrue($this->_preFooInvoked); + $this->assertFalse($this->_postFooInvoked); + } + + + /* Listener methods */ + + public function preFoo(EventArgs $e) + { + $this->_preFooInvoked = true; + } + + public function postFoo(EventArgs $e) + { + $this->_postFooInvoked = true; + } +} + diff --git a/tests/Doctrine/Tests/Models/CMS/CmsUser.php b/tests/Doctrine/Tests/Models/CMS/CmsUser.php index c48b1e0b1..c0c57c28f 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsUser.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsUser.php @@ -39,7 +39,7 @@ class CmsUser */ public $address; /** - * @DoctrineManyToMany(targetEntity="CmsGroup") + * @DoctrineManyToMany(targetEntity="CmsGroup", cascade={"save"}) * @DoctrineJoinTable(name="cms_users_groups", joinColumns={{"name"="user_id", "referencedColumnName"="id"}}, inverseJoinColumns={{"name"="group_id", "referencedColumnName"="id"}}) diff --git a/tests/Doctrine/Tests/ORM/EntityManagerTest.php b/tests/Doctrine/Tests/ORM/EntityManagerTest.php index 60ee4a2d1..b41915ebd 100644 --- a/tests/Doctrine/Tests/ORM/EntityManagerTest.php +++ b/tests/Doctrine/Tests/ORM/EntityManagerTest.php @@ -22,7 +22,7 @@ class EntityManagerTest extends \Doctrine\Tests\OrmTestCase try { $this->_em->setFlushMode('foobar'); $this->fail("Setting invalid flushmode did not trigger exception."); - } catch (\Doctrine\ORM\Exceptions\EntityManagerException $expected) {} + } catch (\Doctrine\ORM\EntityManagerException $expected) {} $this->_em->setFlushMode($prev); } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/BasicCRUDTest.php b/tests/Doctrine/Tests/ORM/Functional/BasicCRUDTest.php index 7ad520829..680b8c3a2 100644 --- a/tests/Doctrine/Tests/ORM/Functional/BasicCRUDTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/BasicCRUDTest.php @@ -157,6 +157,8 @@ class BasicCRUDTest extends \Doctrine\Tests\OrmFunctionalTestCase { public function testManyToManyCollectionClearing() { + echo PHP_EOL . "MANY-MANY" . PHP_EOL; + $user = new CmsUser; $user->name = 'Guilherme'; $user->username = 'gblanco'; @@ -171,11 +173,17 @@ class BasicCRUDTest extends \Doctrine\Tests\OrmFunctionalTestCase { $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) + $this->_em->flush(); + + // Check that there are indeed 10 links in the association table + $count = $this->_em->getConnection()->execute("SELECT COUNT(*) FROM cms_users_groups", + array())->fetchColumn(); + $this->assertEquals(10, $count); //$user->groups->clear(); unset($user->groups); + echo PHP_EOL . "FINAL FLUSH" . PHP_EOL; $this->_em->flush(); // Check that the links in the association table have been deleted diff --git a/tests/Doctrine/Tests/ORM/Hydration/SingleScalarHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/SingleScalarHydratorTest.php index 3e78aca62..ea657e4cf 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/SingleScalarHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/SingleScalarHydratorTest.php @@ -89,7 +89,7 @@ class SingleScalarHydratorTest extends HydrationTest $result = $hydrator->hydrateall($stmt, $this->_createParserResult( $queryComponents, $tableAliasMap)); $this->fail(); - } catch (\Doctrine\ORM\Exceptions\HydrationException $ex) {} + } catch (\Doctrine\ORM\Internal\Hydration\HydrationException $ex) {} } }