custom/plugins/SasBlogModule/src/SasBlogModule.php line 31

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Sas\BlogModule;
  3. use Doctrine\DBAL\Connection;
  4. use Doctrine\DBAL\Driver\Result;
  5. use Sas\BlogModule\Content\Blog\BlogEntriesDefinition;
  6. use Sas\BlogModule\Content\Blog\BlogSeoUrlRoute;
  7. use Sas\BlogModule\Content\Blog\Events\BlogIndexerEvent;
  8. use Sas\BlogModule\Util\Lifecycle;
  9. use Sas\BlogModule\Util\Update;
  10. use Shopware\Core\Content\Media\Aggregate\MediaThumbnailSize\MediaThumbnailSizeEntity;
  11. use Shopware\Core\Content\Seo\SeoUrlTemplate\SeoUrlTemplateCollection;
  12. use Shopware\Core\Content\Seo\SeoUrlTemplate\SeoUrlTemplateEntity;
  13. use Shopware\Core\Defaults;
  14. use Shopware\Core\Framework\Context;
  15. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  16. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  17. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
  18. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  19. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
  20. use Shopware\Core\Framework\Plugin;
  21. use Shopware\Core\Framework\Plugin\Context\InstallContext;
  22. use Shopware\Core\Framework\Plugin\Context\UninstallContext;
  23. use Shopware\Core\Framework\Plugin\Context\UpdateContext;
  24. use Shopware\Core\Framework\Uuid\Uuid;
  25. use Shopware\Core\Kernel;
  26. use Shopware\Core\System\SystemConfig\SystemConfigService;
  27. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  28. class SasBlogModule extends Plugin
  29. {
  30.     public const ANONYMOUS_AUTHOR_ID '64f4c60194634128b9b85d9299797c45';
  31.     public function install(InstallContext $installContext): void
  32.     {
  33.         parent::install($installContext);
  34.         $this->createBlogMediaFolder($installContext->getContext());
  35.         $this->getLifeCycle()->install($installContext->getContext());
  36.     }
  37.     public function uninstall(UninstallContext $context): void
  38.     {
  39.         parent::uninstall($context);
  40.         if ($context->keepUserData()) {
  41.             return;
  42.         }
  43.         /*
  44.          * We need to uninstall our default media folder,
  45.          * the media folder and the thumbnail sizes.
  46.          * However, we have to clean this up within a next update :)
  47.          */
  48.         $this->deleteMediaFolder($context->getContext());
  49.         $this->deleteDefaultMediaFolder($context->getContext());
  50.         $this->deleteSeoUrlTemplate($context->getContext());
  51.         /**
  52.          * And of course we need to drop our tables
  53.          */
  54.         $connection Kernel::getConnection();
  55.         $connection->executeStatement('SET FOREIGN_KEY_CHECKS=0;');
  56.         $connection->executeStatement('DROP TABLE IF EXISTS `sas_blog_entries`');
  57.         $connection->executeStatement('DROP TABLE IF EXISTS `sas_blog_entries_translation`');
  58.         $connection->executeStatement('DROP TABLE IF EXISTS `sas_blog_blog_category`');
  59.         $connection->executeStatement('DROP TABLE IF EXISTS `sas_blog_category_translation`');
  60.         $connection->executeStatement('DROP TABLE IF EXISTS `sas_blog_category`');
  61.         $connection->executeStatement('DROP TABLE IF EXISTS `sas_blog_author_translation`');
  62.         $connection->executeStatement('DROP TABLE IF EXISTS `sas_blog_author`');
  63.         /** @var EntityRepositoryInterface $cmsBlockRepo */
  64.         $cmsBlockRepo $this->container->get('cms_block.repository');
  65.         $context Context::createDefaultContext();
  66.         $criteria = new Criteria();
  67.         $criteria->addFilter(new EqualsAnyFilter('type', ['blog-detail''blog-listing']));
  68.         $cmsBlocks $cmsBlockRepo->searchIds($criteria$context);
  69.         $cmsBlockRepo->delete(array_values($cmsBlocks->getData()), $context);
  70.         $connection->executeQuery('SET FOREIGN_KEY_CHECKS=1;');
  71.     }
  72.     public function update(UpdateContext $updateContext): void
  73.     {
  74.         parent::update($updateContext);
  75.         (new Update())->update($this->container$updateContext);
  76.         if (version_compare($updateContext->getCurrentPluginVersion(), '1.1.0''<')) {
  77.             $this->createBlogMediaFolder($updateContext->getContext());
  78.         }
  79.         if (version_compare($updateContext->getCurrentPluginVersion(), '1.5.10''<')) {
  80.             $this->fixSeoUrlTemplate($updateContext->getContext());
  81.             $this->updateSeoUrls($updateContext->getContext());
  82.         }
  83.     }
  84.     /**
  85.      * We need to create a folder for the blog media with it's,
  86.      * own configuration to generate thumbnails for the teaser image.
  87.      */
  88.     public function createBlogMediaFolder(Context $context): void
  89.     {
  90.         $this->deleteDefaultMediaFolder($context);
  91.         $thumbnailSizes $this->getThumbnailSizes($context);
  92.         /** @var EntityRepositoryInterface $mediaFolderRepository */
  93.         $mediaFolderRepository $this->container->get('media_default_folder.repository');
  94.         $data = [
  95.             [
  96.                 'entity' => BlogEntriesDefinition::ENTITY_NAME,
  97.                 'associationFields' => ['media'],
  98.                 'folder' => [
  99.                     'name' => 'Blog Images',
  100.                     'useParentConfiguration' => false,
  101.                     'configuration' => [
  102.                         'createThumbnails' => true,
  103.                         'keepAspectRatio' => true,
  104.                         'thumbnailQuality' => 90,
  105.                         'mediaThumbnailSizes' => $thumbnailSizes,
  106.                     ],
  107.                 ],
  108.             ],
  109.         ];
  110.         $mediaFolderRepository->create($data$context);
  111.     }
  112.     private function deleteDefaultMediaFolder(Context $context): void
  113.     {
  114.         $criteria = new Criteria();
  115.         $criteria->addFilter(
  116.             new EqualsAnyFilter('entity', [
  117.                 BlogEntriesDefinition::ENTITY_NAME,
  118.             ])
  119.         );
  120.         /** @var EntityRepositoryInterface $mediaFolderRepository */
  121.         $mediaFolderRepository $this->container->get('media_default_folder.repository');
  122.         $mediaFolderIds $mediaFolderRepository->searchIds($criteria$context)->getIds();
  123.         if (!empty($mediaFolderIds)) {
  124.             $ids array_map(static function ($id) {
  125.                 return ['id' => $id];
  126.             }, $mediaFolderIds);
  127.             $mediaFolderRepository->delete($ids$context);
  128.         }
  129.     }
  130.     private function deleteMediaFolder(Context $context): void
  131.     {
  132.         $criteria = new Criteria();
  133.         $criteria->addFilter(
  134.             new EqualsFilter('name''Blog Images')
  135.         );
  136.         /** @var EntityRepositoryInterface $mediaFolderRepository */
  137.         $mediaFolderRepository $this->container->get('media_folder.repository');
  138.         $mediaFolderRepository->search($criteria$context);
  139.         $mediaFolderIds $mediaFolderRepository->searchIds($criteria$context)->getIds();
  140.         if (!empty($mediaFolderIds)) {
  141.             $ids array_map(static function ($id) {
  142.                 return ['id' => $id];
  143.             }, $mediaFolderIds);
  144.             $mediaFolderRepository->delete($ids$context);
  145.         }
  146.     }
  147.     private function deleteSeoUrlTemplate(Context $context): void
  148.     {
  149.         $criteria = new Criteria();
  150.         $criteria->addFilter(
  151.             new EqualsFilter('entityName''sas_blog_entries')
  152.         );
  153.         /** @var EntityRepositoryInterface $seoUrlTemplateRepository */
  154.         $seoUrlTemplateRepository $this->container->get('seo_url_template.repository');
  155.         $seoUrlTemplateRepository->search($criteria$context);
  156.         $seoUrlTemplateIds $seoUrlTemplateRepository->searchIds($criteria$context)->getIds();
  157.         if (!empty($seoUrlTemplateIds)) {
  158.             $ids array_map(static function ($id) {
  159.                 return ['id' => $id];
  160.             }, $seoUrlTemplateIds);
  161.             $seoUrlTemplateRepository->delete($ids$context);
  162.         }
  163.     }
  164.     private function getThumbnailSizes(Context $context): array
  165.     {
  166.         $mediaThumbnailSizes = [
  167.             '330x185' => [
  168.                 'width' => 330,
  169.                 'height' => 185,
  170.             ],
  171.             '650x365' => [
  172.                 'width' => 650,
  173.                 'height' => 365,
  174.             ],
  175.             '900x506' => [
  176.                 'width' => 900,
  177.                 'height' => 506,
  178.             ],
  179.             '1280x720' => [
  180.                 'width' => 1280,
  181.                 'height' => 720,
  182.             ],
  183.         ];
  184.         $criteria = new Criteria();
  185.         /** @var EntityRepositoryInterface $thumbnailSizeRepository */
  186.         $thumbnailSizeRepository $this->container->get('media_thumbnail_size.repository');
  187.         $thumbnailSizes $thumbnailSizeRepository->search($criteria$context)->getEntities();
  188.         $mediaThumbnailSizesAddedIds = [];
  189.         /** @var MediaThumbnailSizeEntity $thumbnailSize */
  190.         foreach ($thumbnailSizes as $thumbnailSize) {
  191.             $key $thumbnailSize->getWidth() . 'x' $thumbnailSize->getHeight();
  192.             if (\array_key_exists($key$mediaThumbnailSizes)) {
  193.                 $mediaThumbnailSize $mediaThumbnailSizes[$key];
  194.                 $mediaThumbnailSizesAddedIds[$key] = array_merge(
  195.                     ['id' => $thumbnailSize->getId()],
  196.                     $mediaThumbnailSize,
  197.                 );
  198.                 unset($mediaThumbnailSizes[$key]);
  199.             }
  200.         }
  201.         $mediaThumbnailSizesCreateData = [];
  202.         foreach ($mediaThumbnailSizes as $key => $mediaThumbnailSize) {
  203.             $data array_merge(
  204.                 ['id' => Uuid::randomHex()],
  205.                 $mediaThumbnailSize,
  206.             );
  207.             $mediaThumbnailSizesCreateData[$key] = $data;
  208.             $mediaThumbnailSizesAddedIds[$key] = $data;
  209.         }
  210.         if (\count($mediaThumbnailSizesCreateData) > 0) {
  211.             $thumbnailSizeRepository->create(array_values($mediaThumbnailSizesCreateData), $context);
  212.         }
  213.         return array_values($mediaThumbnailSizesAddedIds);
  214.     }
  215.     private function getLifeCycle(): Lifecycle
  216.     {
  217.         /** @var SystemConfigService $systemConfig */
  218.         $systemConfig $this->container->get(SystemConfigService::class);
  219.         /** @var EntityRepositoryInterface $cmsPageRepository */
  220.         $cmsPageRepository $this->container->get('cms_page.repository');
  221.         return new Lifecycle(
  222.             $systemConfig,
  223.             $cmsPageRepository
  224.         );
  225.     }
  226.     private function fixSeoUrlTemplate(Context $context): void
  227.     {
  228.         $criteria = new Criteria();
  229.         $criteria->addFilter(new EqualsFilter('routeName'BlogSeoUrlRoute::ROUTE_NAME));
  230.         $criteria->addFilter(new EqualsFilter('entityName'BlogEntriesDefinition::ENTITY_NAME));
  231.         $criteria->addFilter(new NotFilter(
  232.             NotFilter::CONNECTION_AND,
  233.             [new EqualsFilter('template'null)]
  234.         ));
  235.         /** @var EntityRepositoryInterface $seoUrlTemplateRepository */
  236.         $seoUrlTemplateRepository $this->container->get('seo_url_template.repository');
  237.         /** @var SeoUrlTemplateCollection $seoUrlTemplates */
  238.         $seoUrlTemplates $seoUrlTemplateRepository->search($criteria$context)->getEntities();
  239.         $update = [];
  240.         /** @var SeoUrlTemplateEntity $seoUrlTemplate */
  241.         foreach ($seoUrlTemplates as $seoUrlTemplate) {
  242.             // If the clients fixed it in SEO template we don't have to do again
  243.             if (strpos($seoUrlTemplate->getTemplate(), 'entry.translated')) {
  244.                 continue;
  245.             }
  246.             // We found the issue
  247.             if (!strpos($seoUrlTemplate->getTemplate(), 'entry.title')) {
  248.                 continue;
  249.             }
  250.             $templateReplaced str_replace('entry.title''entry.translated.title'$seoUrlTemplate->getTemplate());
  251.             if (!\is_string($templateReplaced)) {
  252.                 continue;
  253.             }
  254.             $update[] = [
  255.                 'id' => $seoUrlTemplate->getId(),
  256.                 'template' => $templateReplaced,
  257.             ];
  258.         }
  259.         if (\count($update) === 0) {
  260.             return;
  261.         }
  262.         $seoUrlTemplateRepository->update($update$context);
  263.     }
  264.     private function updateSeoUrls(Context $context): void
  265.     {
  266.         $blogArticlesIds $this->getBlogArticlesIds();
  267.         if (\count($blogArticlesIds) === 0) {
  268.             return;
  269.         }
  270.         if ($this->container->get('event_dispatcher') instanceof EventDispatcherInterface) {
  271.             $eventDispatcher $this->container->get('event_dispatcher');
  272.             $eventDispatcher->dispatch(new BlogIndexerEvent($blogArticlesIds$context));
  273.         }
  274.     }
  275.     private function getBlogArticlesIds(): array
  276.     {
  277.         /** @var Connection $connection */
  278.         $connection $this->container->get(Connection::class);
  279.         if (!$connection->getSchemaManager()->tablesExist([BlogEntriesDefinition::ENTITY_NAME])) {
  280.             return [];
  281.         }
  282.         $now = (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT);
  283.         $query $connection->createQueryBuilder();
  284.         $query->select([
  285.             'LOWER(HEX(id)) as id',
  286.         ]);
  287.         $query->where('active = true')->andWhere('published_at <= :now');
  288.         $query->setParameter('now'$now);
  289.         $query->from(BlogEntriesDefinition::ENTITY_NAME);
  290.         if (!$query->execute() instanceof Result) {
  291.             return [];
  292.         }
  293.         $results $query->execute()->fetchAllAssociative();
  294.         if (empty($results)) {
  295.             return [];
  296.         }
  297.         return array_column($results'id');
  298.     }
  299. }