<?php
declare(strict_types=1);
namespace Hitso\Bundle\CommonBundle\Doctrine\Manager;
use Carbon\Carbon;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Inflector\Inflector;
use Doctrine\Common\Persistence\ObjectRepository;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\Common\Util\Debug;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Hitso\Bundle\CatalogBundle\Entity\Product;
use Hitso\Bundle\CatalogBundle\Entity\ProductEditable;
use Hitso\Bundle\CommonBundle\Doctrine\Event\EntityEvent;
use Hitso\Bundle\CommonBundle\Doctrine\Factory\EntityCloneFactory;
use Hitso\Bundle\CommonBundle\Doctrine\Factory\EntityCloneFactoryInterface;
use Hitso\Bundle\CommonBundle\Doctrine\Service\LoggableReverter;
use Hitso\Bundle\CommonBundle\Entity\EntityInterface;
use Hitso\Bundle\CommonBundle\Entity\User;
use Hitso\Bundle\CommonBundle\Traits\DeletableInterface;
use Hitso\Bundle\CommonBundle\Traits\EditableInterface;
use Hitso\Bundle\CommonBundle\Traits\EnabledInterface;
use Hitso\Bundle\FormBundle\Form\Handler\FormHandlerInterface;
use Hitso\Bundle\MultiSiteBundle\MultiSite\SiteContext;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
/**
* Class AbstractManager
*
* @package Hitso\Bundle\CommonBundle\Doctrine\Manager
*/
abstract class AbstractManager implements ManagerInterface
{
/**
* @var bool
*/
protected $editable = false;
/**
* @var EntityManagerInterface
*/
protected $doctrine;
/**
* @var EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* @var FormFactoryInterface
*/
protected $formHandler;
/**
* @var EntityCloneFactoryInterface
*/
protected $entityCloneFactory;
/**
* @var ParameterBagInterface
*/
protected $parameterBag;
/**
* AbstractManager constructor.
*
* @param EntityManagerInterface $doctrine
* @param EventDispatcherInterface $eventDispatcher
* @param FormHandlerInterface $formHandler
* @param EntityCloneFactory $entityCloneFactory
* @param ParameterBagInterface $parameterBag
*/
public function __construct(
EntityManagerInterface $doctrine,
EventDispatcherInterface $eventDispatcher,
FormHandlerInterface $formHandler,
EntityCloneFactory $entityCloneFactory,
ParameterBagInterface $parameterBag
) {
$this->doctrine = $doctrine;
$this->eventDispatcher = $eventDispatcher;
$this->formHandler = $formHandler;
$this->entityCloneFactory = $entityCloneFactory;
$this->parameterBag = $parameterBag;
}
/**
* @return string
*/
abstract protected function getFormTypeClass(): string;
/**
* @param EntityInterface $entity
* @param bool $flush
*
* @throws \Doctrine\Common\Persistence\Mapping\MappingException
* @throws \Doctrine\ORM\ORMException
* @throws \ReflectionException
*/
public function saveResource(EntityInterface $entity, bool $flush = true): void
{
if ($this->doctrine->contains($entity)) {
$this->updateResource($entity, $flush);
return;
}
$this->createResource($entity, $flush);
}
/**
* @param EntityInterface $entity
* @param bool $flush
* @param User|null $user
* @param bool $isGranted
*
* @throws \ReflectionException
*/
public function createResource(EntityInterface $entity, bool $flush = true, User $user = null, $isGranted = true): void
{
$this->dispatchEvent(self::PRE_ENTITY_CREATE_EVENT, $entity);
$this->doctrine->persist($entity);
if ($flush) {
$this->flushResource();
}
$this->dispatchEvent(self::POST_ENTITY_CREATE_EVENT, $entity);
$this->dispatchEvent(self::POST_ENTITY_SAVE_EVENT, $entity);
}
/**
* @param EntityInterface $entity
* @param bool $flush
* @param bool $isGranted
*
* @throws \ReflectionException
*/
public function updateResource(EntityInterface $entity, bool $flush = true, $isGranted = false): void
{
$this->dispatchEvent(self::PRE_ENTITY_UPDATE_EVENT, $entity);
$this->doctrine->persist($entity);
if ($flush) {
$this->flushResource();
}
$this->dispatchEvent(self::POST_ENTITY_UPDATE_EVENT, $entity);
$this->dispatchEvent(self::POST_ENTITY_SAVE_EVENT, $entity);
}
/**
* @param EntityInterface $entity
* @param bool $flush
*
* @throws \ReflectionException
*/
public function removeResource(EntityInterface $entity, bool $flush = true): void
{
$this->dispatchEvent(self::PRE_ENTITY_REMOVE_EVENT, $entity);
$this->doctrine->remove($entity);
if ($flush) {
$this->flushResource();
}
$this->dispatchEvent(self::POST_ENTITY_REMOVE_EVENT, $entity);
}
public function flushResource(): void
{
$this->doctrine->flush();
}
/**
* @return ObjectRepository
*/
public function getRepository(): ObjectRepository
{
return $this->doctrine->getRepository($this->getEntityClass());
}
/**
* @return ClassMetadata
*/
public function getClassMetadata()
{
return $this->doctrine->getClassMetadata($this->getEntityClass());
}
/**
* @param Criteria|null $criteria
*
* @return Collection
*/
public function getCollection(Criteria $criteria = null): Collection
{
return $this->getRepository()->matching($criteria ? $criteria : new Criteria());
}
/**
* @param EntityInterface $data
* @param array $options
* @param string|null $formType
*
* @return FormInterface
*/
public function initForm(EntityInterface $data, array $options = [], string $formType = null): FormInterface
{
if (null === $formType) {
$formType = $this->getFormTypeClass();
}
return $this->formHandler->createForm($data, $options, $formType);
}
/**
* @return EntityInterface
* @throws \ReflectionException
*/
public function initResource(): EntityInterface
{
$class = $this->getEntityClass();
$entity = new $class();
$this->dispatchEvent(self::POST_ENTITY_INIT_EVENT, $entity);
return $entity;
}
/**
* @param string $name
* @param EntityInterface $entity
*
* @throws \ReflectionException
*/
public function dispatchEvent(string $name, EntityInterface $entity): void
{
$eventName = $this->getEventName($entity, $name);
$event = new EntityEvent($entity);
$this->eventDispatcher->dispatch($eventName, $event);
}
/**
* @param EntityInterface $entity
* @param $name
*
* @return string
* @throws \ReflectionException
*/
private function getEventName(EntityInterface $entity, $name): string
{
if ($entity instanceof EventResourceNameInterface) {
$eventResourceName = $entity->getEventResourceName();
} else {
$eventResourceName = (new \ReflectionClass($entity))->getShortName();
}
return sprintf('%s.%s', Inflector::tableize($eventResourceName), $name);
}
/**
* @param EntityInterface $entity
*
* @return ClassMetadata
*/
public function getEntityMetadata(EntityInterface $entity): ClassMetadata
{
return $this->doctrine->getClassMetadata(ClassUtils::getClass($entity));
}
/**
* @param EntityInterface $entity
* @param User $user
*
* @throws \ReflectionException
*/
public function setDeletable(EntityInterface $entity, User $user)
{
if ($this->isDeletable($entity)) {
$entity->setDeletable(true);
$entity->setDeletableAt(Carbon::now());
$entity->setDeletableBy($user);
$this->updateResource($entity);
}
}
/**
* @param EntityInterface $entity
*
* @throws \Exception
*/
public function resetDeletable(EntityInterface $entity)
{
if ($this->isDeletable($entity)) {
$entity->setDeletable(false);
$entity->setDeletableAt(null);
$entity->setDeletableBy(null);
$this->updateResource($entity);
}
}
/**
* @param EntityInterface $entityTo
* @param EntityInterface $entityFrom
* @param User|null $user
*
* @return EntityInterface
* @throws \Exception
*/
public function setCloningParams(EntityInterface $entityTo, EntityInterface $entityFrom = null, User $user = null): EntityInterface
{
if ($this->isEditable($entityTo)) {
$entityTo->setEditable(true);
$entityTo->setEditableAt(Carbon::now());
if ($user) {
$entityTo->setEditableBy($user);
}
if ($entityFrom) {
$entityTo->setParentId($entityFrom ? $entityFrom->getId() : null);
}
$meta = $this->getEntityMetadata($entityTo);
if ($entityFrom && $meta->hasAssociation('route')) {
$entityTo->setRoute();
}
}
return $entityTo;
}
/**
* @param EntityInterface $entity
*
* @return EntityInterface
*/
public function resetCloningParams(EntityInterface $entity): EntityInterface
{
if ($this->isEditable($entity)) {
$entity->setParentId(null);
$entity->setEditable(false);
$entity->setEditableAt(null);
$entity->setEditableBy(null);
}
return $entity;
}
/**
* @param EntityInterface $entityTo
* @param EntityInterface|null $entityFrom
* @param User|null $user
*
* @return EntityInterface
* @throws \Exception
*/
public function updateCloningParams(EntityInterface $entityTo, EntityInterface $entityFrom = null, User $user = null): EntityInterface
{
return $entityTo;
}
/**
* @param EntityInterface $entity
*
* @return bool
*/
public function isEditable(EntityInterface $entity): bool
{
return $entity instanceof EditableInterface;
}
/**
* @param EntityInterface $entity
*
* @return bool
*/
public function isDeletable(EntityInterface $entity): bool
{
return $entity instanceof DeletableInterface;
}
/**
* @param string|null $action
*
* @return array
*/
public function getAcceptanceRoles(string $action = null): array
{
$acceptanceRoles = $this->parameterBag->get('common.acceptance');
$entityClass = ClassUtils::getRealClass($this->getEntityClass());
if (array_key_exists($entityClass, $acceptanceRoles)) {
if (array_key_exists($action, $acceptanceRoles[$entityClass]) && $acceptanceRoles[$entityClass][$action]) {
return $acceptanceRoles[$entityClass][$action];
}
if (array_key_exists('roles', $acceptanceRoles[$entityClass])) {
return $acceptanceRoles[$entityClass]['roles'];
}
}
return [];
}
/**
* @return array
*/
public function getNoDuplicatedField(): array
{
return [];
}
/**
* @return array
*/
public function getNoDuplicatedAssociation(): array
{
return [];
}
/**
* @param EntityInterface $entity
* @param EntityInterface $associationRecord
*
* @return EntityInterface
*/
public function removeRecord(
EntityInterface $entity,
EntityInterface $associationRecord
): EntityInterface {
$this->doctrine->remove($associationRecord);
return $entity;
}
}