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

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($taxonomyEntry, null);
  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. }