<?php
namespace App\EventSubscriber;
use ApiPlatform\Core\EventListener\EventPriorities;
use App\Entity\Company;
use App\Entity\MediaObject;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ViewEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Doctrine\Common\Annotations\AnnotationReader as DocReader;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\PropertyAccess\PropertyAccess;
class KernelEventsSubscriber implements EventSubscriberInterface
{
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var EntityManagerInterface
*/
private $manager;
/**
* List of media objects to be deleted
* @var array
*/
private $suppress = [];
public function __construct(LoggerInterface $logger, EntityManagerInterface $manager)
{
$this->logger = $logger;
$this->manager = $manager;
}
public static function getSubscribedEvents()
{
return [
KernelEvents::VIEW => ['removeMediaObjects', EventPriorities::PRE_WRITE],
KernelEvents::REQUEST => ['logMediaObjects', EventPriorities::PRE_DESERIALIZE],
];
}
/**
* Logs the media objects to be removed later
* @param RequestEvent $event
*/
public function logMediaObjects(RequestEvent $event)
{
$params = $event->getRequest()->attributes->get('_route_params', []);
if(isset($params['_api_item_operation_name']) && ($operation = $params['_api_item_operation_name']) === 'PATCH'){
$class = $params['_api_resource_class'];
$id = $params['id'];
$object = $this->manager->getRepository($class)->findOneBy(['id' => $id]);
try {
$reflection = new \ReflectionClass($class);
$reader = new DocReader();
foreach ($reflection->getProperties() as $property) {
$annotation = $reader->getPropertyAnnotation($property, ORM\ManyToOne::class);
if ($annotation instanceof ORM\ManyToOne && $annotation->targetEntity == MediaObject::class){
$propertyAccessor = PropertyAccess::createPropertyAccessor();
$media = $propertyAccessor->getValue($object, $property->name);
if ($media instanceof MediaObject){
$this->suppress[$class][] = ['name' => $property->name, "id" => $media->getId()];
}
}
}
} catch (\ReflectionException $e) {
$this->logger->error("Unable to register media object: {$e->getMessage()}");
}
}
}
/**
* Removes the media objects logged to be removed
* @param ViewEvent $event
*/
public function removeMediaObjects(ViewEvent $event)
{
$object = $event->getControllerResult();
if (is_object($object) && isset($this->suppress[get_class($object)])){
$propertyAccessor = PropertyAccess::createPropertyAccessor();
$properties = $this->suppress[get_class($object)];
foreach ($properties as $property) {
$media = $propertyAccessor->getValue($object, $property['name']);
if ($media instanceof MediaObject){
$oldId = $property['id'];
if ($oldId !== $media->getId()){
$this->manager->remove($this->manager->getRepository(MediaObject::class)->findOneBy(['id' => $oldId]));
$this->logger->info("Marked media object #$oldId for removal");
}
}
}
$this->manager->flush();
}
}
}