unite-cms/unite-cms

View on GitHub
src/Bundle/DoctrineORMBundle/Content/ContentManager.php

Summary

Maintainability
A
0 mins
Test Coverage
F
55%
<?php

namespace UniteCMS\DoctrineORMBundle\Content;

use DateTime;
use Doctrine\DBAL\Exception\DeadlockException;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\Persistence\ObjectManager;
use Doctrine\Persistence\ObjectRepository;
use Symfony\Component\Security\Core\Security;
use UniteCMS\CoreBundle\Query\ContentCriteria;
use UniteCMS\CoreBundle\Content\ContentInterface;
use UniteCMS\CoreBundle\Content\ContentManagerInterface;
use UniteCMS\CoreBundle\Content\ContentResultInterface;
use UniteCMS\CoreBundle\Domain\Domain;
use UniteCMS\CoreBundle\Exception\InvalidContentVersionException;
use UniteCMS\DoctrineORMBundle\Entity\Content;
use UniteCMS\DoctrineORMBundle\Entity\Revision;

class ContentManager implements ContentManagerInterface
{
    const ENTITY = Content::class;

    /**
     * @var ManagerRegistry
     */
    protected $registry;

    /**
     * @var Security $security
     */
    protected $security;

    /**
     * @var ContentInterface[]
     */
    protected $contentToPersist = [];

    /**
     * @var ContentInterface[]
     */
    protected $contentToRemove = [];

    /**
     * ContentManager constructor.
     *
     * @param ManagerRegistry $registry
     * @param Security $security
     */
    public function __construct(ManagerRegistry $registry, Security $security) {
        $this->registry = $registry;
        $this->security = $security;
    }

    /**
     * @param \UniteCMS\CoreBundle\Domain\Domain $domain
     *
     * @return ObjectManager
     */
    protected function em(Domain $domain) : ObjectManager {
        return $this->registry->getManager($domain->getId());
    }

    /**
     * @param \UniteCMS\CoreBundle\Domain\Domain $domain
     *
     * @return \UniteCMS\DoctrineORMBundle\Repository\ContentRepository
     */
    protected function repository(Domain $domain) : ObjectRepository {
        return $this->em($domain)->getRepository(static::ENTITY);
    }

    /**
     * {@inheritDoc}
     */
    public function transactional(Domain $domain, callable $transaction) {
        $ret = null;
        $retries = 4;

        while($retries > 0) {
            try {
                $this->em($domain)->beginTransaction();
                $ret = $transaction();
                $this->em($domain)->getConnection()->commit();

                return $ret;
            } catch (DeadlockException $deadlockException) {
                $this->em($domain)->getConnection()->rollBack();
                $retries--;

                if($retries <= 0) {
                    throw $deadlockException;
                }

                // Wait for 1 sec and try again.
                sleep(1);

            } catch (\Exception $e) {
                $this->em($domain)->getConnection()->rollBack();
                throw $e;
            }
        }

        return $ret;
    }

    /**
     * {@inheritDoc}
     */
    public function find(Domain $domain, string $type, ContentCriteria $criteria, bool $includeDeleted = false, ?callable $resultFilter = null): ContentResultInterface {
        return new ContentResult(
            $this->repository($domain),
            $type,
            $criteria,
            $includeDeleted,
            $resultFilter
        );
    }

    /**
     * {@inheritDoc}
     */
    public function get(Domain $domain, string $type, string $id, bool $includeDeleted = false): ?ContentInterface {
        return $this->repository($domain)->typedFind($type, $id, $includeDeleted);
    }

    /**
     * {@inheritDoc}
     */
    public function create(Domain $domain, string $type): ContentInterface {
        $class = static::ENTITY;
        $content = new $class($type);
        $this->contentToPersist[] = $content;
        return $content;
    }

    /**
     * {@inheritDoc}
     */
    public function update(Domain $domain, ContentInterface $content, array $inputData = []): ContentInterface {
        return $content
            ->setData($inputData)
            ->setUpdated(new DateTime('now'));
    }

    /**
     * {@inheritDoc}
     */
    public function revert(Domain $domain, ContentInterface $content, int $version) : ContentInterface {

        $revision = $this->em($domain)
            ->getRepository(Revision::class)
            ->findOneForContent($content, $version);

        if(!$revision) {
            throw new InvalidContentVersionException();
        }

        $content->setData($revision->getData());
        return $content;
    }

    /**
     * {@inheritDoc}
     */
    public function revisions(Domain $domain, ContentInterface $content, int $limit = 20, int $offset = 0, array $orderBy = ['version' => 'DESC']) : array {
        return $this->em($domain)
            ->getRepository(Revision::class)
            ->findForContent($content, $limit, $offset, $orderBy);
    }

    /**
     * {@inheritDoc}
     */
    public function delete(Domain $domain, ContentInterface $content): ContentInterface {
        $content->setDeleted(new DateTime());
        return $content;
    }

    public function recover(Domain $domain, ContentInterface $content) : ContentInterface {
        return $content->setDeleted(null);
    }

    /**
     * {@inheritDoc}
     */
    public function permanentDelete(Domain $domain, ContentInterface $content): ContentInterface {
        $this->contentToRemove[] = $content;
        return $content;
    }

    /**
     * {@inheritDoc}
     */
    public function flush(Domain $domain) : void {

        foreach($this->contentToPersist as $content) {
            $this->em($domain)->persist($content);
        }

        foreach($this->contentToRemove as $content) {
            $this->em($domain)->remove($content);
        }

        $this->em($domain)->flush();
        $this->contentToPersist = [];
        $this->contentToRemove = [];
    }

    /**
     * {@inheritDoc}
     */
    public function noFlush(Domain $domain) : void {

        foreach($this->contentToPersist as $content) {
            unset($content);
        }

        $this->contentToPersist = [];
        $this->contentToRemove = [];
    }
}