<?php
declare(strict_types=1);
namespace Hitso\Bundle\CommonBundle\Repository;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\Common\Util\Debug;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\Tools\Pagination\Paginator;
use Gedmo\Loggable\Entity\Repository\LogEntryRepository;
use Hitso\Bundle\CommonBundle\Doctrine\Behaviours\VersionInterface;
use Hitso\Bundle\CommonBundle\Entity\ChangeLog;
use Hitso\Bundle\CommonBundle\Entity\EntityInterface;
use Symfony\Bridge\Doctrine\RegistryInterface;
/**
* Class ChangeLogRepository
*
* @package Hitso\Bundle\CommonBundle\Repository
*/
class ChangeLogRepository extends LogEntryRepository implements ServiceEntityRepositoryInterface
{
/**
* ChangeLogRepository constructor.
*
* @param RegistryInterface $registry
* @param string $className
*/
public function __construct(RegistryInterface $registry, string $className = ChangeLog::class)
{
parent::__construct($registry->getEntityManager(), $registry->getEntityManager()->getClassMetadata($className));
}
/**
* @param object $entity
* @param int $version
*/
public function revert($entity, $version = 1)
{
if ($entity instanceof VersionInterface) {
$entity->skipVersion(true);
/** @var ChangeLog $changeLog */
$changeLog = $this->findOneBy(
[
'objectId' => $entity->getId(),
'objectClass' => ClassUtils::getClass($entity),
'version' => $version,
]
);
if ($changeLog) {
$changeLog->setCurrent($entity->isCurrent());
}
}
parent::revert($entity, $version);
}
/**
* @param ChangeLog $logEntry
*
* @return object|null
* @throws \Doctrine\ORM\ORMException
* @throws \Doctrine\ORM\OptimisticLockException
* @throws \Doctrine\ORM\TransactionRequiredException
*/
public function getVersionedEntity(ChangeLog $logEntry)
{
return $this->_em->find($logEntry->getObjectClass(), $logEntry->getObjectId());
}
/**
* @param string $className
* @param iterable $objectIds
*
* @return array
*/
public function findLatest(string $className, iterable $objectIds): array
{
return $this
->createQueryBuilder('v', 'v.id')
->leftJoin(ChangeLog::class, 'v2', Join::WITH, 'v.objectId = v2.objectId AND v.version < v2.version')
->select('v')
->andWhere('v.objectParent = :className')->orWhere('v.objectClass = :className')
->andWhere('v2.objectId IS NULL')
->andWhere('v.objectId IN (:objectIds)')
->orderBy('v.version', 'DESC')
->setParameters(['className' => $className, 'objectIds' => $objectIds])
->getQuery()
->getResult();
}
/**
* @param string $className
* @param iterable $objectIds
*
* @return array
*/
public function findCurrent(string $className, iterable $objectIds): array
{
return $this
->createQueryBuilder('v', 'v.id')
->select('v')
->andWhere('v.objectParent = :className')->orWhere('v.objectClass = :className')
->andWhere('v.objectId IN (:objectIds)')
->andWhere('v.current = 1')
->orderBy('v.version', 'DESC')
->setParameters(['className' => $className, 'objectIds' => $objectIds])
->getQuery()
->getResult();
}
/**
* @return int
*/
public function getTotalCount(): int
{
$queryBuilder = $this->createQueryBuilder('e');
$query = $queryBuilder->getQuery();
$query->useQueryCache(true);
$query->useResultCache(true);
$paginator = new Paginator($query, true);
$paginator->setUseOutputWalkers(false);
return $paginator->count();
}
/**
* Get the query for loading of log entries
*
* @param object $entity
*
* @return Query
*/
public function getLogEntriesQuery($entity)
{
$q = parent::getLogEntriesQuery($entity);
$q
->useResultCache(false)
->useQueryCache(true);
return $q;
}
/**
* @param EntityInterface $entity
* @param int $pending
*
* @return mixed
*/
public function getPendingLogByEntity(EntityInterface $entity, int $pending = 1)
{
$qb = $this->createQueryBuilder('c')
->select()
->andWhere('c.objectClass = :className')
->setParameter('className', get_class($entity))
->andWhere('c.objectId = :objectId')
->setParameter('objectId', $entity->getId())
->orderBy('c.id', 'DESC');
if ($pending == 1) {
$qb->andWhere('c.pending = :pending')
->setParameter('pending', 1);
}
return $qb->getQuery()
->getResult();
}
/**
* @param EntityInterface $entity
* @param int $pending
*
* @return mixed
*/
public function countPendingLogByEntity(EntityInterface $entity, int $pending = 1)
{
$qb = $this->createQueryBuilder('c')
->select('COUNT(c.id)')
->andWhere('c.objectClass = :className')
->setParameter('className', get_class($entity))
->andWhere('c.objectId = :objectId')
->setParameter('objectId', $entity->getId())
;
if ($pending == 1) {
$qb->andWhere('c.pending = :pending')
->setParameter('pending', 1);
}
return (int)$qb->getQuery()->getSingleScalarResult();
}
public function getEntityLogsQuery(EntityInterface $entity)
{
return $this->createQueryBuilder('c')
->select()
->andWhere('c.objectClass = :className')
->setParameter('className', get_class($entity))
->andWhere('c.objectId = :objectId')
->setParameter('objectId', $entity->getId())
->orderBy('c.version', 'DESC');
}
public function getEntityLogs(EntityInterface $entity)
{
return $this->getEntityLogsQuery($entity)->getQuery()->getResult();
}
public function getPendingLogsQuery(EntityInterface $entity)
{
$qb = $this->getEntityLogsQuery($entity);
$qb->andWhere('c.pending = :pending')
->setParameter('pending', 1);
return $qb;
}
public function getNoPendingLogsQuery(EntityInterface $entity)
{
$qb = $this->getEntityLogsQuery($entity);
$qb->andWhere('c.pending = :pending')
->setParameter('pending', 0);
return $qb;
}
public function getNoPendingLogs(EntityInterface $entity)
{
$qb = $this->getNoPendingLogsQuery($entity);
return $qb->getQuery()
->getResult();
}
public function getPendingLogs(EntityInterface $entity)
{
$qb = $this->getPendingLogsQuery($entity);
return $qb->getQuery()
->getResult();
}
public function getLastLog(EntityInterface $entity)
{
$qb = $this->getEntityLogsQuery($entity);
$qb->setMaxResults(1);
return $qb->getQuery()
->getOneOrNullResult();
}
public function getLastPendingLog(EntityInterface $entity)
{
$qb = $this->getPendingLogsQuery($entity);
$qb->setMaxResults(1);
return $qb->getQuery()
->getOneOrNullResult();
}
public function getLastNoPendingLog(EntityInterface $entity)
{
$qb = $this->getNoPendingLogsQuery($entity);
$qb->setMaxResults(1);
return $qb->getQuery()
->getOneOrNullResult();
}
public function setCurrentVersion(EntityInterface $entity, int $value = 0, int $version = null)
{
$queryBuilder = $this->createQueryBuilder('e')
->update()
->set('e.current', ':current')
->setParameter('current', $value)
->where("e.objectClass = :objectClass")
->setParameter('objectClass', ClassUtils::getClass($entity))
->andWhere('e.objectId = :objectId')
->setParameter('objectId', $entity->getId());
if ($version) {
$queryBuilder->andWhere('e.version = :version')
->setParameter('version', $version);
}
$queryBuilder->getQuery()
->getSingleScalarResult();
}
public function getLogByVersion(EntityInterface $entity, int $version = 1)
{
return $this->findOneBy(
[
'objectId' => $entity->getId(),
'objectClass' => ClassUtils::getClass($entity),
'version' => $version,
]
);
}
public function getCurrentVersion(EntityInterface $entity)
{
$log = $this->createQueryBuilder('c')
->select()
->where('c.objectClass = :className AND c.objectId = :objectId AND c.current = 1')
->setParameter('className', ClassUtils::getClass($entity))
->setParameter('objectId', $entity->getId())
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
if ($log) {
return $log->getVersion();
}
return 1;
}
}