Files
awo-hamburg-intranet/mysyde/common/classes/UnitOfWork.php
2026-02-17 14:56:23 +01:00

247 lines
8.9 KiB
PHP

<?php
namespace DynCom\mysyde\common\classes;
use DynCom\mysyde\common\interfaces\GenericDBModelInterface;
use DynCom\mysyde\common\interfaces\GenericDBQueryWrapperInterface;
use DynCom\mysyde\common\interfaces\Repository;
use DynCom\mysyde\common\interfaces\RepositoryUnitOfWorkInterface;
/**
* Created by PhpStorm.
* User: Bauer
* Date: 6/2/2015
* Time: 10:04 AM
*/
class UnitOfWork implements RepositoryUnitOfWorkInterface
{
const ENTITY_STATE_NEW = 'ENTITY_STATE_NEW';
const ENTITY_STATE_CLEAN = 'ENTITY_STATE_CLEAN';
const ENTITY_STATE_DIRTY = 'ENTITY_STATE_DIRTY';
const ENTITY_STATE_REMOVED = 'ENTITY_STATE_REMOVED';
protected $possibleStates = array(self::ENTITY_STATE_NEW, self::ENTITY_STATE_CLEAN, self::ENTITY_STATE_DIRTY, self::ENTITY_STATE_REMOVED);
protected $repositories;
protected $entityCollection;
protected $db;
/**
* UnitOfWork constructor.
* @param GenericDBQueryWrapperInterface $queryWrapper
*/
public function __construct(GenericDBQueryWrapperInterface $queryWrapper)
{
$this->entityCollection = new GenericObjectStorage();
$this->db = $queryWrapper;
}
/**
* @param Repository $repository
*/
public function registerRepository(Repository $repository)
{
$objectClass = $repository->getObjectClass();
$this->repositories[$objectClass] = $repository;
}
/**
* @param GenericDBModelInterface $entity
*/
public function registerNew(GenericDBModelInterface $entity)
{
$this->registerEntity($entity, self::ENTITY_STATE_NEW);
}
/**
* @param GenericDBModelInterface $entity
*/
public function registerClean(GenericDBModelInterface $entity)
{
$this->registerEntity($entity, self::ENTITY_STATE_CLEAN);
}
/**
* @param GenericDBModelInterface $entity
*/
public function registerDirty(GenericDBModelInterface $entity)
{
$this->registerEntity($entity, self::ENTITY_STATE_DIRTY);
}
/**
* @param GenericDBModelInterface $entity
*/
public function registerRemoved(GenericDBModelInterface $entity)
{
$this->registerEntity($entity, self::ENTITY_STATE_REMOVED);
}
/**
* @param GenericDBModelInterface $entity
* @param $state
*/
public function registerEntity(GenericDBModelInterface $entity, $state)
{
if (!in_array($state, $this->possibleStates, true)) {
throw new \InvalidArgumentException('State \'' . $state . '\' to register is not defined');
}
$className = get_class($entity);
if (!array_key_exists($className, $this->repositories)) {
throw new \InvalidArgumentException('UoW has no repository for entity of class ' . $className);
}
$data = array('className' => get_class($entity),'state' => $state);
if (isset($entity->id) && ((int)$entity->id > 0)) {
$this->repositories[$className]->lockRecordByID((int)$entity->id);
}
if($this->entityCollection->contains($entity)) {
$data = $this->entityCollection->offsetGet($entity);
$existingObjState = $data['state'];
if($existingObjState === $state) {
$this->entityCollection->detach($entity);
} else {
throw new \LogicException('Cannot register the same entity with different states');
}
}
$this->entityCollection->attach($entity, $data);
}
/**
* @param GenericDBModelInterface $entity
*/
public function unregisterEntity(GenericDBModelInterface $entity)
{
$className = get_class($entity);
if (!array_key_exists($className, $this->repositories)) {
throw new \InvalidArgumentException('UoW has no repository of entity of class ' . $className);
}
if ($this->entityCollection->contains($entity)) {
$this->entityCollection->detach($entity);
}
if (isset($entity->id) && ((int)$entity->id > 0)) {
$this->repositories[$className]->releaseRecordLock((int)$entity->id);
}
}
/**
* @param Repository $repository
*/
public function unregisterRepository(Repository $repository)
{
$className = $repository->getObjectClass();
if (array_key_exists($className, $this->repositories)) {
unset($this->repositories[$className]);
}
}
/**
* @return $this
*/
public function clear()
{
$this->entityCollection->clear();
return $this;
}
public function commit()
{
if (!$this->db->startTransaction()) {
throw new \BadMethodCallException('Could not start transaction. ' . $this->db->getErrorMessage());
}
$c = $this->entityCollection;
$c->rewind();
while ($c->valid()) {
$object = $c->current();
$data = $c->getInfo();
$className = $data['className'];
$state = $data['state'];
$repository = $this->repositories[$className];
if($repository instanceof Repository && $object instanceof GenericDBModelInterface) {
switch ($state) {
case self::ENTITY_STATE_CLEAN:
break;
case self::ENTITY_STATE_NEW:
$currInsertQuery = $repository->getCreateInDBQuery($object);
$this->db->setQuery($currInsertQuery);
if (!$this->db->doQuery()) {
throw new \BadMethodCallException('Could not execute Query in transaction. ' . $this->db->getErrorMessage());
}
break;
case self::ENTITY_STATE_DIRTY:
$currUpdateQuery = $repository->getUpdateSingleQuery($object);
$this->db->setQuery($currUpdateQuery);
if (!$this->db->doQuery()) {
throw new \BadMethodCallException('Could not execute Query in transaction. ' . $this->db->getErrorMessage());
}
break;
case self::ENTITY_STATE_REMOVED:
$currDeleteQuery = $repository->getDeleteByIDQuery($object);
$this->db->setQuery($currDeleteQuery);
if (!$this->db->doQuery()) {
throw new \BadMethodCallException('Could not execute Query in transaction. ' . $this->db->getErrorMessage());
}
break;
}
}
$c->next();
}
if (!$this->db->commitTransaction()) {
throw new \BadMethodCallException('Could not commit transaction. ' . $this->db->getErrorMessage());
}
$c->rewind();
while ($c->valid()) {
$index = $c->key();
$object = $c->current();
$data = $c->getInfo();
$className = $data['className'];
$state = $data['state'];
$repository = $this->repositories[$className];
if ($repository instanceof Repository && $object instanceof GenericDBModelInterface)
{
switch ($state) {
case self::ENTITY_STATE_CLEAN: break;
case self::ENTITY_STATE_NEW:
$repository->add($object,false);
break;
case self::ENTITY_STATE_DIRTY:
$repository->releaseRecordLock($object->id);
$repository->recacheEntityFromDBByID($object->id);
break;
case self::ENTITY_STATE_REMOVED:
$repository->releaseRecordLock($object->id);
$repository->removeEntityFromCache($object);
break;
}
}
$c->next();
}
$this->clear();
}
public function rollback()
{
//Only release record locks - transaction actually happens only on commit(),
//but entities with ID are locked from registration in their repositories
$c = $this->entityCollection;
$c->rewind();
while ($c->valid()) {
$index = $c->key();
$object = $c->current();
$data = $c->getInfo();
$className = $data['className'];
$state = $data['state'];
$repository = $this->repositories[$className];
if (isset($object->id) && ((int)$object->id > 0) && $repository instanceof Repository) {
$repository->releaseRecordLock($object->id);
}
$c->next();
}
$c->rewind();
}
}