vendor/doctrine/orm/src/EntityManager.php line 329

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