<?php
namespace Cms\CoreBundle\Util\Doctrine;
use Cms\CoreBundle\Model\Interfaces\DatabaseOptimizationInterface;
use Doctrine\ORM\Decorator\EntityManagerDecorator;
use Doctrine\ORM\EntityManager as DoctrineEntityManager;
use Doctrine\ORM\EntityRepository as DoctrineEntityRepository;
/**
* Class EntityManager
* @package Cms\CoreBundle\Util\Doctrine
*
* @method EntityRepository getRepository($entityName)
*/
class EntityManager extends EntityManagerDecorator
{
/**
* @var array
*/
private array $customRepositoryCache = [];
/**
* @param iterable $entities
* @return $this
*/
public function optimize(iterable $entities): self
{
$optmizations = [];
foreach ($entities as $entity) {
if ($entity instanceof DatabaseOptimizationInterface) {
foreach ($entity->optimize() as $class => $id) {
if ( ! array_key_exists($class, $optmizations)) {
$optmizations[$class] = [];
}
$optmizations[$class][] = $id;
}
}
}
foreach ($optmizations as $type => $ids) {
$qb = $this->getRepository($type)->createQueryBuilder('optimizations')
->andWhere('optimizations.id IN (:ids)')
->setParameter('ids', $ids);
$qb->getQuery()->getResult();
}
return $this;
}
/**
* @param string $class
* @param string $entity
* @return DoctrineEntityRepository
*/
public function getCustomRepository(string $class, string $entity): DoctrineEntityRepository
{
if ( ! is_subclass_of($class, DoctrineEntityRepository::class)) {
throw new \LogicException();
}
if ( ! array_key_exists($class, $this->customRepositoryCache)) {
$this->customRepositoryCache[$class] = new $class(
$this,
$this->getClassMetadata($entity)
);
}
return $this->customRepositoryCache[$class];
}
/**
* @return EntityManager
*/
public function clone(): EntityManager
{
return new self(
DoctrineEntityManager::create(
$this->getConnection(),
$this->getConfiguration(),
$this->getEventManager(),
),
);
}
/**
* @param mixed $entity
* @return bool
*/
public function isDirty($entity): bool
{
return ($this->getUnitOfWork()->isEntityScheduled($entity) === true);
}
/**
* {@inheritdoc}
*/
public function detach($object): void
{
if ($object !== null) {
parent::detach($object);
}
}
/**
* @param iterable $entities
* @return $this
*/
public function persistAll(iterable $entities): self
{
// loop over all, assume some iterable type
foreach ($entities as $entity) {
// just persist
$this->persist($entity);
}
// chain
return $this;
}
/**
* @param iterable $classes
* @return $this
*/
public function clearAll(iterable $classes): self
{
foreach ($classes as $class) {
if ( ! empty($class)) {
$this->clear($class);
}
}
return $this;
}
/**
* @param iterable $entities
* @return $this
*/
public function removeAll(iterable $entities): self
{
// loop over all, assume some iterable type
foreach ($entities as $entity) {
// just remove
$this->remove($entity);
}
// chain
return $this;
}
/**
* @param mixed $entity
* @return $this
*/
public function save($entity): self
{
// persist and flush
$this->persist($entity);
$this->flush();
// chain
return $this;
}
/**
* @param iterable $entities
* @param bool $flushEach
* @return $this
*/
public function saveAll(iterable $entities, bool $flushEach = false): self
{
// loop over entities, we assume array or iterable type
foreach ($entities as $entity) {
// branch on whether we are flushing as we go
if ($flushEach === true) {
// use our helper, flushes on each call
$this->save($entity);
} else {
// just persist
$this->persist($entity);
}
}
// if we didn't flush on each, we need to flush now
if ($flushEach === false) {
$this->flush();
}
// chain
return $this;
}
/**
* @param iterable $entities
* @param int $batch
* @return $this
*/
public function saveBatches(iterable $entities, int $batch = 200): self
{
// default the batch size if it is not legit
if ($batch < 0) {
$batch = 1;
}
// in case we are passed an associative array or traversable, need our own counting variable
$count = 0;
foreach ($entities as $entity) {
$this->persist($entity);
$count++;
if ($count % $batch === 0) {
$this->flush();
}
}
// final flush to get last incomplete set flushed out
$this->flush();
// chain
return $this;
}
/**
* @param mixed $entity
* @return $this
*/
public function delete($entity): self
{
// remove and flush
$this->remove($entity);
$this->flush();
// chain
return $this;
}
/**
* @param iterable $entities
* @param bool $flushEach
* @return $this
*/
public function deleteAll(iterable $entities, bool $flushEach = false): self
{
// loop over each, assume array or iterable
foreach ($entities as $entity) {
// check for flushing
if ($flushEach === true) {
// use our helper, this flushes after each call
$this->delete($entity);
} else {
// schedule removal
$this->remove($entity);
}
}
// flush if we didn't do it after each
if ($flushEach === false) {
$this->flush();
}
// chain
return $this;
}
/**
* {@inheritDoc}
*/
public function wrapInTransaction(callable $func)
{
$wrappedFunc = fn () => $func($this);
return $this->wrapped->wrapInTransaction($wrappedFunc);
}
/**
* {@inheritDoc}
*/
public function transactional($func)
{
return $this->wrapInTransaction($func);
}
}