vendor/ibexa/taxonomy/src/lib/Event/Subscriber/ContentEventsSubscriber.php line 109

Open in your IDE?
  1. <?php
  2. /**
  3.  * @copyright Copyright (C) Ibexa AS. All rights reserved.
  4.  * @license For full copyright and license information view LICENSE file distributed with this source code.
  5.  */
  6. declare(strict_types=1);
  7. namespace Ibexa\Taxonomy\Event\Subscriber;
  8. use Doctrine\ORM\EntityManagerInterface;
  9. use Ibexa\Contracts\AdminUi\Event\ContentProxyCreateEvent;
  10. use Ibexa\Contracts\Core\Repository\ContentService;
  11. use Ibexa\Contracts\Core\Repository\Events\Content\DeleteContentEvent;
  12. use Ibexa\Contracts\Core\Repository\Events\Content\PublishVersionEvent;
  13. use Ibexa\Contracts\Core\Repository\LocationService;
  14. use Ibexa\Contracts\Taxonomy\Service\TaxonomyServiceInterface;
  15. use Ibexa\Contracts\Taxonomy\Value\TaxonomyEntryCreateStruct;
  16. use Ibexa\Taxonomy\Exception\TaxonomyEntryInvalidParentException;
  17. use Ibexa\Taxonomy\Exception\TaxonomyEntryNotFoundException;
  18. use Ibexa\Taxonomy\Exception\TaxonomyNotFoundException;
  19. use Ibexa\Taxonomy\FieldType\TaxonomyEntry\Value;
  20. use Ibexa\Taxonomy\Service\TaxonomyConfiguration;
  21. use Psr\Log\LoggerInterface;
  22. use Psr\Log\NullLogger;
  23. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  24. /**
  25.  * @internal
  26.  */
  27. final class ContentEventsSubscriber implements EventSubscriberInterface
  28. {
  29.     private EntityManagerInterface $entityManager;
  30.     private TaxonomyServiceInterface $taxonomyService;
  31.     private TaxonomyConfiguration $taxonomyConfiguration;
  32.     private LocationService $locationService;
  33.     private ContentService $contentService;
  34.     private LoggerInterface $logger;
  35.     public function __construct(
  36.         EntityManagerInterface $entityManager,
  37.         TaxonomyServiceInterface $taxonomyService,
  38.         TaxonomyConfiguration $taxonomyConfiguration,
  39.         LocationService $locationService,
  40.         ContentService $contentService,
  41.         ?LoggerInterface $logger null
  42.     ) {
  43.         $this->entityManager $entityManager;
  44.         $this->taxonomyService $taxonomyService;
  45.         $this->taxonomyConfiguration $taxonomyConfiguration;
  46.         $this->locationService $locationService;
  47.         $this->contentService $contentService;
  48.         $this->logger $logger ?? new NullLogger();
  49.     }
  50.     public static function getSubscribedEvents(): array
  51.     {
  52.         return [
  53.             DeleteContentEvent::class => 'onContentDelete',
  54.             PublishVersionEvent::class => 'onVersionPublish',
  55.             ContentProxyCreateEvent::class => 'onContentProxyCreate',
  56.         ];
  57.     }
  58.     public function onContentDelete(DeleteContentEvent $event): void
  59.     {
  60.         $content $event->getContentInfo();
  61.         $contentType $content->getContentType();
  62.         if (!$this->taxonomyConfiguration->isContentTypeAssociatedWithTaxonomy($contentType)) {
  63.             return;
  64.         }
  65.         try {
  66.             $taxonomyEntry $this->taxonomyService->loadEntryByContentId($content->id);
  67.         } catch (TaxonomyEntryNotFoundException $e) {
  68.             $this->logger->error(
  69.                 "Cannot find TaxonomyEntry for Content ID: {$content->id}. "
  70.                 'This may be an orphan Content item left after corresponding TaxonomyEntry was removed.',
  71.                 [
  72.                     'exception' => $e,
  73.                 ],
  74.             );
  75.             return;
  76.         }
  77.         // This operation will be performed without the limit and may result in a timeout when running in FPM.
  78.         // Repository Event Listeners are running outside the main transaction and
  79.         // breaking operation here would result in creating orphan content items.
  80.         // UI has configurable size of the subtree that can be removed at once to prevent timeouts.
  81.         $childrenEntry $this->taxonomyService->loadEntryChildren($taxonomyEntrynull);
  82.         foreach ($childrenEntry as $childEntry) {
  83.             $this->contentService->deleteContent($childEntry->content->contentInfo);
  84.         }
  85.         $this->taxonomyService->removeEntry($taxonomyEntry);
  86.         $this->entityManager->flush();
  87.     }
  88.     public function onVersionPublish(PublishVersionEvent $event): void
  89.     {
  90.         $content $event->getContent();
  91.         $versionInfo $event->getVersionInfo();
  92.         // we only create entry once when first version is published
  93.         if ($versionInfo->versionNo 1) {
  94.             return;
  95.         }
  96.         $contentType $content->getContentType();
  97.         try {
  98.             $taxonomy $this->taxonomyConfiguration->getTaxonomyForContentType($contentType);
  99.         } catch (TaxonomyNotFoundException $exception) {
  100.             // this will occur for all content types so no need to spam logs
  101.             return;
  102.         }
  103.         $createStruct = new TaxonomyEntryCreateStruct(
  104.             $this->taxonomyConfiguration->getEntryIdentifierFieldFromContent($content),
  105.             $taxonomy,
  106.             $this->taxonomyConfiguration->getEntryParentFieldFromContent($content),
  107.             $content
  108.         );
  109.         $this->taxonomyService->createEntry($createStruct);
  110.     }
  111.     public function onContentProxyCreate(ContentProxyCreateEvent $event): void
  112.     {
  113.         $options $event->getOptions();
  114.         if (!$options->has(ContentProxyCreateEvent::OPTION_CONTENT_DRAFT)) {
  115.             return;
  116.         }
  117.         /** @var \Ibexa\Core\Repository\Values\Content\Content $contentDraft */
  118.         $contentDraft $event->getOptions()->get(ContentProxyCreateEvent::OPTION_CONTENT_DRAFT);
  119.         $contentType $contentDraft->getContentType();
  120.         try {
  121.             $taxonomy $this->taxonomyConfiguration->getTaxonomyForContentType($contentType);
  122.         } catch (TaxonomyNotFoundException $exception) {
  123.             return;
  124.         }
  125.         $taxonomyParentLocation $this->locationService->loadLocationByRemoteId(
  126.             $this->taxonomyConfiguration->getConfigForTaxonomy(
  127.                 $taxonomy,
  128.                 TaxonomyConfiguration::CONFIG_PARENT_LOCATION_REMOTE_ID
  129.             )
  130.         );
  131.         if ($taxonomyParentLocation->id !== $event->getParentLocationId()) {
  132.             throw TaxonomyEntryInvalidParentException::createWithTaxonomyName($taxonomy);
  133.         }
  134.         $contentUpdateStruct $this->contentService->newContentUpdateStruct();
  135.         $fieldIdentifier $this->taxonomyConfiguration->getFieldMappings($taxonomy)['parent'];
  136.         $contentUpdateStruct->setField($fieldIdentifier, new Value($options->get('parent_entry')));
  137.         $this->contentService->updateContent($contentDraft->getVersionInfo(), $contentUpdateStruct, []);
  138.     }
  139. }