EntityManager.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983
  1. <?php
  2. /*
  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.
  14. *
  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>.
  18. */
  19. namespace Doctrine\ORM;
  20. use BadMethodCallException;
  21. use Doctrine\Common\EventManager;
  22. use Doctrine\Common\Util\ClassUtils;
  23. use Doctrine\DBAL\Connection;
  24. use Doctrine\DBAL\DriverManager;
  25. use Doctrine\DBAL\LockMode;
  26. use Doctrine\ORM\Mapping\ClassMetadata;
  27. use Doctrine\ORM\Mapping\ClassMetadataFactory;
  28. use Doctrine\ORM\Proxy\ProxyFactory;
  29. use Doctrine\ORM\Query\Expr;
  30. use Doctrine\ORM\Query\FilterCollection;
  31. use Doctrine\ORM\Query\ResultSetMapping;
  32. use Doctrine\ORM\Repository\RepositoryFactory;
  33. use Doctrine\Persistence\Mapping\MappingException;
  34. use Doctrine\Persistence\ObjectRepository;
  35. use InvalidArgumentException;
  36. use Throwable;
  37. use function array_keys;
  38. use function call_user_func;
  39. use function get_class;
  40. use function gettype;
  41. use function is_array;
  42. use function is_callable;
  43. use function is_object;
  44. use function is_string;
  45. use function ltrim;
  46. use function sprintf;
  47. use function trigger_error;
  48. use const E_USER_DEPRECATED;
  49. /**
  50. * The EntityManager is the central access point to ORM functionality.
  51. *
  52. * It is a facade to all different ORM subsystems such as UnitOfWork,
  53. * Query Language and Repository API. Instantiation is done through
  54. * the static create() method. The quickest way to obtain a fully
  55. * configured EntityManager is:
  56. *
  57. * use Doctrine\ORM\Tools\Setup;
  58. * use Doctrine\ORM\EntityManager;
  59. *
  60. * $paths = array('/path/to/entity/mapping/files');
  61. *
  62. * $config = Setup::createAnnotationMetadataConfiguration($paths);
  63. * $dbParams = array('driver' => 'pdo_sqlite', 'memory' => true);
  64. * $entityManager = EntityManager::create($dbParams, $config);
  65. *
  66. * For more information see
  67. * {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html}
  68. *
  69. * You should never attempt to inherit from the EntityManager: Inheritance
  70. * is not a valid extension point for the EntityManager. Instead you
  71. * should take a look at the {@see \Doctrine\ORM\Decorator\EntityManagerDecorator}
  72. * and wrap your entity manager in a decorator.
  73. */
  74. /* final */class EntityManager implements EntityManagerInterface
  75. {
  76. /**
  77. * The used Configuration.
  78. *
  79. * @var Configuration
  80. */
  81. private $config;
  82. /**
  83. * The database connection used by the EntityManager.
  84. *
  85. * @var Connection
  86. */
  87. private $conn;
  88. /**
  89. * The metadata factory, used to retrieve the ORM metadata of entity classes.
  90. *
  91. * @var ClassMetadataFactory
  92. */
  93. private $metadataFactory;
  94. /**
  95. * The UnitOfWork used to coordinate object-level transactions.
  96. *
  97. * @var UnitOfWork
  98. */
  99. private $unitOfWork;
  100. /**
  101. * The event manager that is the central point of the event system.
  102. *
  103. * @var EventManager
  104. */
  105. private $eventManager;
  106. /**
  107. * The proxy factory used to create dynamic proxies.
  108. *
  109. * @var ProxyFactory
  110. */
  111. private $proxyFactory;
  112. /**
  113. * The repository factory used to create dynamic repositories.
  114. *
  115. * @var RepositoryFactory
  116. */
  117. private $repositoryFactory;
  118. /**
  119. * The expression builder instance used to generate query expressions.
  120. *
  121. * @var Expr
  122. */
  123. private $expressionBuilder;
  124. /**
  125. * Whether the EntityManager is closed or not.
  126. *
  127. * @var bool
  128. */
  129. private $closed = false;
  130. /**
  131. * Collection of query filters.
  132. *
  133. * @var FilterCollection
  134. */
  135. private $filterCollection;
  136. /** @var Cache The second level cache regions API. */
  137. private $cache;
  138. /**
  139. * Creates a new EntityManager that operates on the given database connection
  140. * and uses the given Configuration and EventManager implementations.
  141. */
  142. protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
  143. {
  144. $this->conn = $conn;
  145. $this->config = $config;
  146. $this->eventManager = $eventManager;
  147. $metadataFactoryClassName = $config->getClassMetadataFactoryName();
  148. $this->metadataFactory = new $metadataFactoryClassName();
  149. $this->metadataFactory->setEntityManager($this);
  150. $this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
  151. $this->repositoryFactory = $config->getRepositoryFactory();
  152. $this->unitOfWork = new UnitOfWork($this);
  153. $this->proxyFactory = new ProxyFactory(
  154. $this,
  155. $config->getProxyDir(),
  156. $config->getProxyNamespace(),
  157. $config->getAutoGenerateProxyClasses()
  158. );
  159. if ($config->isSecondLevelCacheEnabled()) {
  160. $cacheConfig = $config->getSecondLevelCacheConfiguration();
  161. $cacheFactory = $cacheConfig->getCacheFactory();
  162. $this->cache = $cacheFactory->createCache($this);
  163. }
  164. }
  165. /**
  166. * {@inheritDoc}
  167. */
  168. public function getConnection()
  169. {
  170. return $this->conn;
  171. }
  172. /**
  173. * Gets the metadata factory used to gather the metadata of classes.
  174. *
  175. * @return ClassMetadataFactory
  176. */
  177. public function getMetadataFactory()
  178. {
  179. return $this->metadataFactory;
  180. }
  181. /**
  182. * {@inheritDoc}
  183. */
  184. public function getExpressionBuilder()
  185. {
  186. if ($this->expressionBuilder === null) {
  187. $this->expressionBuilder = new Query\Expr();
  188. }
  189. return $this->expressionBuilder;
  190. }
  191. /**
  192. * {@inheritDoc}
  193. */
  194. public function beginTransaction()
  195. {
  196. $this->conn->beginTransaction();
  197. }
  198. /**
  199. * {@inheritDoc}
  200. */
  201. public function getCache()
  202. {
  203. return $this->cache;
  204. }
  205. /**
  206. * {@inheritDoc}
  207. */
  208. public function transactional($func)
  209. {
  210. if (! is_callable($func)) {
  211. throw new InvalidArgumentException('Expected argument of type "callable", got "' . gettype($func) . '"');
  212. }
  213. $this->conn->beginTransaction();
  214. try {
  215. $return = call_user_func($func, $this);
  216. $this->flush();
  217. $this->conn->commit();
  218. return $return ?: true;
  219. } catch (Throwable $e) {
  220. $this->close();
  221. $this->conn->rollBack();
  222. throw $e;
  223. }
  224. }
  225. /**
  226. * {@inheritDoc}
  227. */
  228. public function commit()
  229. {
  230. $this->conn->commit();
  231. }
  232. /**
  233. * {@inheritDoc}
  234. */
  235. public function rollback()
  236. {
  237. $this->conn->rollBack();
  238. }
  239. /**
  240. * Returns the ORM metadata descriptor for a class.
  241. *
  242. * The class name must be the fully-qualified class name without a leading backslash
  243. * (as it is returned by get_class($obj)) or an aliased class name.
  244. *
  245. * Examples:
  246. * MyProject\Domain\User
  247. * sales:PriceRequest
  248. *
  249. * Internal note: Performance-sensitive method.
  250. *
  251. * @param string $className
  252. *
  253. * @return ClassMetadata
  254. */
  255. public function getClassMetadata($className)
  256. {
  257. return $this->metadataFactory->getMetadataFor($className);
  258. }
  259. /**
  260. * {@inheritDoc}
  261. */
  262. public function createQuery($dql = '')
  263. {
  264. $query = new Query($this);
  265. if (! empty($dql)) {
  266. $query->setDQL($dql);
  267. }
  268. return $query;
  269. }
  270. /**
  271. * {@inheritDoc}
  272. */
  273. public function createNamedQuery($name)
  274. {
  275. return $this->createQuery($this->config->getNamedQuery($name));
  276. }
  277. /**
  278. * {@inheritDoc}
  279. */
  280. public function createNativeQuery($sql, ResultSetMapping $rsm)
  281. {
  282. $query = new NativeQuery($this);
  283. $query->setSQL($sql);
  284. $query->setResultSetMapping($rsm);
  285. return $query;
  286. }
  287. /**
  288. * {@inheritDoc}
  289. */
  290. public function createNamedNativeQuery($name)
  291. {
  292. [$sql, $rsm] = $this->config->getNamedNativeQuery($name);
  293. return $this->createNativeQuery($sql, $rsm);
  294. }
  295. /**
  296. * {@inheritDoc}
  297. */
  298. public function createQueryBuilder()
  299. {
  300. return new QueryBuilder($this);
  301. }
  302. /**
  303. * Flushes all changes to objects that have been queued up to now to the database.
  304. * This effectively synchronizes the in-memory state of managed objects with the
  305. * database.
  306. *
  307. * If an entity is explicitly passed to this method only this entity and
  308. * the cascade-persist semantics + scheduled inserts/removals are synchronized.
  309. *
  310. * @param object|mixed[]|null $entity
  311. *
  312. * @return void
  313. *
  314. * @throws OptimisticLockException If a version check on an entity that
  315. * makes use of optimistic locking fails.
  316. * @throws ORMException
  317. */
  318. public function flush($entity = null)
  319. {
  320. if ($entity !== null) {
  321. @trigger_error(
  322. 'Calling ' . __METHOD__ . '() with any arguments to flush specific entities is deprecated and will not be supported in Doctrine ORM 3.0.',
  323. E_USER_DEPRECATED
  324. );
  325. }
  326. $this->errorIfClosed();
  327. $this->unitOfWork->commit($entity);
  328. }
  329. /**
  330. * Finds an Entity by its identifier.
  331. *
  332. * @param string $className The class name of the entity to find.
  333. * @param mixed $id The identity of the entity to find.
  334. * @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
  335. * or NULL if no specific lock mode should be used
  336. * during the search.
  337. * @param int|null $lockVersion The version of the entity to find when using
  338. * optimistic locking.
  339. *
  340. * @return object|null The entity instance or NULL if the entity can not be found.
  341. *
  342. * @throws OptimisticLockException
  343. * @throws ORMInvalidArgumentException
  344. * @throws TransactionRequiredException
  345. * @throws ORMException
  346. *
  347. * @template T
  348. * @psalm-param class-string<T> $className
  349. * @psalm-return ?T
  350. */
  351. public function find($className, $id, $lockMode = null, $lockVersion = null)
  352. {
  353. $class = $this->metadataFactory->getMetadataFor(ltrim($className, '\\'));
  354. if ($lockMode !== null) {
  355. $this->checkLockRequirements($lockMode, $class);
  356. }
  357. if (! is_array($id)) {
  358. if ($class->isIdentifierComposite) {
  359. throw ORMInvalidArgumentException::invalidCompositeIdentifier();
  360. }
  361. $id = [$class->identifier[0] => $id];
  362. }
  363. foreach ($id as $i => $value) {
  364. if (is_object($value) && $this->metadataFactory->hasMetadataFor(ClassUtils::getClass($value))) {
  365. $id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
  366. if ($id[$i] === null) {
  367. throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
  368. }
  369. }
  370. }
  371. $sortedId = [];
  372. foreach ($class->identifier as $identifier) {
  373. if (! isset($id[$identifier])) {
  374. throw ORMException::missingIdentifierField($class->name, $identifier);
  375. }
  376. $sortedId[$identifier] = $id[$identifier];
  377. unset($id[$identifier]);
  378. }
  379. if ($id) {
  380. throw ORMException::unrecognizedIdentifierFields($class->name, array_keys($id));
  381. }
  382. $unitOfWork = $this->getUnitOfWork();
  383. $entity = $unitOfWork->tryGetById($sortedId, $class->rootEntityName);
  384. // Check identity map first
  385. if ($entity !== false) {
  386. if (! ($entity instanceof $class->name)) {
  387. return null;
  388. }
  389. switch (true) {
  390. case $lockMode === LockMode::OPTIMISTIC:
  391. $this->lock($entity, $lockMode, $lockVersion);
  392. break;
  393. case $lockMode === LockMode::NONE:
  394. case $lockMode === LockMode::PESSIMISTIC_READ:
  395. case $lockMode === LockMode::PESSIMISTIC_WRITE:
  396. $persister = $unitOfWork->getEntityPersister($class->name);
  397. $persister->refresh($sortedId, $entity, $lockMode);
  398. break;
  399. }
  400. return $entity; // Hit!
  401. }
  402. $persister = $unitOfWork->getEntityPersister($class->name);
  403. switch (true) {
  404. case $lockMode === LockMode::OPTIMISTIC:
  405. $entity = $persister->load($sortedId);
  406. $unitOfWork->lock($entity, $lockMode, $lockVersion);
  407. return $entity;
  408. case $lockMode === LockMode::PESSIMISTIC_READ:
  409. case $lockMode === LockMode::PESSIMISTIC_WRITE:
  410. return $persister->load($sortedId, null, null, [], $lockMode);
  411. default:
  412. return $persister->loadById($sortedId);
  413. }
  414. }
  415. /**
  416. * {@inheritDoc}
  417. */
  418. public function getReference($entityName, $id)
  419. {
  420. $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
  421. if (! is_array($id)) {
  422. $id = [$class->identifier[0] => $id];
  423. }
  424. $sortedId = [];
  425. foreach ($class->identifier as $identifier) {
  426. if (! isset($id[$identifier])) {
  427. throw ORMException::missingIdentifierField($class->name, $identifier);
  428. }
  429. $sortedId[$identifier] = $id[$identifier];
  430. unset($id[$identifier]);
  431. }
  432. if ($id) {
  433. throw ORMException::unrecognizedIdentifierFields($class->name, array_keys($id));
  434. }
  435. $entity = $this->unitOfWork->tryGetById($sortedId, $class->rootEntityName);
  436. // Check identity map first, if its already in there just return it.
  437. if ($entity !== false) {
  438. return $entity instanceof $class->name ? $entity : null;
  439. }
  440. if ($class->subClasses) {
  441. return $this->find($entityName, $sortedId);
  442. }
  443. $entity = $this->proxyFactory->getProxy($class->name, $sortedId);
  444. $this->unitOfWork->registerManaged($entity, $sortedId, []);
  445. return $entity;
  446. }
  447. /**
  448. * {@inheritDoc}
  449. */
  450. public function getPartialReference($entityName, $identifier)
  451. {
  452. $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
  453. $entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName);
  454. // Check identity map first, if its already in there just return it.
  455. if ($entity !== false) {
  456. return $entity instanceof $class->name ? $entity : null;
  457. }
  458. if (! is_array($identifier)) {
  459. $identifier = [$class->identifier[0] => $identifier];
  460. }
  461. $entity = $class->newInstance();
  462. $class->setIdentifierValues($entity, $identifier);
  463. $this->unitOfWork->registerManaged($entity, $identifier, []);
  464. $this->unitOfWork->markReadOnly($entity);
  465. return $entity;
  466. }
  467. /**
  468. * Clears the EntityManager. All entities that are currently managed
  469. * by this EntityManager become detached.
  470. *
  471. * @param string|null $entityName if given, only entities of this type will get detached
  472. *
  473. * @return void
  474. *
  475. * @throws ORMInvalidArgumentException If a non-null non-string value is given.
  476. * @throws MappingException If a $entityName is given, but that entity is not
  477. * found in the mappings.
  478. */
  479. public function clear($entityName = null)
  480. {
  481. if ($entityName !== null && ! is_string($entityName)) {
  482. throw ORMInvalidArgumentException::invalidEntityName($entityName);
  483. }
  484. if ($entityName !== null) {
  485. @trigger_error(
  486. 'Calling ' . __METHOD__ . '() with any arguments to clear specific entities is deprecated and will not be supported in Doctrine ORM 3.0.',
  487. E_USER_DEPRECATED
  488. );
  489. }
  490. $this->unitOfWork->clear(
  491. $entityName === null
  492. ? null
  493. : $this->metadataFactory->getMetadataFor($entityName)->getName()
  494. );
  495. }
  496. /**
  497. * {@inheritDoc}
  498. */
  499. public function close()
  500. {
  501. $this->clear();
  502. $this->closed = true;
  503. }
  504. /**
  505. * Tells the EntityManager to make an instance managed and persistent.
  506. *
  507. * The entity will be entered into the database at or before transaction
  508. * commit or as a result of the flush operation.
  509. *
  510. * NOTE: The persist operation always considers entities that are not yet known to
  511. * this EntityManager as NEW. Do not pass detached entities to the persist operation.
  512. *
  513. * @param object $entity The instance to make managed and persistent.
  514. *
  515. * @return void
  516. *
  517. * @throws ORMInvalidArgumentException
  518. * @throws ORMException
  519. */
  520. public function persist($entity)
  521. {
  522. if (! is_object($entity)) {
  523. throw ORMInvalidArgumentException::invalidObject('EntityManager#persist()', $entity);
  524. }
  525. $this->errorIfClosed();
  526. $this->unitOfWork->persist($entity);
  527. }
  528. /**
  529. * Removes an entity instance.
  530. *
  531. * A removed entity will be removed from the database at or before transaction commit
  532. * or as a result of the flush operation.
  533. *
  534. * @param object $entity The entity instance to remove.
  535. *
  536. * @return void
  537. *
  538. * @throws ORMInvalidArgumentException
  539. * @throws ORMException
  540. */
  541. public function remove($entity)
  542. {
  543. if (! is_object($entity)) {
  544. throw ORMInvalidArgumentException::invalidObject('EntityManager#remove()', $entity);
  545. }
  546. $this->errorIfClosed();
  547. $this->unitOfWork->remove($entity);
  548. }
  549. /**
  550. * Refreshes the persistent state of an entity from the database,
  551. * overriding any local changes that have not yet been persisted.
  552. *
  553. * @param object $entity The entity to refresh.
  554. *
  555. * @return void
  556. *
  557. * @throws ORMInvalidArgumentException
  558. * @throws ORMException
  559. */
  560. public function refresh($entity)
  561. {
  562. if (! is_object($entity)) {
  563. throw ORMInvalidArgumentException::invalidObject('EntityManager#refresh()', $entity);
  564. }
  565. $this->errorIfClosed();
  566. $this->unitOfWork->refresh($entity);
  567. }
  568. /**
  569. * Detaches an entity from the EntityManager, causing a managed entity to
  570. * become detached. Unflushed changes made to the entity if any
  571. * (including removal of the entity), will not be synchronized to the database.
  572. * Entities which previously referenced the detached entity will continue to
  573. * reference it.
  574. *
  575. * @deprecated 2.7 This method is being removed from the ORM and won't have any replacement
  576. *
  577. * @param object $entity The entity to detach.
  578. *
  579. * @return void
  580. *
  581. * @throws ORMInvalidArgumentException
  582. */
  583. public function detach($entity)
  584. {
  585. @trigger_error('Method ' . __METHOD__ . '() is deprecated and will be removed in Doctrine ORM 3.0.', E_USER_DEPRECATED);
  586. if (! is_object($entity)) {
  587. throw ORMInvalidArgumentException::invalidObject('EntityManager#detach()', $entity);
  588. }
  589. $this->unitOfWork->detach($entity);
  590. }
  591. /**
  592. * Merges the state of a detached entity into the persistence context
  593. * of this EntityManager and returns the managed copy of the entity.
  594. * The entity passed to merge will not become associated/managed with this EntityManager.
  595. *
  596. * @deprecated 2.7 This method is being removed from the ORM and won't have any replacement
  597. *
  598. * @param object $entity The detached entity to merge into the persistence context.
  599. *
  600. * @return object The managed copy of the entity.
  601. *
  602. * @throws ORMInvalidArgumentException
  603. * @throws ORMException
  604. */
  605. public function merge($entity)
  606. {
  607. @trigger_error('Method ' . __METHOD__ . '() is deprecated and will be removed in Doctrine ORM 3.0.', E_USER_DEPRECATED);
  608. if (! is_object($entity)) {
  609. throw ORMInvalidArgumentException::invalidObject('EntityManager#merge()', $entity);
  610. }
  611. $this->errorIfClosed();
  612. return $this->unitOfWork->merge($entity);
  613. }
  614. /**
  615. * {@inheritDoc}
  616. */
  617. public function copy($entity, $deep = false)
  618. {
  619. @trigger_error('Method ' . __METHOD__ . '() is deprecated and will be removed in Doctrine ORM 3.0.', E_USER_DEPRECATED);
  620. throw new BadMethodCallException('Not implemented.');
  621. }
  622. /**
  623. * {@inheritDoc}
  624. */
  625. public function lock($entity, $lockMode, $lockVersion = null)
  626. {
  627. $this->unitOfWork->lock($entity, $lockMode, $lockVersion);
  628. }
  629. /**
  630. * Gets the repository for an entity class.
  631. *
  632. * @param string $entityName The name of the entity.
  633. *
  634. * @return ObjectRepository|EntityRepository The repository class.
  635. *
  636. * @template T
  637. * @psalm-param class-string<T> $entityName
  638. * @psalm-return EntityRepository<T>
  639. */
  640. public function getRepository($entityName)
  641. {
  642. return $this->repositoryFactory->getRepository($this, $entityName);
  643. }
  644. /**
  645. * Determines whether an entity instance is managed in this EntityManager.
  646. *
  647. * @param object $entity
  648. *
  649. * @return bool TRUE if this EntityManager currently manages the given entity, FALSE otherwise.
  650. */
  651. public function contains($entity)
  652. {
  653. return $this->unitOfWork->isScheduledForInsert($entity)
  654. || $this->unitOfWork->isInIdentityMap($entity)
  655. && ! $this->unitOfWork->isScheduledForDelete($entity);
  656. }
  657. /**
  658. * {@inheritDoc}
  659. */
  660. public function getEventManager()
  661. {
  662. return $this->eventManager;
  663. }
  664. /**
  665. * {@inheritDoc}
  666. */
  667. public function getConfiguration()
  668. {
  669. return $this->config;
  670. }
  671. /**
  672. * Throws an exception if the EntityManager is closed or currently not active.
  673. *
  674. * @return void
  675. *
  676. * @throws ORMException If the EntityManager is closed.
  677. */
  678. private function errorIfClosed()
  679. {
  680. if ($this->closed) {
  681. throw ORMException::entityManagerClosed();
  682. }
  683. }
  684. /**
  685. * {@inheritDoc}
  686. */
  687. public function isOpen()
  688. {
  689. return ! $this->closed;
  690. }
  691. /**
  692. * {@inheritDoc}
  693. */
  694. public function getUnitOfWork()
  695. {
  696. return $this->unitOfWork;
  697. }
  698. /**
  699. * {@inheritDoc}
  700. */
  701. public function getHydrator($hydrationMode)
  702. {
  703. return $this->newHydrator($hydrationMode);
  704. }
  705. /**
  706. * {@inheritDoc}
  707. */
  708. public function newHydrator($hydrationMode)
  709. {
  710. switch ($hydrationMode) {
  711. case Query::HYDRATE_OBJECT:
  712. return new Internal\Hydration\ObjectHydrator($this);
  713. case Query::HYDRATE_ARRAY:
  714. return new Internal\Hydration\ArrayHydrator($this);
  715. case Query::HYDRATE_SCALAR:
  716. return new Internal\Hydration\ScalarHydrator($this);
  717. case Query::HYDRATE_SINGLE_SCALAR:
  718. return new Internal\Hydration\SingleScalarHydrator($this);
  719. case Query::HYDRATE_SIMPLEOBJECT:
  720. return new Internal\Hydration\SimpleObjectHydrator($this);
  721. default:
  722. $class = $this->config->getCustomHydrationMode($hydrationMode);
  723. if ($class !== null) {
  724. return new $class($this);
  725. }
  726. }
  727. throw ORMException::invalidHydrationMode($hydrationMode);
  728. }
  729. /**
  730. * {@inheritDoc}
  731. */
  732. public function getProxyFactory()
  733. {
  734. return $this->proxyFactory;
  735. }
  736. /**
  737. * {@inheritDoc}
  738. */
  739. public function initializeObject($obj)
  740. {
  741. $this->unitOfWork->initializeObject($obj);
  742. }
  743. /**
  744. * Factory method to create EntityManager instances.
  745. *
  746. * @param array<string, mixed>|Connection $connection An array with the connection parameters or an existing Connection instance.
  747. * @param Configuration $config The Configuration instance to use.
  748. * @param EventManager $eventManager The EventManager instance to use.
  749. *
  750. * @return EntityManager The created EntityManager.
  751. *
  752. * @throws InvalidArgumentException
  753. * @throws ORMException
  754. */
  755. public static function create($connection, Configuration $config, ?EventManager $eventManager = null)
  756. {
  757. if (! $config->getMetadataDriverImpl()) {
  758. throw ORMException::missingMappingDriverImpl();
  759. }
  760. $connection = static::createConnection($connection, $config, $eventManager);
  761. return new EntityManager($connection, $config, $connection->getEventManager());
  762. }
  763. /**
  764. * Factory method to create Connection instances.
  765. *
  766. * @param array<string, mixed>|Connection $connection An array with the connection parameters or an existing Connection instance.
  767. * @param Configuration $config The Configuration instance to use.
  768. * @param EventManager $eventManager The EventManager instance to use.
  769. *
  770. * @return Connection
  771. *
  772. * @throws InvalidArgumentException
  773. * @throws ORMException
  774. */
  775. protected static function createConnection($connection, Configuration $config, ?EventManager $eventManager = null)
  776. {
  777. if (is_array($connection)) {
  778. return DriverManager::getConnection($connection, $config, $eventManager ?: new EventManager());
  779. }
  780. if (! $connection instanceof Connection) {
  781. throw new InvalidArgumentException(
  782. sprintf(
  783. 'Invalid $connection argument of type %s given%s.',
  784. is_object($connection) ? get_class($connection) : gettype($connection),
  785. is_object($connection) ? '' : ': "' . $connection . '"'
  786. )
  787. );
  788. }
  789. if ($eventManager !== null && $connection->getEventManager() !== $eventManager) {
  790. throw ORMException::mismatchedEventManager();
  791. }
  792. return $connection;
  793. }
  794. /**
  795. * {@inheritDoc}
  796. */
  797. public function getFilters()
  798. {
  799. if ($this->filterCollection === null) {
  800. $this->filterCollection = new FilterCollection($this);
  801. }
  802. return $this->filterCollection;
  803. }
  804. /**
  805. * {@inheritDoc}
  806. */
  807. public function isFiltersStateClean()
  808. {
  809. return $this->filterCollection === null || $this->filterCollection->isClean();
  810. }
  811. /**
  812. * {@inheritDoc}
  813. */
  814. public function hasFilters()
  815. {
  816. return $this->filterCollection !== null;
  817. }
  818. /**
  819. * @throws OptimisticLockException
  820. * @throws TransactionRequiredException
  821. */
  822. private function checkLockRequirements(int $lockMode, ClassMetadata $class): void
  823. {
  824. switch ($lockMode) {
  825. case LockMode::OPTIMISTIC:
  826. if (! $class->isVersioned) {
  827. throw OptimisticLockException::notVersioned($class->name);
  828. }
  829. break;
  830. case LockMode::PESSIMISTIC_READ:
  831. case LockMode::PESSIMISTIC_WRITE:
  832. if (! $this->getConnection()->isTransactionActive()) {
  833. throw TransactionRequiredException::transactionRequired();
  834. }
  835. }
  836. }
  837. }