Add context aware enttity manager and support for multiple entity managers

master
eater 7 years ago
parent 7efe056ba0
commit 46443a477f
Signed by: eater
GPG Key ID: AD2560A0F84F0759

@ -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/"
}
}
}

@ -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' => [
'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;
}),
];

@ -0,0 +1,273 @@
<?php
namespace BitCommunism\Doctrine;
use DI\Container;
use Doctrine\ORM\Tools\Setup;
class ContextAwareEntityManager implements EntityManagerLike
{
/**
* @var Container
*/
private $container;
/**
* @var DefaultEntityManager[]
*/
private $entityManagers = [];
/**
* @var string[]
*/
private $entityMangerKeyCache = [];
/**
* @var string[]
*/
private $entityManagerKeys = [];
/**
* @var string[]
*/
private $entityManagerPaths = [];
/**
* @var string[]
*/
private $entityManagerKeyMap = [];
/**
* @var string[]
*/
private $entityManagerPathMap = [];
public function __construct(Container $c)
{
$this->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);
}
}

@ -0,0 +1,57 @@
<?php
namespace BitCommunism\Doctrine;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\ORMException;
class DefaultEntityManager extends EntityManager implements EntityManagerLike
{
protected $name;
/**
* DefaultEntityManager constructor.
* @param string $name
* @param Connection $conn
* @param Configuration $config
* @param EventManager $eventManager
*/
protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager, string $name = null)
{
parent::__construct($conn, $config, $eventManager);
$this->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;
}
}

@ -0,0 +1,11 @@
<?php
namespace BitCommunism\Doctrine;
interface EntityManagerLike
{
public function getEntityManager(string $name): DefaultEntityManager;
public function find($className, $id);
}
Loading…
Cancel
Save