From 46443a477f37e59322ca06a378e28092c6969887 Mon Sep 17 00:00:00 2001 From: eater Date: Mon, 22 Jan 2018 09:04:56 +0100 Subject: [PATCH] Add context aware enttity manager and support for multiple entity managers --- composer.json | 7 +- config/default.php | 52 +++--- src/ContextAwareEntityManager.php | 273 ++++++++++++++++++++++++++++++ src/DefaultEntityManager.php | 57 +++++++ src/EntityManagerLike.php | 11 ++ 5 files changed, 378 insertions(+), 22 deletions(-) create mode 100644 src/ContextAwareEntityManager.php create mode 100644 src/DefaultEntityManager.php create mode 100644 src/EntityManagerLike.php diff --git a/composer.json b/composer.json index 658a0fe..8bb9165 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,12 @@ { "name": "bitcommunism/doctrine", - "version": "1.0.0", + "version": "1.0.1", "require": { "doctrine/orm": "^2.6" + }, + "autoload": { + "psr-4": { + "BitCommunism\\Doctrine\\": "src/" + } } } diff --git a/config/default.php b/config/default.php index a12323c..6dc7cce 100644 --- a/config/default.php +++ b/config/default.php @@ -2,43 +2,53 @@ namespace BitCommunism\Doctrine; -use function DI\add; use DI\Container; +use Doctrine\ORM\Tools\Console\ConsoleRunner; +use function DI\add; use function DI\factory; use function DI\get; use function DI\string; -use Doctrine\ORM\EntityManager; -use Doctrine\ORM\Tools\Console\ConsoleRunner; -use Doctrine\ORM\Tools\Setup; return [ - 'marx.calls' => [ - 'doctrine' => function(EntityManager $em) { - return ConsoleRunner::createHelperSet($em); + 'marx.calls' => add([ + 'doctrine' => function (EntityManagerLike $em, $arguments) { + $connection = $em->getEntityManager($arguments[0] ?? 'default'); + + return ConsoleRunner::createHelperSet($connection); }, - ], + ]), + + 'doctrine.debug' => get('debug'), + 'doctrine.single-connection' => false, + + 'doctrine.connections' => add([ + 'default', + ]), + + 'doctrine.connection.default' => get('doctrine.connection'), + 'doctrine.entity-paths.default' => get('doctrine.entity-paths'), - 'doctrine.debug' => false, 'doctrine.entity-paths' => add([ string('{basedir}/src/Entity') ]), - 'doctrine.database' => [ - 'driver' => 'pdo_mysql', + + 'doctrine.connection' => add([ + 'driver' => 'pdo_mysql', 'hostname' => 'localhost', - 'user' => 'root', 'password' => '', - 'dbname' => '', - ], + 'user' => 'root', + ]), + + 'em' => get(EntityManagerLike::class), - 'em' => get(EntityManager::class), + EntityManagerLike::class => factory(function (Container $c) { + $contextAware = $c->get(ContextAwareEntityManager::class); - EntityManager::class => factory(function (Container $c) { - $setup = Setup::createAnnotationMetadataConfiguration( - $c->get('doctrine.entity-paths'), - $c->get('doctrine.debug') - ); + if ($c->get('doctrine.single-connection')) { + return $contextAware->getEntityManager('default'); + } - return EntityManager::create($c->get('doctrine.database'), $setup); + return $contextAware; }), ]; \ No newline at end of file diff --git a/src/ContextAwareEntityManager.php b/src/ContextAwareEntityManager.php new file mode 100644 index 0000000..b29e13a --- /dev/null +++ b/src/ContextAwareEntityManager.php @@ -0,0 +1,273 @@ +container = $c; + $connectionNames = $c->get('doctrine.connections'); + + foreach ($connectionNames as $connectionName) { + $this->indexConnection($connectionName); + } + + $this->finishIndex(); + } + + private function indexConnection(string $name) + { + $fullConnectionKey = 'doctrine.connection.' . $name; + $fullEntityPathKey = 'doctrine.entity-paths.' . $name; + + if (!$this->container->has($fullEntityPathKey) || !$this->container->has($fullConnectionKey)) { + return; + } + + $entityPaths = $this->container->get($fullEntityPathKey); + + foreach ($entityPaths as $entityPath) { + $this->entityManagerPathMap[$entityPath] = $name; + } + + $this->entityManagerPaths = array_merge($this->entityManagerPaths, $entityPaths); + } + + private function finishIndex() + { + sort($this->entityManagerPaths, SORT_DESC); + sort($this->entityManagerKeys, SORT_DESC); + } + + /** + * @param $object + * @return DefaultEntityManager + * @throws \Doctrine\ORM\ORMException + * @throws \ReflectionException + */ + public function getEntityManagerForObject($object): DefaultEntityManager + { + return $this->getEntityManagerForClass(get_class($object)); + } + + /** + * @param string $className + * @return DefaultEntityManager + * @throws \Doctrine\ORM\ORMException + * @throws \ReflectionException + */ + public function getEntityManagerForClass(string $className): DefaultEntityManager + { + if (!isset($this->entityMangerKeyCache)) { + $this->entityMangerKeyCache[$className] = $this->resolveEntityManagerKeyForClass($className); + } + + return $this->getEntityManager($this->entityMangerKeyCache[$className]); + } + + /** + * @param string $className + * @return string + * @throws \ReflectionException + */ + private function resolveEntityManagerKeyForClass(string $className): string + { + if (isset($this->entityManagerKeyMap[$className])) { + return $this->entityManagerKeyMap[$className]; + } + + $reflection = new \ReflectionClass($className); + $filePath = $reflection->getFileName(); + + foreach ($this->entityManagerPaths as $entityManagerPath) { + if (strlen($entityManagerPath) > strlen($filePath)) { + continue; + } + + if (substr($filePath, 0, strlen($entityManagerPath)) === $entityManagerPath) { + return $this->entityManagerPathMap[$entityManagerPath]; + } + } + + foreach ($this->entityManagerKeys as $entityManagerKey) { + if (strlen($entityManagerKey) > strlen($className)) { + continue; + } + + if (substr($className, 0, strlen($entityManagerKey)) === $entityManagerKey) { + return $this->entityManagerKeyMap[$entityManagerKey]; + } + } + + return 'default'; + } + + + /** + * @param string $name + * @return DefaultEntityManager + */ + public function getEntityManager(string $name): DefaultEntityManager + { + if (!isset($this->entityManagers[$name])) { + $this->entityManagers[$name] = $this->createEntityManager($name); + } + + return $this->entityManagers[$name]; + } + + /** + * @param string $name + * @return DefaultEntityManager + */ + protected function createEntityManager(string $name): DefaultEntityManager + { + if (!$this->container->has('doctrine.connection.' . $name)) { + throw new \RuntimeException("Connection '{$name}' doesn't exist"); + } + + $connection = $this->container->get('doctrine.connection.' . $name); + + if (!$this->container->has('doctrine.entity-paths.' . $name)) { + throw new \RuntimeException("No entity paths defined for connection '{$name}'"); + } + + $paths = $this->container->get('doctrine.entity-paths.' . $name); + + $config = Setup::createAnnotationMetadataConfiguration($paths, $this->container->get('doctrine.debug')); + return DefaultEntityManager::create($connection, $config, null, $name); + } + + + /** + * @param $className + * @param $id + * @return null|object + * @throws \Doctrine\ORM\ORMException + * @throws \ReflectionException + */ + public function find($className, $id) + { + return $this->getEntityManagerForClass($className)->find($className, $id); + } + + public function persist($object) + { + return $this->getEntityManagerForObject($object)->persist($object); + } + + public function remove($object) + { + return $this->getEntityManagerForObject($object)->remove($object); + } + + public function merge($object) + { + return $this->getEntityManagerForObject($object)->merge($object); + } + + public function clear($objectName = null) + { + // TODO: Implement clear() method. + } + + public function detach($object) + { + return $this->getEntityManagerForObject($object)->detach($object); + } + + public function refresh($object) + { + return $this->getEntityManagerForObject($object)->refresh($object); + } + + public function flush($entity = null) + { + if ($entity === null) { + return $this->getEntityManagerForObject($entity)->flush($entity); + } + + foreach ($this->entityManagers as $entityManager) { + $entityManager->flush(); + } + } + + public function getRepository($className) + { + return $this->getEntityManagerForClass($className)->getRepository($className); + } + + public function getClassMetadata($className) + { + return $this->getEntityManagerForClass($className)->getClassMetadata($className); + } + + public function initializeObject($object) + { + return $this->getEntityManagerForObject($object)->initializeObject($object); + } + + public function contains($object) + { + return $this->getEntityManagerForObject($object)->contains($object); + } + + public function close() + { + foreach ($this->entityManagers as $entityManager) { + $entityManager->close(); + } + } + + public function copy($entity, $deep = false) + { + return $this->getEntityManagerForObject($entity)->copy($entity, $deep); + } + + public function lock($entity, $lockMode, $lockVersion = null) + { + return $this->getEntityManagerForObject($entity)->lock($entity, $lockMode, $lockVersion); + } +} \ No newline at end of file diff --git a/src/DefaultEntityManager.php b/src/DefaultEntityManager.php new file mode 100644 index 0000000..2ee8db8 --- /dev/null +++ b/src/DefaultEntityManager.php @@ -0,0 +1,57 @@ +name = $name; + } + + /** + * @param string $name + * @param Configuration $connection + * @param Configuration $config + * @param EventManager|null $eventManager + * @return EntityManager|static + * @throws ORMException + */ + public static function create($connection, Configuration $config, EventManager $eventManager = null, string $name = null) + { + if (!$config->getMetadataDriverImpl()) { + throw ORMException::missingMappingDriverImpl(); + } + + $connection = static::createConnection($connection, $config, $eventManager); + + return new static($connection, $config, $connection->getEventManager(), $name); + } + + public function getEntityManager(string $name): DefaultEntityManager + { + if ($name !== $this->name) { + throw new \RuntimeException("This DefaultEntityManager instance only has em with the name '{$name}'"); + } + + return $this; + } +} \ No newline at end of file diff --git a/src/EntityManagerLike.php b/src/EntityManagerLike.php new file mode 100644 index 0000000..89f438a --- /dev/null +++ b/src/EntityManagerLike.php @@ -0,0 +1,11 @@ +