3 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15 * This software consists of voluntary contributions made by many individuals
16 * and is licensed under the MIT license. For more information, see
17 * <http://www.doctrine-project.org>.
20 namespace Doctrine\ORM;
23 Doctrine\Common\EventManager,
24 Doctrine\Common\Persistence\ObjectManager,
25 Doctrine\DBAL\Connection,
26 Doctrine\DBAL\LockMode,
27 Doctrine\ORM\Mapping\ClassMetadata,
28 Doctrine\ORM\Mapping\ClassMetadataFactory,
29 Doctrine\ORM\Query\ResultSetMapping,
30 Doctrine\ORM\Proxy\ProxyFactory,
31 Doctrine\ORM\Query\FilterCollection;
34 * The EntityManager is the central access point to ORM functionality.
37 * @author Benjamin Eberlei <kontakt@beberlei.de>
38 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
39 * @author Jonathan Wage <jonwage@gmail.com>
40 * @author Roman Borschel <roman@code-factory.org>
42 class EntityManager implements ObjectManager
45 * The used Configuration.
47 * @var \Doctrine\ORM\Configuration
52 * The database connection used by the EntityManager.
54 * @var \Doctrine\DBAL\Connection
59 * The metadata factory, used to retrieve the ORM metadata of entity classes.
61 * @var \Doctrine\ORM\Mapping\ClassMetadataFactory
63 private $metadataFactory;
66 * The EntityRepository instances.
70 private $repositories = array();
73 * The UnitOfWork used to coordinate object-level transactions.
75 * @var \Doctrine\ORM\UnitOfWork
80 * The event manager that is the central point of the event system.
82 * @var \Doctrine\Common\EventManager
84 private $eventManager;
87 * The maintained (cached) hydrators. One instance per type.
91 private $hydrators = array();
94 * The proxy factory used to create dynamic proxies.
96 * @var \Doctrine\ORM\Proxy\ProxyFactory
98 private $proxyFactory;
101 * The expression builder instance used to generate query expressions.
103 * @var \Doctrine\ORM\Query\Expr
105 private $expressionBuilder;
108 * Whether the EntityManager is closed or not.
112 private $closed = false;
115 * Collection of query filters.
117 * @var Doctrine\ORM\Query\FilterCollection
119 private $filterCollection;
122 * Creates a new EntityManager that operates on the given database connection
123 * and uses the given Configuration and EventManager implementations.
125 * @param \Doctrine\DBAL\Connection $conn
126 * @param \Doctrine\ORM\Configuration $config
127 * @param \Doctrine\Common\EventManager $eventManager
129 protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
132 $this->config = $config;
133 $this->eventManager = $eventManager;
135 $metadataFactoryClassName = $config->getClassMetadataFactoryName();
137 $this->metadataFactory = new $metadataFactoryClassName;
138 $this->metadataFactory->setEntityManager($this);
139 $this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
141 $this->unitOfWork = new UnitOfWork($this);
142 $this->proxyFactory = new ProxyFactory(
144 $config->getProxyDir(),
145 $config->getProxyNamespace(),
146 $config->getAutoGenerateProxyClasses()
151 * Gets the database connection object used by the EntityManager.
153 * @return \Doctrine\DBAL\Connection
155 public function getConnection()
161 * Gets the metadata factory used to gather the metadata of classes.
163 * @return \Doctrine\ORM\Mapping\ClassMetadataFactory
165 public function getMetadataFactory()
167 return $this->metadataFactory;
171 * Gets an ExpressionBuilder used for object-oriented construction of query expressions.
176 * $qb = $em->createQueryBuilder();
177 * $expr = $em->getExpressionBuilder();
178 * $qb->select('u')->from('User', 'u')
179 * ->where($expr->orX($expr->eq('u.id', 1), $expr->eq('u.id', 2)));
182 * @return \Doctrine\ORM\Query\Expr
184 public function getExpressionBuilder()
186 if ($this->expressionBuilder === null) {
187 $this->expressionBuilder = new Query\Expr;
190 return $this->expressionBuilder;
194 * Starts a transaction on the underlying database connection.
196 public function beginTransaction()
198 $this->conn->beginTransaction();
202 * Executes a function in a transaction.
204 * The function gets passed this EntityManager instance as an (optional) parameter.
206 * {@link flush} is invoked prior to transaction commit.
208 * If an exception occurs during execution of the function or flushing or transaction commit,
209 * the transaction is rolled back, the EntityManager closed and the exception re-thrown.
211 * @param callable $func The function to execute transactionally.
212 * @return mixed Returns the non-empty value returned from the closure or true instead
214 public function transactional($func)
216 if (!is_callable($func)) {
217 throw new \InvalidArgumentException('Expected argument of type "callable", got "' . gettype($func) . '"');
220 $this->conn->beginTransaction();
223 $return = call_user_func($func, $this);
226 $this->conn->commit();
228 return $return ?: true;
229 } catch (Exception $e) {
231 $this->conn->rollback();
238 * Commits a transaction on the underlying database connection.
240 public function commit()
242 $this->conn->commit();
246 * Performs a rollback on the underlying database connection.
248 public function rollback()
250 $this->conn->rollback();
254 * Returns the ORM metadata descriptor for a class.
256 * The class name must be the fully-qualified class name without a leading backslash
257 * (as it is returned by get_class($obj)) or an aliased class name.
260 * MyProject\Domain\User
263 * @return \Doctrine\ORM\Mapping\ClassMetadata
264 * @internal Performance-sensitive method.
266 public function getClassMetadata($className)
268 return $this->metadataFactory->getMetadataFor($className);
272 * Creates a new Query object.
274 * @param string $dql The DQL string.
275 * @return \Doctrine\ORM\Query
277 public function createQuery($dql = "")
279 $query = new Query($this);
281 if ( ! empty($dql)) {
282 $query->setDql($dql);
289 * Creates a Query from a named query.
291 * @param string $name
292 * @return \Doctrine\ORM\Query
294 public function createNamedQuery($name)
296 return $this->createQuery($this->config->getNamedQuery($name));
300 * Creates a native SQL query.
303 * @param ResultSetMapping $rsm The ResultSetMapping to use.
304 * @return NativeQuery
306 public function createNativeQuery($sql, ResultSetMapping $rsm)
308 $query = new NativeQuery($this);
310 $query->setSql($sql);
311 $query->setResultSetMapping($rsm);
317 * Creates a NativeQuery from a named native query.
319 * @param string $name
320 * @return \Doctrine\ORM\NativeQuery
322 public function createNamedNativeQuery($name)
324 list($sql, $rsm) = $this->config->getNamedNativeQuery($name);
326 return $this->createNativeQuery($sql, $rsm);
330 * Create a QueryBuilder instance
332 * @return QueryBuilder $qb
334 public function createQueryBuilder()
336 return new QueryBuilder($this);
340 * Flushes all changes to objects that have been queued up to now to the database.
341 * This effectively synchronizes the in-memory state of managed objects with the
344 * If an entity is explicitly passed to this method only this entity and
345 * the cascade-persist semantics + scheduled inserts/removals are synchronized.
347 * @param object $entity
348 * @throws \Doctrine\ORM\OptimisticLockException If a version check on an entity that
349 * makes use of optimistic locking fails.
351 public function flush($entity = null)
353 $this->errorIfClosed();
355 $this->unitOfWork->commit($entity);
359 * Finds an Entity by its identifier.
361 * @param string $entityName
363 * @param integer $lockMode
364 * @param integer $lockVersion
368 public function find($entityName, $id, $lockMode = LockMode::NONE, $lockVersion = null)
370 $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
372 if ( ! is_array($id)) {
373 $id = array($class->identifier[0] => $id);
378 foreach ($class->identifier as $identifier) {
379 if ( ! isset($id[$identifier])) {
380 throw ORMException::missingIdentifierField($class->name, $identifier);
383 $sortedId[$identifier] = $id[$identifier];
386 $unitOfWork = $this->getUnitOfWork();
388 // Check identity map first
389 if (($entity = $unitOfWork->tryGetById($sortedId, $class->rootEntityName)) !== false) {
390 if ( ! ($entity instanceof $class->name)) {
395 case LockMode::OPTIMISTIC:
396 $this->lock($entity, $lockMode, $lockVersion);
399 case LockMode::PESSIMISTIC_READ:
400 case LockMode::PESSIMISTIC_WRITE:
401 $persister = $unitOfWork->getEntityPersister($class->name);
402 $persister->refresh($sortedId, $entity, $lockMode);
406 return $entity; // Hit!
409 $persister = $unitOfWork->getEntityPersister($class->name);
413 return $persister->load($sortedId);
415 case LockMode::OPTIMISTIC:
416 if ( ! $class->isVersioned) {
417 throw OptimisticLockException::notVersioned($class->name);
420 $entity = $persister->load($sortedId);
422 $unitOfWork->lock($entity, $lockMode, $lockVersion);
427 if ( ! $this->getConnection()->isTransactionActive()) {
428 throw TransactionRequiredException::transactionRequired();
431 return $persister->load($sortedId, null, null, array(), $lockMode);
436 * Gets a reference to the entity identified by the given type and identifier
437 * without actually loading it, if the entity is not yet loaded.
439 * @param string $entityName The name of the entity type.
440 * @param mixed $id The entity identifier.
441 * @return object The entity reference.
443 public function getReference($entityName, $id)
445 $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
447 if ( ! is_array($id)) {
448 $id = array($class->identifier[0] => $id);
453 foreach ($class->identifier as $identifier) {
454 if ( ! isset($id[$identifier])) {
455 throw ORMException::missingIdentifierField($class->name, $identifier);
458 $sortedId[$identifier] = $id[$identifier];
461 // Check identity map first, if its already in there just return it.
462 if (($entity = $this->unitOfWork->tryGetById($sortedId, $class->rootEntityName)) !== false) {
463 return ($entity instanceof $class->name) ? $entity : null;
466 if ($class->subClasses) {
467 return $this->find($entityName, $sortedId);
470 if ( ! is_array($sortedId)) {
471 $sortedId = array($class->identifier[0] => $sortedId);
474 $entity = $this->proxyFactory->getProxy($class->name, $sortedId);
476 $this->unitOfWork->registerManaged($entity, $sortedId, array());
482 * Gets a partial reference to the entity identified by the given type and identifier
483 * without actually loading it, if the entity is not yet loaded.
485 * The returned reference may be a partial object if the entity is not yet loaded/managed.
486 * If it is a partial object it will not initialize the rest of the entity state on access.
487 * Thus you can only ever safely access the identifier of an entity obtained through
490 * The use-cases for partial references involve maintaining bidirectional associations
491 * without loading one side of the association or to update an entity without loading it.
492 * Note, however, that in the latter case the original (persistent) entity data will
493 * never be visible to the application (especially not event listeners) as it will
494 * never be loaded in the first place.
496 * @param string $entityName The name of the entity type.
497 * @param mixed $identifier The entity identifier.
498 * @return object The (partial) entity reference.
500 public function getPartialReference($entityName, $identifier)
502 $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
504 // Check identity map first, if its already in there just return it.
505 if (($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) !== false) {
506 return ($entity instanceof $class->name) ? $entity : null;
509 if ( ! is_array($identifier)) {
510 $identifier = array($class->identifier[0] => $identifier);
513 $entity = $class->newInstance();
515 $class->setIdentifierValues($entity, $identifier);
517 $this->unitOfWork->registerManaged($entity, $identifier, array());
518 $this->unitOfWork->markReadOnly($entity);
524 * Clears the EntityManager. All entities that are currently managed
525 * by this EntityManager become detached.
527 * @param string $entityName if given, only entities of this type will get detached
529 public function clear($entityName = null)
531 $this->unitOfWork->clear($entityName);
535 * Closes the EntityManager. All entities that are currently managed
536 * by this EntityManager become detached. The EntityManager may no longer
537 * be used after it is closed.
539 public function close()
543 $this->closed = true;
547 * Tells the EntityManager to make an instance managed and persistent.
549 * The entity will be entered into the database at or before transaction
550 * commit or as a result of the flush operation.
552 * NOTE: The persist operation always considers entities that are not yet known to
553 * this EntityManager as NEW. Do not pass detached entities to the persist operation.
555 * @param object $object The instance to make managed and persistent.
557 public function persist($entity)
559 if ( ! is_object($entity)) {
560 throw ORMInvalidArgumentException::invalidObject('EntityManager#persist()' , $entity);
563 $this->errorIfClosed();
565 $this->unitOfWork->persist($entity);
569 * Removes an entity instance.
571 * A removed entity will be removed from the database at or before transaction commit
572 * or as a result of the flush operation.
574 * @param object $entity The entity instance to remove.
576 public function remove($entity)
578 if ( ! is_object($entity)) {
579 throw ORMInvalidArgumentException::invalidObject('EntityManager#remove()' , $entity);
582 $this->errorIfClosed();
584 $this->unitOfWork->remove($entity);
588 * Refreshes the persistent state of an entity from the database,
589 * overriding any local changes that have not yet been persisted.
591 * @param object $entity The entity to refresh.
593 public function refresh($entity)
595 if ( ! is_object($entity)) {
596 throw ORMInvalidArgumentException::invalidObject('EntityManager#refresh()' , $entity);
599 $this->errorIfClosed();
601 $this->unitOfWork->refresh($entity);
605 * Detaches an entity from the EntityManager, causing a managed entity to
606 * become detached. Unflushed changes made to the entity if any
607 * (including removal of the entity), will not be synchronized to the database.
608 * Entities which previously referenced the detached entity will continue to
611 * @param object $entity The entity to detach.
613 public function detach($entity)
615 if ( ! is_object($entity)) {
616 throw ORMInvalidArgumentException::invalidObject('EntityManager#detach()' , $entity);
619 $this->unitOfWork->detach($entity);
623 * Merges the state of a detached entity into the persistence context
624 * of this EntityManager and returns the managed copy of the entity.
625 * The entity passed to merge will not become associated/managed with this EntityManager.
627 * @param object $entity The detached entity to merge into the persistence context.
628 * @return object The managed copy of the entity.
630 public function merge($entity)
632 if ( ! is_object($entity)) {
633 throw ORMInvalidArgumentException::invalidObject('EntityManager#merge()' , $entity);
636 $this->errorIfClosed();
638 return $this->unitOfWork->merge($entity);
642 * Creates a copy of the given entity. Can create a shallow or a deep copy.
644 * @param object $entity The entity to copy.
645 * @return object The new entity.
646 * @todo Implementation need. This is necessary since $e2 = clone $e1; throws an E_FATAL when access anything on $e:
647 * Fatal error: Maximum function nesting level of '100' reached, aborting!
649 public function copy($entity, $deep = false)
651 throw new \BadMethodCallException("Not implemented.");
655 * Acquire a lock on the given entity.
657 * @param object $entity
658 * @param int $lockMode
659 * @param int $lockVersion
660 * @throws OptimisticLockException
661 * @throws PessimisticLockException
663 public function lock($entity, $lockMode, $lockVersion = null)
665 $this->unitOfWork->lock($entity, $lockMode, $lockVersion);
669 * Gets the repository for an entity class.
671 * @param string $entityName The name of the entity.
672 * @return EntityRepository The repository class.
674 public function getRepository($entityName)
676 $entityName = ltrim($entityName, '\\');
678 if (isset($this->repositories[$entityName])) {
679 return $this->repositories[$entityName];
682 $metadata = $this->getClassMetadata($entityName);
683 $repositoryClassName = $metadata->customRepositoryClassName;
685 if ($repositoryClassName === null) {
686 $repositoryClassName = $this->config->getDefaultRepositoryClassName();
689 $repository = new $repositoryClassName($this, $metadata);
691 $this->repositories[$entityName] = $repository;
697 * Determines whether an entity instance is managed in this EntityManager.
699 * @param object $entity
700 * @return boolean TRUE if this EntityManager currently manages the given entity, FALSE otherwise.
702 public function contains($entity)
704 return $this->unitOfWork->isScheduledForInsert($entity)
705 || $this->unitOfWork->isInIdentityMap($entity)
706 && ! $this->unitOfWork->isScheduledForDelete($entity);
710 * Gets the EventManager used by the EntityManager.
712 * @return \Doctrine\Common\EventManager
714 public function getEventManager()
716 return $this->eventManager;
720 * Gets the Configuration used by the EntityManager.
722 * @return \Doctrine\ORM\Configuration
724 public function getConfiguration()
726 return $this->config;
730 * Throws an exception if the EntityManager is closed or currently not active.
732 * @throws ORMException If the EntityManager is closed.
734 private function errorIfClosed()
737 throw ORMException::entityManagerClosed();
742 * Check if the Entity manager is open or closed.
746 public function isOpen()
748 return (!$this->closed);
752 * Gets the UnitOfWork used by the EntityManager to coordinate operations.
754 * @return \Doctrine\ORM\UnitOfWork
756 public function getUnitOfWork()
758 return $this->unitOfWork;
762 * Gets a hydrator for the given hydration mode.
764 * This method caches the hydrator instances which is used for all queries that don't
765 * selectively iterate over the result.
767 * @param int $hydrationMode
768 * @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator
770 public function getHydrator($hydrationMode)
772 if ( ! isset($this->hydrators[$hydrationMode])) {
773 $this->hydrators[$hydrationMode] = $this->newHydrator($hydrationMode);
776 return $this->hydrators[$hydrationMode];
780 * Create a new instance for the given hydration mode.
782 * @param int $hydrationMode
783 * @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator
785 public function newHydrator($hydrationMode)
787 switch ($hydrationMode) {
788 case Query::HYDRATE_OBJECT:
789 return new Internal\Hydration\ObjectHydrator($this);
791 case Query::HYDRATE_ARRAY:
792 return new Internal\Hydration\ArrayHydrator($this);
794 case Query::HYDRATE_SCALAR:
795 return new Internal\Hydration\ScalarHydrator($this);
797 case Query::HYDRATE_SINGLE_SCALAR:
798 return new Internal\Hydration\SingleScalarHydrator($this);
800 case Query::HYDRATE_SIMPLEOBJECT:
801 return new Internal\Hydration\SimpleObjectHydrator($this);
804 if (($class = $this->config->getCustomHydrationMode($hydrationMode)) !== null) {
805 return new $class($this);
809 throw ORMException::invalidHydrationMode($hydrationMode);
813 * Gets the proxy factory used by the EntityManager to create entity proxies.
815 * @return ProxyFactory
817 public function getProxyFactory()
819 return $this->proxyFactory;
823 * Helper method to initialize a lazy loading proxy or persistent collection.
825 * This method is a no-op for other objects
829 public function initializeObject($obj)
831 $this->unitOfWork->initializeObject($obj);
835 * Factory method to create EntityManager instances.
837 * @param mixed $conn An array with the connection parameters or an existing
838 * Connection instance.
839 * @param Configuration $config The Configuration instance to use.
840 * @param EventManager $eventManager The EventManager instance to use.
841 * @return EntityManager The created EntityManager.
843 public static function create($conn, Configuration $config, EventManager $eventManager = null)
845 if ( ! $config->getMetadataDriverImpl()) {
846 throw ORMException::missingMappingDriverImpl();
850 case (is_array($conn)):
851 $conn = \Doctrine\DBAL\DriverManager::getConnection(
852 $conn, $config, ($eventManager ?: new EventManager())
856 case ($conn instanceof Connection):
857 if ($eventManager !== null && $conn->getEventManager() !== $eventManager) {
858 throw ORMException::mismatchedEventManager();
863 throw new \InvalidArgumentException("Invalid argument: " . $conn);
866 return new EntityManager($conn, $config, $conn->getEventManager());
870 * Gets the enabled filters.
872 * @return FilterCollection The active filter collection.
874 public function getFilters()
876 if (null === $this->filterCollection) {
877 $this->filterCollection = new FilterCollection($this);
880 return $this->filterCollection;
884 * Checks whether the state of the filter collection is clean.
886 * @return boolean True, if the filter collection is clean.
888 public function isFiltersStateClean()
890 return null === $this->filterCollection || $this->filterCollection->isClean();
894 * Checks whether the Entity Manager has filters.
896 * @return True, if the EM has a filter collection.
898 public function hasFilters()
900 return null !== $this->filterCollection;