vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php line 69

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