EntityManager.php 18.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
<?php
/*
 *  $Id$
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
19
 * <http://www.doctrine-project.org>.
20 21
 */

22
namespace Doctrine\ORM;
23

24 25 26 27
use Doctrine\Common\EventManager,
    Doctrine\DBAL\Connection,
    Doctrine\ORM\Mapping\ClassMetadata,
    Doctrine\ORM\Mapping\ClassMetadataFactory,
28
    Doctrine\ORM\Proxy\ProxyFactory;
29

30
/**
31
 * The EntityManager is the central access point to ORM functionality.
32
 *
33
 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
34 35
 * @link    www.doctrine-project.org
 * @since   2.0
36
 * @version $Revision$
37 38 39 40
 * @author  Benjamin Eberlei <kontakt@beberlei.de>
 * @author  Guilherme Blanco <guilhermeblanco@hotmail.com>
 * @author  Jonathan Wage <jonwage@gmail.com>
 * @author  Roman Borschel <roman@code-factory.org>
41
 */
42
class EntityManager
43
{
44 45 46
    /**
     * The used Configuration.
     *
47
     * @var Doctrine\ORM\Configuration
48
     */
49
    private $_config;
50

51 52 53
    /**
     * The database connection used by the EntityManager.
     *
54
     * @var Doctrine\DBAL\Connection
55 56
     */
    private $_conn;
57

58
    /**
59
     * The metadata factory, used to retrieve the ORM metadata of entity classes.
60
     *
61
     * @var Doctrine\ORM\Mapping\ClassMetadataFactory
62 63
     */
    private $_metadataFactory;
64

65 66 67 68 69 70
    /**
     * The EntityRepository instances.
     *
     * @var array
     */
    private $_repositories = array();
71

72
    /**
73
     * The UnitOfWork used to coordinate object-level transactions.
74
     *
75
     * @var Doctrine\ORM\UnitOfWork
76 77
     */
    private $_unitOfWork;
78

79 80 81
    /**
     * The event manager that is the central point of the event system.
     *
82
     * @var Doctrine\Common\EventManager
83 84
     */
    private $_eventManager;
romanb's avatar
romanb committed
85

86 87 88 89 90 91 92
    /**
     * The maintained (cached) hydrators. One instance per type.
     *
     * @var array
     */
    private $_hydrators = array();

93
    /**
romanb's avatar
romanb committed
94
     * The proxy factory used to create dynamic proxies.
95
     *
romanb's avatar
romanb committed
96
     * @var Doctrine\ORM\Proxy\ProxyFactory
97
     */
98
    private $_proxyFactory;
99

100 101 102 103
    /**
     * @var ExpressionBuilder The expression builder instance used to generate query expressions.
     */
    private $_expressionBuilder;
104

105 106 107
    /**
     * Whether the EntityManager is closed or not.
     */
romanb's avatar
romanb committed
108
    private $_closed = false;
109

110
    /**
romanb's avatar
romanb committed
111 112
     * Creates a new EntityManager that operates on the given database connection
     * and uses the given Configuration and EventManager implementations.
113
     *
romanb's avatar
romanb committed
114
     * @param Doctrine\DBAL\Connection $conn
115 116
     * @param Doctrine\ORM\Configuration $config
     * @param Doctrine\Common\EventManager $eventManager
117
     */
118
    protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
119 120
    {
        $this->_conn = $conn;
romanb's avatar
romanb committed
121 122
        $this->_config = $config;
        $this->_eventManager = $eventManager;
123
        $this->_metadataFactory = new ClassMetadataFactory($this);
124
        $this->_metadataFactory->setCacheDriver($this->_config->getMetadataCacheImpl());
125
        $this->_unitOfWork = new UnitOfWork($this);
126 127 128 129
        $this->_proxyFactory = new ProxyFactory($this,
                $config->getProxyDir(),
                $config->getProxyNamespace(),
                $config->getAutoGenerateProxyClasses());
130
    }
131

132 133 134
    /**
     * Gets the database connection object used by the EntityManager.
     *
135
     * @return Doctrine\DBAL\Connection
136 137 138 139 140
     */
    public function getConnection()
    {
        return $this->_conn;
    }
141

romanb's avatar
romanb committed
142
    /**
143
     * Gets the metadata factory used to gather the metadata of classes.
144 145
     *
     * @return Doctrine\ORM\Mapping\ClassMetadataFactory
romanb's avatar
romanb committed
146
     */
147
    public function getMetadataFactory()
romanb's avatar
romanb committed
148
    {
149
        return $this->_metadataFactory;
romanb's avatar
romanb committed
150
    }
151

152 153
    /**
     * Gets an ExpressionBuilder used for object-oriented construction of query expressions.
154
     *
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
     * Example:
     *
     *     [php]
     *     $qb = $em->createQueryBuilder();
     *     $expr = $em->getExpressionBuilder();
     *     $qb->select('u')->from('User', 'u')
     *         ->where($expr->orX($expr->eq('u.id', 1), $expr->eq('u.id', 2)));
     *
     * @return ExpressionBuilder
     */
    public function getExpressionBuilder()
    {
        if ($this->_expressionBuilder === null) {
            $this->_expressionBuilder = new Query\Expr;
        }
        return $this->_expressionBuilder;
    }
172

173
    /**
174
     * Starts a transaction on the underlying database connection.
175 176 177
     */
    public function beginTransaction()
    {
178
        $this->_conn->beginTransaction();
179
    }
180

181
    /**
182
     * Commits a transaction on the underlying database connection.
183
     */
184
    public function commit()
185
    {
186
        $this->_conn->commit();
187
    }
188

189 190 191 192 193 194
    /**
     * Performs a rollback on the underlying database connection and closes the
     * EntityManager as it may now be in a corrupted state.
     */
    public function rollback()
    {
romanb's avatar
romanb committed
195 196
        $this->_conn->rollback();
        $this->close();
197
    }
198 199 200 201

    /**
     * Returns the metadata for a class.
     *
202
     * @return Doctrine\ORM\Mapping\ClassMetadata
203
     * @internal Performance-sensitive method.
204 205
     */
    public function getClassMetadata($className)
206
    {
207 208
        return $this->_metadataFactory->getMetadataFor($className);
    }
209

210
    /**
211
     * Creates a new Query object.
212
     *
213
     * @param string  The DQL string.
214
     * @return Doctrine\ORM\Query
215 216 217
     */
    public function createQuery($dql = "")
    {
218
        $query = new Query($this);
219
        if ( ! empty($dql)) {
220
            $query->setDql($dql);
221 222 223
        }
        return $query;
    }
224

225
    /**
226
     * Creates a Query from a named query.
227
     *
228
     * @param string $name
229
     * @return Doctrine\ORM\Query
230 231 232
     */
    public function createNamedQuery($name)
    {
233
        return $this->createQuery($this->_config->getNamedQuery($name));
234
    }
235

236
    /**
237 238 239
     * Creates a native SQL query.
     *
     * @param string $sql
240
     * @param ResultSetMapping $rsm The ResultSetMapping to use.
241
     * @return NativeQuery
242
     */
243
    public function createNativeQuery($sql, \Doctrine\ORM\Query\ResultSetMapping $rsm)
244
    {
245 246 247 248
        $query = new NativeQuery($this);
        $query->setSql($sql);
        $query->setResultSetMapping($rsm);
        return $query;
249
    }
250

251
    /**
252
     * Creates a NativeQuery from a named native query.
253
     *
254
     * @param string $name
255
     * @return Doctrine\ORM\NativeQuery
256 257 258
     */
    public function createNamedNativeQuery($name)
    {
259 260
        list($sql, $rsm) = $this->_config->getNamedNativeQuery($name);
        return $this->createNativeQuery($sql, $rsm);
261
    }
262

263
    /**
264 265 266
     * Create a QueryBuilder instance
     *
     * @return QueryBuilder $qb
267
     */
268
    public function createQueryBuilder()
269
    {
270
        return new QueryBuilder($this);
271
    }
272

273 274
    /**
     * Flushes all changes to objects that have been queued up to now to the database.
275 276
     * This effectively synchronizes the in-memory state of managed objects with the
     * database.
277 278 279
     */
    public function flush()
    {
280
        $this->_errorIfClosed();
281
        $this->_unitOfWork->commit();
282
    }
283

284
    /**
285
     * Finds an Entity by its identifier.
286
     *
romanb's avatar
romanb committed
287
     * This is just a convenient shortcut for getRepository($entityName)->find($id).
288
     *
289 290
     * @param string $entityName
     * @param mixed $identifier
291
     * @return object
292 293 294 295 296
     */
    public function find($entityName, $identifier)
    {
        return $this->getRepository($entityName)->find($identifier);
    }
297 298 299

    /**
     * Gets a reference to the entity identified by the given type and identifier
300
     * without actually loading it.
301
     *
302 303 304
     * If partial objects are allowed, this method will return a partial object that only
     * has its identifier populated. Otherwise a proxy is returned that automatically
     * loads itself on first access.
305 306 307 308 309
     *
     * @return object The entity reference.
     */
    public function getReference($entityName, $identifier)
    {
310
        $class = $this->_metadataFactory->getMetadataFor($entityName);
311

romanb's avatar
romanb committed
312
        // Check identity map first, if its already in there just return it.
313
        if ($entity = $this->_unitOfWork->tryGetById($identifier, $class->rootEntityName)) {
romanb's avatar
romanb committed
314 315
            return $entity;
        }
316 317
        if ( ! is_array($identifier)) {
            $identifier = array($class->identifier[0] => $identifier);
romanb's avatar
romanb committed
318
        }
319
        $entity = $this->_proxyFactory->getProxy($class->name, $identifier);
320
        $this->_unitOfWork->registerManaged($entity, $identifier, array());
romanb's avatar
romanb committed
321

322 323
        return $entity;
    }
324

325
    /**
326 327 328 329
     * Clears the EntityManager. All entities that are currently managed
     * by this EntityManager become detached.
     *
     * @param string $entityName
330 331 332 333
     */
    public function clear($entityName = null)
    {
        if ($entityName === null) {
334
            $this->_unitOfWork->clear();
335
        } else {
romanb's avatar
romanb committed
336
            //TODO
romanb's avatar
romanb committed
337
            throw new ORMException("EntityManager#clear(\$entityName) not yet implemented.");
338 339
        }
    }
340

341
    /**
342 343 344
     * Closes the EntityManager. All entities that are currently managed
     * by this EntityManager become detached. The EntityManager may no longer
     * be used after it is closed.
345 346 347
     */
    public function close()
    {
348
        $this->clear();
romanb's avatar
romanb committed
349
        $this->_closed = true;
350
    }
351

352
    /**
romanb's avatar
romanb committed
353
     * Tells the EntityManager to make an instance managed and persistent.
354
     *
romanb's avatar
romanb committed
355 356
     * The entity will be entered into the database at or before transaction
     * commit or as a result of the flush operation.
357 358 359
     * 
     * NOTE: The persist operation always considers entities that are not yet known to
     * this EntityManager as NEW. Do not pass detached entities to the persist operation.
360
     *
romanb's avatar
romanb committed
361
     * @param object $object The instance to make managed and persistent.
362
     */
363
    public function persist($entity)
364
    {
365 366 367
        if ( ! is_object($entity)) {
            throw new \InvalidArgumentException(gettype($entity));
        }
368
        $this->_errorIfClosed();
369
        $this->_unitOfWork->persist($entity);
370
    }
371

372
    /**
romanb's avatar
romanb committed
373
     * Removes an entity instance.
374
     *
romanb's avatar
romanb committed
375
     * A removed entity will be removed from the database at or before transaction commit
376 377
     * or as a result of the flush operation.
     *
romanb's avatar
romanb committed
378
     * @param object $entity The entity instance to remove.
379
     */
romanb's avatar
romanb committed
380
    public function remove($entity)
381
    {
382 383 384
        if ( ! is_object($entity)) {
            throw new \InvalidArgumentException(gettype($entity));
        }
385
        $this->_errorIfClosed();
romanb's avatar
romanb committed
386
        $this->_unitOfWork->remove($entity);
387
    }
388

romanb's avatar
romanb committed
389
    /**
romanb's avatar
romanb committed
390
     * Refreshes the persistent state of an entity from the database,
romanb's avatar
romanb committed
391
     * overriding any local changes that have not yet been persisted.
romanb's avatar
romanb committed
392
     *
romanb's avatar
romanb committed
393
     * @param object $entity The entity to refresh.
romanb's avatar
romanb committed
394
     */
395
    public function refresh($entity)
romanb's avatar
romanb committed
396
    {
397 398 399
        if ( ! is_object($entity)) {
            throw new \InvalidArgumentException(gettype($entity));
        }
romanb's avatar
romanb committed
400
        $this->_errorIfClosed();
romanb's avatar
romanb committed
401
        $this->_unitOfWork->refresh($entity);
romanb's avatar
romanb committed
402
    }
403 404

    /**
405 406 407
     * Detaches an entity from the EntityManager, causing a managed entity to
     * become detached.  Unflushed changes made to the entity if any
     * (including removal of the entity), will not be synchronized to the database.
408
     * Entities which previously referenced the detached entity will continue to
409
     * reference it.
410 411 412 413 414
     *
     * @param object $entity The entity to detach.
     */
    public function detach($entity)
    {
415 416 417
        if ( ! is_object($entity)) {
            throw new \InvalidArgumentException(gettype($entity));
        }
418
        $this->_unitOfWork->detach($entity);
419 420 421 422
    }

    /**
     * Merges the state of a detached entity into the persistence context
423 424
     * of this EntityManager and returns the managed copy of the entity.
     * The entity passed to merge will not become associated/managed with this EntityManager.
425
     *
426
     * @param object $entity The detached entity to merge into the persistence context.
427 428 429 430
     * @return object The managed copy of the entity.
     */
    public function merge($entity)
    {
431 432 433
        if ( ! is_object($entity)) {
            throw new \InvalidArgumentException(gettype($entity));
        }
434
        $this->_errorIfClosed();
435 436
        return $this->_unitOfWork->merge($entity);
    }
437

romanb's avatar
romanb committed
438 439 440
    /**
     * Creates a copy of the given entity. Can create a shallow or a deep copy.
     *
441 442
     * @param object $entity  The entity to copy.
     * @return object  The new entity.
443
     * @todo Implementation or remove.
romanb's avatar
romanb committed
444
     */
445
    public function copy($entity, $deep = false)
romanb's avatar
romanb committed
446
    {
447
        throw new \BadMethodCallException("Not implemented.");
romanb's avatar
romanb committed
448
    }
449

450
    /**
451
     * Gets the repository for an entity class.
452
     *
453
     * @param string $entityName  The name of the Entity.
454
     * @return EntityRepository  The repository.
455 456 457 458 459 460 461 462
     */
    public function getRepository($entityName)
    {
        if (isset($this->_repositories[$entityName])) {
            return $this->_repositories[$entityName];
        }

        $metadata = $this->getClassMetadata($entityName);
463
        $customRepositoryClassName = $metadata->customRepositoryClassName;
464

465
        if ($customRepositoryClassName !== null) {
466
            $repository = new $customRepositoryClassName($this, $metadata);
467
        } else {
468
            $repository = new EntityRepository($this, $metadata);
469
        }
470

471 472 473 474
        $this->_repositories[$entityName] = $repository;

        return $repository;
    }
475

476
    /**
romanb's avatar
romanb committed
477
     * Determines whether an entity instance is managed in this EntityManager.
478
     *
479
     * @param object $entity
480
     * @return boolean TRUE if this EntityManager currently manages the given entity, FALSE otherwise.
481
     */
482
    public function contains($entity)
483
    {
484
        return $this->_unitOfWork->isScheduledForInsert($entity) ||
485 486
               $this->_unitOfWork->isInIdentityMap($entity) &&
               ! $this->_unitOfWork->isScheduledForDelete($entity);
487
    }
488

489 490 491
    /**
     * Gets the EventManager used by the EntityManager.
     *
492
     * @return Doctrine\Common\EventManager
493 494 495 496 497
     */
    public function getEventManager()
    {
        return $this->_eventManager;
    }
498

499
    /**
romanb's avatar
romanb committed
500
     * Gets the Configuration used by the EntityManager.
501
     *
502
     * @return Doctrine\ORM\Configuration
503
     */
romanb's avatar
romanb committed
504
    public function getConfiguration()
505
    {
romanb's avatar
romanb committed
506 507
        return $this->_config;
    }
508

509 510
    /**
     * Throws an exception if the EntityManager is closed or currently not active.
511
     *
romanb's avatar
romanb committed
512
     * @throws ORMException If the EntityManager is closed.
513
     */
514
    private function _errorIfClosed()
romanb's avatar
romanb committed
515
    {
516
        if ($this->_closed) {
romanb's avatar
romanb committed
517
            throw ORMException::entityManagerClosed();
romanb's avatar
romanb committed
518
        }
519
    }
520

521
    /**
romanb's avatar
romanb committed
522
     * Gets the UnitOfWork used by the EntityManager to coordinate operations.
523
     *
524
     * @return Doctrine\ORM\UnitOfWork
525
     */
romanb's avatar
romanb committed
526
    public function getUnitOfWork()
527
    {
romanb's avatar
romanb committed
528
        return $this->_unitOfWork;
529
    }
530

531 532 533
    /**
     * Gets a hydrator for the given hydration mode.
     *
534 535 536 537 538
     * This method caches the hydrator instances which is used for all queries that don't
     * selectively iterate over the result.
     *
     * @param int $hydrationMode
     * @return Doctrine\ORM\Internal\Hydration\AbstractHydrator
539 540 541 542
     */
    public function getHydrator($hydrationMode)
    {
        if ( ! isset($this->_hydrators[$hydrationMode])) {
543
            $this->_hydrators[$hydrationMode] = $this->newHydrator($hydrationMode);
544
        }
545

546 547 548
        return $this->_hydrators[$hydrationMode];
    }

549 550
    /**
     * Create a new instance for the given hydration mode.
551
     *
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572
     * @param  int $hydrationMode
     * @return Doctrine\ORM\Internal\Hydration\AbstractHydrator
     */
    public function newHydrator($hydrationMode)
    {
        switch ($hydrationMode) {
            case Query::HYDRATE_OBJECT:
                $hydrator = new Internal\Hydration\ObjectHydrator($this);
                break;
            case Query::HYDRATE_ARRAY:
                $hydrator = new Internal\Hydration\ArrayHydrator($this);
                break;
            case Query::HYDRATE_SCALAR:
                $hydrator = new Internal\Hydration\ScalarHydrator($this);
                break;
            case Query::HYDRATE_SINGLE_SCALAR:
                $hydrator = new Internal\Hydration\SingleScalarHydrator($this);
                break;
            default:
                throw ORMException::invalidHydrationMode($hydrationMode);
        }
573

574 575 576
        return $hydrator;
    }

577
    /**
578
     * Gets the proxy factory used by the EntityManager to create entity proxies.
579
     *
580
     * @return ProxyFactory
581
     */
582
    public function getProxyFactory()
583
    {
584
        return $this->_proxyFactory;
585
    }
586

587
    /**
romanb's avatar
romanb committed
588
     * Factory method to create EntityManager instances.
589
     *
romanb's avatar
romanb committed
590
     * @param mixed $conn An array with the connection parameters or an existing
591 592 593 594
     *      Connection instance.
     * @param Configuration $config The Configuration instance to use.
     * @param EventManager $eventManager The EventManager instance to use.
     * @return EntityManager The created EntityManager.
romanb's avatar
romanb committed
595
     */
596
    public static function create($conn, Configuration $config, EventManager $eventManager = null)
romanb's avatar
romanb committed
597
    {
598 599 600
        if (!$config->getMetadataDriverImpl()) {
            throw ORMException::missingMappingDriverImpl();
        }
601

romanb's avatar
romanb committed
602
        if (is_array($conn)) {
603 604 605
            $conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, ($eventManager ?: new EventManager()));
        } else if ($conn instanceof Connection) {
            if ($eventManager !== null && $conn->getEventManager() !== $eventManager) {
606
                 throw ORMException::mismatchedEventManager();
607 608
            }
        } else {
609
            throw new \InvalidArgumentException("Invalid argument: " . $conn);
romanb's avatar
romanb committed
610
        }
611 612

        return new EntityManager($conn, $config, $conn->getEventManager());
romanb's avatar
romanb committed
613
    }
614
}