mirror of https://github.com/nextcloud/server.git
Introduce Doctrine ORM
TODOs: - [x] EntityManager wrapper - [ ] EntityRepository wrapper - [ ] Query wrapper (for the DQL) - [x] Command integration - [ ] Psr6 Cache wrapper around the ICache - [ ] Decide if we want a wrapper for the Mapping DTO class (lot of work not much beneficts) Signed-off-by: Carl Schwan <carl@carlschwan.eu>introduce/orm
parent
cab0f1327e
commit
e255b4b7a2
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace OC\DB\ORM;
|
||||
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\ORMSetup;
|
||||
use OC\DB\ConnectionAdapter;
|
||||
use OCP\DB\ORM\IQuery;
|
||||
use OCP\DB\ORM\IEntityManager;
|
||||
use OCP\DB\ORM\IEntityRepository;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
class EntityManagerAdapter implements IEntityManager {
|
||||
|
||||
private EntityManager $em;
|
||||
private ConnectionAdapter $connection;
|
||||
|
||||
public function __construct(ConnectionAdapter $connection) {
|
||||
$paths = array_filter(array_map(fn ($appId) => \OC_App::getAppPath($appId) . '/lib/Entity/', \OC_App::getEnabledApps()), fn ($path) => is_dir($path));
|
||||
$isDevMode = true;
|
||||
$proxyDir = null;
|
||||
$cache = null;
|
||||
|
||||
|
||||
$evm = $connection->getInner()->getEventManager();
|
||||
$tablePrefix = new TablePrefix('oc_');
|
||||
|
||||
$evm->addEventListener(\Doctrine\ORM\Events::loadClassMetadata, $tablePrefix);
|
||||
// TODO actually use our cache with a psr6 cache wrapper or at least our cache config
|
||||
$config = ORMSetup::createAnnotationMetadataConfiguration($paths, $isDevMode, $proxyDir, $cache);
|
||||
|
||||
$this->em = EntityManager::create($connection->getInner(), $config, $evm);
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
public function createQuery($dql = ''): IQuery
|
||||
{
|
||||
return new QueryAdapter($this->em->createQuery($dql));
|
||||
}
|
||||
|
||||
public function flush(): void {
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
public function find(string $className, $id, ?int $lockMode = null, ?int $lockVersion = null): ?object {
|
||||
return $this->em->find($className, $id, $lockMode, $lockVersion);
|
||||
}
|
||||
|
||||
public function clear(): void {
|
||||
$this->em->clear();
|
||||
}
|
||||
|
||||
public function persist(object $entity): void {
|
||||
$this->em->persist($entity);
|
||||
}
|
||||
|
||||
public function remove(object $entity): void {
|
||||
$this->em->remove($entity);
|
||||
}
|
||||
|
||||
public function lock(object $entity, int $lockMode, $lockVersion = null): void {
|
||||
$this->em->lock($entity, $lockMode, $lockVersion);
|
||||
}
|
||||
|
||||
public function getRepository($className): IEntityRepository {
|
||||
/** @var EntityRepository $internalRepo */
|
||||
$internalRepo = $this->em->getRepository($className);
|
||||
return new EntityRepositoryAdapter($internalRepo);
|
||||
}
|
||||
|
||||
public function contains(object $entity): bool {
|
||||
return $this->em->contains($entity);
|
||||
}
|
||||
|
||||
public function getConnection(): IDBConnection {
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for internal use
|
||||
*/
|
||||
public function get(): EntityManager {
|
||||
return $this->em;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace OC\DB\ORM;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use OCP\DB\ORM\IEntityRepository;
|
||||
|
||||
class EntityRepositoryAdapter implements IEntityRepository
|
||||
{
|
||||
private EntityRepository $entityRepository;
|
||||
|
||||
public function __construct(EntityRepository $entityRepository) {
|
||||
$this->entityRepository = $entityRepository;
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace OC\DB\ORM;
|
||||
|
||||
use OCP\ICache;
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
|
||||
class CacheItemAdapter implements CacheItemInterface {
|
||||
private ICache $cacheAdapter;
|
||||
private string $key;
|
||||
private $value;
|
||||
private bool $fetched = false;
|
||||
private ?\DateTime $_expireAt = null;
|
||||
private int $_expireAfter = -1;
|
||||
|
||||
public function __construct(ICache $cache, $key) {
|
||||
$this->cache = $cache;
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
private function fetch(): void {
|
||||
if (!$this->fetched) {
|
||||
$this->value = $this->cache->get($this->key);
|
||||
$this->fetched = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function getKey() {
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function get() {
|
||||
$this->fetch();
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function isHit() {
|
||||
$this->fetch();
|
||||
return $this->value !== null;
|
||||
}
|
||||
|
||||
public function set($value) {
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function expiresAt($expiration) {
|
||||
$this->_expireAt = $expiration;
|
||||
}
|
||||
|
||||
public function expiresAfter($time) {
|
||||
$this->_expireAfter = $time;
|
||||
}
|
||||
|
||||
public function getExpireAt(): ?\DateTime
|
||||
{
|
||||
return $this->_expireAt;
|
||||
}
|
||||
|
||||
public function getExpireAfter(): int
|
||||
{
|
||||
return $this->_expireAfter;
|
||||
}
|
||||
}
|
||||
|
||||
class Psr6CacheAdapter implements CacheItemPoolInterface {
|
||||
private ICache $cache;
|
||||
|
||||
public function __construct(ICache $cache) {
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
public function getItem($key) {
|
||||
return new CacheItemAdapter($this->cache, $key);
|
||||
}
|
||||
|
||||
public function getItems(array $keys = array()) {
|
||||
for (int )
|
||||
// TODO: Implement getItems() method.
|
||||
}
|
||||
|
||||
public function hasItem($key)
|
||||
{
|
||||
// TODO: Implement hasItem() method.
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
// TODO: Implement clear() method.
|
||||
}
|
||||
|
||||
public function deleteItem($key)
|
||||
{
|
||||
// TODO: Implement deleteItem() method.
|
||||
}
|
||||
|
||||
public function deleteItems(array $keys)
|
||||
{
|
||||
// TODO: Implement deleteItems() method.
|
||||
}
|
||||
|
||||
public function save(CacheItemInterface $item)
|
||||
{
|
||||
// TODO: Implement save() method.
|
||||
}
|
||||
|
||||
public function saveDeferred(CacheItemInterface $item)
|
||||
{
|
||||
// TODO: Implement saveDeferred() method.
|
||||
}
|
||||
|
||||
public function commit()
|
||||
{
|
||||
// TODO: Implement commit() method.
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace OC\DB\ORM;
|
||||
|
||||
use Doctrine\ORM\Query;
|
||||
use OCP\DB\ORM\IQuery;
|
||||
|
||||
class QueryAdapter implements IQuery {
|
||||
private Query $query;
|
||||
|
||||
public function __construct(Query $query) {
|
||||
$this->query = $query;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace OC\DB\ORM;
|
||||
|
||||
use \Doctrine\ORM\Event\LoadClassMetadataEventArgs;
|
||||
|
||||
class TablePrefix
|
||||
{
|
||||
protected $prefix = 'oc_';
|
||||
|
||||
public function __construct($prefix)
|
||||
{
|
||||
$this->prefix = (string)$prefix;
|
||||
}
|
||||
|
||||
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
|
||||
{
|
||||
$classMetadata = $eventArgs->getClassMetadata();
|
||||
|
||||
if (!$classMetadata->isInheritanceTypeSingleTable() || $classMetadata->getName() === $classMetadata->rootEntityName) {
|
||||
$classMetadata->setPrimaryTable([
|
||||
'name' => $this->prefix . $classMetadata->getTableName()
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) {
|
||||
if ($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_MANY && $mapping['isOwningSide']) {
|
||||
$mappedTableName = $mapping['joinTable']['name'];
|
||||
$classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace OCP\DB\ORM;
|
||||
|
||||
use DateTimeInterface;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @since 25.0.0
|
||||
*/
|
||||
interface IEntityManager {
|
||||
/**
|
||||
* Creates a new Query object.
|
||||
*
|
||||
* @param string $dql The DQL string.
|
||||
* @since 25.0.0
|
||||
*/
|
||||
public function createQuery(string $dql = ''): IQuery;
|
||||
|
||||
/**
|
||||
* Flushes all changes to objects that have been queued up to now to the database.
|
||||
* This effectively synchronizes the in-memory state of managed objects with the
|
||||
* database.
|
||||
*
|
||||
* @throws OptimisticLockException If a version check on an entity that
|
||||
* makes use of optimistic locking fails.
|
||||
* @throws \OCP\DB\Exception
|
||||
* @since 25.0.0
|
||||
*/
|
||||
public function flush(): void;
|
||||
|
||||
/**
|
||||
* Finds an Entity by its identifier.
|
||||
*
|
||||
* @param string $className The class name of the entity to find.
|
||||
* @param mixed $id The identity of the entity to find.
|
||||
* @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
|
||||
* or NULL if no specific lock mode should be used
|
||||
* during the search.
|
||||
* @param int|null $lockVersion The version of the entity to find when using
|
||||
* optimistic locking.
|
||||
* @psalm-param class-string<T> $className
|
||||
* @psalm-param LockMode::*|null $lockMode
|
||||
*
|
||||
* @return object|null The entity instance or NULL if the entity can not be found.
|
||||
* @psalm-return ?T
|
||||
*
|
||||
* @throws OptimisticLockException
|
||||
* @throws \OCP\DB\Exception
|
||||
*
|
||||
* @template T
|
||||
* @since 25.0.0
|
||||
*/
|
||||
public function find(string $className, $id, ?int $lockMode = null, ?int $lockVersion = null): ?object;
|
||||
|
||||
/**
|
||||
* Clears the EntityManager. All entities that are currently managed
|
||||
* by this EntityManager become detached.
|
||||
*
|
||||
* @throws \OCP\DB\Exception If a $entityName is given, but that entity is not
|
||||
* found in the mappings.
|
||||
* @since 25.0.0
|
||||
*/
|
||||
public function clear(): void;
|
||||
|
||||
/**
|
||||
* Tells the EntityManager to make an instance managed and persistent.
|
||||
*
|
||||
* The entity will be entered into the database at or before transaction
|
||||
* commit or as a result of the flush operation.
|
||||
*
|
||||
* NOTE: The persist operation always considers entities that are not yet known to
|
||||
* this EntityManager as NEW. Do not pass detached entities to the persist operation.
|
||||
*
|
||||
* @param object $entity The instance to make managed and persistent.
|
||||
*
|
||||
* @throws \OCP\DB\Exception
|
||||
* @since 25.0.0
|
||||
*/
|
||||
public function persist(object $entity): void;
|
||||
|
||||
/**
|
||||
* Removes an entity instance.
|
||||
*
|
||||
* A removed entity will be removed from the database at or before transaction commit
|
||||
* or as a result of the flush operation.
|
||||
*
|
||||
* @param object $entity The entity instance to remove.
|
||||
*
|
||||
@throws \OCP\DB\Exception
|
||||
* @since 25.0.0
|
||||
*/
|
||||
public function remove(object $entity): void;
|
||||
|
||||
/**
|
||||
* Acquire a lock on the given entity.
|
||||
*
|
||||
* @param int|DateTimeInterface|null $lockVersion
|
||||
* @psalm-param LockMode::* $lockMode
|
||||
*
|
||||
*
|
||||
* @throws OptimisticLockException
|
||||
* @throws PessimisticLockException
|
||||
* @since 25.0.0
|
||||
*/
|
||||
public function lock(object $entity, int $lockMode, $lockVersion = null): void;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @psalm-param class-string<T> $className
|
||||
*
|
||||
* @psalm-return IEntityRepository<T>
|
||||
*
|
||||
* @template T of object
|
||||
* @since 25.0.0
|
||||
*/
|
||||
public function getRepository($className): IEntityRepository;
|
||||
|
||||
/**
|
||||
* Determines whether an entity instance is managed in this EntityManager.
|
||||
*
|
||||
* @return bool TRUE if this EntityManager currently manages the given entity, FALSE otherwise.
|
||||
* @since 25.0.0
|
||||
*/
|
||||
public function contains(object $entity): bool;
|
||||
|
||||
/**
|
||||
* @return IDBConnection
|
||||
* @since 25.0.0
|
||||
*/
|
||||
public function getConnection(): IDBConnection;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace OCP\DB\ORM;
|
||||
|
||||
interface IEntityRepository
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace OCP\DB\ORM;
|
||||
|
||||
interface IQuery
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace OCP\DB\ORM;
|
||||
|
||||
class LockMode
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace OCP\DB\ORM;
|
||||
|
||||
class OptimisticLockException extends \Exception {
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace OCP\DB\ORM;
|
||||
|
||||
class PessimisticLockException extends \Exception {
|
||||
|
||||
}
|
Loading…
Reference in New Issue