From 45946f1ec4f48d33b1c69bbe9e6425dc26096432 Mon Sep 17 00:00:00 2001 From: Lucas Simeon Date: Wed, 5 Jun 2024 19:46:54 +0200 Subject: [PATCH] Add symfony command to remove expired anonymous wishlists To reduce database table size generated by guest customers --- doc/01-installation.md | 11 ++++ .../RemoveAnonymousWishlistsCommand.php | 55 +++++++++++++++++++ .../BitBagSyliusWishlistExtension.php | 1 + src/DependencyInjection/Configuration.php | 13 +++++ src/Remover/AnonymousWishlistsRemover.php | 32 +++++++++++ .../AnonymousWishlistsRemoverInterface.php | 17 ++++++ src/Repository/WishlistRepository.php | 12 ++++ .../WishlistRepositoryInterface.php | 5 ++ src/Resources/config/services.yml | 13 +++++ ..._delete_all_anonymous_until_wishlists.yaml | 46 ++++++++++++++++ .../Repository/WishlistRepositoryTest.php | 16 ++++++ 11 files changed, 221 insertions(+) create mode 100644 src/Console/RemoveAnonymousWishlistsCommand.php create mode 100644 src/Remover/AnonymousWishlistsRemover.php create mode 100644 src/Remover/AnonymousWishlistsRemoverInterface.php create mode 100644 tests/Integration/DataFixtures/ORM/test_it_delete_all_anonymous_until_wishlists.yaml diff --git a/doc/01-installation.md b/doc/01-installation.md index 382c5204..9c55afb5 100644 --- a/doc/01-installation.md +++ b/doc/01-installation.md @@ -111,3 +111,14 @@ framework: ``` All commands from the plugin implement the `WishlistSyncCommandInterface` interface, so there is no need for other configuration. + +## Removing anonymous wishlists after expiration period + +You can remove anonymous wishlists that have not been updated for a specified period of time. To do so, you need to add `bitbag:remove-anonymous-wishlists` Symfony console command to your cron jobs. + +You can specify the expiration period in your parameters file to override the default value of 30 days: + +```yaml +parameters: + bitbag_sylius_wishlist_plugin.parameters.anonymous_wishlist_expiration_period: 30 days # Remove all anonymous wishlists that were updated more than 30 days ago. +``` \ No newline at end of file diff --git a/src/Console/RemoveAnonymousWishlistsCommand.php b/src/Console/RemoveAnonymousWishlistsCommand.php new file mode 100644 index 00000000..c64b4f14 --- /dev/null +++ b/src/Console/RemoveAnonymousWishlistsCommand.php @@ -0,0 +1,55 @@ +setDescription('Removes anonymous wishlists that have been idle for a period set in `bitbag_sylius_wishlist_plugin.parameters.anonymous_wishlist_expiration_period` configuration key.') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + /** @var string $expirationTime */ + $expirationTime = $this->getContainer()->getParameter('bitbag_sylius_wishlist_plugin.parameters.anonymous_wishlist_expiration_period'); + + if (empty($expirationTime)) { + $output->writeln('`bitbag_sylius_wishlist_plugin.parameters.anonymous_wishlist_expiration_period` configuration key is not set, so no wishlists will be removed.'); + + return 0; + } + + $output->writeln(sprintf( + 'Command will remove anonymous wishlists that have been idle for %s.', + (string) $expirationTime, + )); + + /** @var AnonymousWishlistsRemoverInterface $anonymousWishlistsRemover */ + $anonymousWishlistsRemover = $this->getContainer()->get('bitbag_sylius_wishlist_plugin.services.anonymous_wishlists_remover'); + $anonymousWishlistsRemover->remove(); + + return 0; + } +} diff --git a/src/DependencyInjection/BitBagSyliusWishlistExtension.php b/src/DependencyInjection/BitBagSyliusWishlistExtension.php index 338b482a..4db58e1f 100644 --- a/src/DependencyInjection/BitBagSyliusWishlistExtension.php +++ b/src/DependencyInjection/BitBagSyliusWishlistExtension.php @@ -29,6 +29,7 @@ public function load(array $config, ContainerBuilder $container): void $this->registerResources('bitbag_sylius_wishlist_plugin', 'doctrine/orm', $config['resources'], $container); $loader->load('services.yml'); $container->setParameter('bitbag_sylius_wishlist_plugin.parameters.wishlist_cookie_token', $config['wishlist_cookie_token']); + $container->setParameter('bitbag_sylius_wishlist_plugin.parameters.anonymous_wishlist_expiration_period', $config['anonymous_wishlist_expiration_period']); $container->setParameter('bitbag_sylius_wishlist_plugin.parameters.allowed_mime_types', $config['allowed_mime_types']); } diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index af32f9cc..b2a8e5ce 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -45,6 +45,19 @@ public function getConfigTreeBuilder(): TreeBuilder }) ->end() ->end() + ->scalarNode('anonymous_wishlist_expiration_period') + ->defaultValue('30 days') + ->cannotBeEmpty() + ->validate() + ->always(function ($value) { + if (!is_string($value)) { + throw new InvalidConfigurationException('anonymous_wishlist_expiration_period must be string'); + } + + return $value; + }) + ->end() + ->end() ->arrayNode('allowed_mime_types') ->defaultValue([ 'text/csv', diff --git a/src/Remover/AnonymousWishlistsRemover.php b/src/Remover/AnonymousWishlistsRemover.php new file mode 100644 index 00000000..248e4b21 --- /dev/null +++ b/src/Remover/AnonymousWishlistsRemover.php @@ -0,0 +1,32 @@ +wishlistRepository->deleteAllAnonymousUntil( + new \DateTime('-' . $this->expirationPeriod), + ); + } +} diff --git a/src/Remover/AnonymousWishlistsRemoverInterface.php b/src/Remover/AnonymousWishlistsRemoverInterface.php new file mode 100644 index 00000000..9d609013 --- /dev/null +++ b/src/Remover/AnonymousWishlistsRemoverInterface.php @@ -0,0 +1,17 @@ +getOneOrNullResult() ; } + + public function deleteAllAnonymousUntil(\DateTime $until): int + { + return $this->createQueryBuilder('o') + ->delete(WishlistInterface::class, 'o') + ->where('o.shopUser IS NULL') + ->andWhere('o.updatedAt < :until') + ->setParameter('until', $until) + ->getQuery() + ->execute() + ; + } } diff --git a/src/Repository/WishlistRepositoryInterface.php b/src/Repository/WishlistRepositoryInterface.php index b7f685f2..6f72e664 100644 --- a/src/Repository/WishlistRepositoryInterface.php +++ b/src/Repository/WishlistRepositoryInterface.php @@ -40,4 +40,9 @@ public function findAllByAnonymousAndChannel(?string $token, ChannelInterface $c public function findOneByTokenAndName(string $token, string $name): ?WishlistInterface; public function findOneByShopUserAndName(ShopUserInterface $shopUser, string $name): ?WishlistInterface; + + /** + * @return int Number of deleted wishlists. + */ + public function deleteAllAnonymousUntil(\DateTime $until): int; } diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 350e05c5..bdcad2ae 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -2,6 +2,11 @@ imports: - { resource: "services/**/*.yml" } services: + bitbag_sylius_wishlist_plugin.console.remove_anonymous_wishlists_command: + class: BitBag\SyliusWishlistPlugin\Console\RemoveAnonymousWishlistsCommand + tags: + - { name: console.command } + bitbag_sylius_wishlist_plugin.controller.action.base_wishlist_products_action: abstract: true class: BitBag\SyliusWishlistPlugin\Controller\Action\BaseWishlistProductsAction @@ -348,6 +353,14 @@ services: - '@request_stack' - "@translator" + bitbag_sylius_wishlist_plugin.services.anonymous_wishlists_remover: + public: true + class: BitBag\SyliusWishlistPlugin\Remover\AnonymousWishlistsRemover + arguments: + - "@bitbag_sylius_wishlist_plugin.repository.wishlist" + - "@bitbag_sylius_wishlist_plugin.manager.wishlist" + - "%bitbag_sylius_wishlist_plugin.parameters.anonymous_wishlist_expiration_period%" + bitbag_sylius_wishlist_plugin.checker.wishlist_name_checker: class: BitBag\SyliusWishlistPlugin\Checker\WishlistNameChecker diff --git a/tests/Integration/DataFixtures/ORM/test_it_delete_all_anonymous_until_wishlists.yaml b/tests/Integration/DataFixtures/ORM/test_it_delete_all_anonymous_until_wishlists.yaml new file mode 100644 index 00000000..6937b1db --- /dev/null +++ b/tests/Integration/DataFixtures/ORM/test_it_delete_all_anonymous_until_wishlists.yaml @@ -0,0 +1,46 @@ +Sylius\Component\Locale\Model\Locale: + locale: + createdAt: '' + code: 'en_US' +Sylius\Component\Currency\Model\Currency: + dollar: + code: 'USD' +Sylius\Component\Core\Model\Channel: + channel_us: + code: 'US' + name: 'name' + defaultLocale: '@locale' + locales: [ '@locale' ] + taxCalculationStrategy: 'order_items_based' + baseCurrency: '@dollar' + enabled: true +Sylius\Component\Core\Model\Customer: + customer_oliver: + firstName: 'John' + lastName: 'Nowak' + email: 'oliver@queen.com' + emailCanonical: 'test2@example.com' +Sylius\Component\Core\Model\ShopUser: + user_oliver: + plainPassword: '123password' + roles: [ 'ROLE_USER' ] + enabled: 'true' + customer: '@customer_oliver' + username: 'oliver@queen.com' + usernameCanonical: 'oliver@queen.com' +BitBag\SyliusWishlistPlugin\Entity\Wishlist: + wishlist_one: + name: 'Wishlist One' + channel: '@channel_us' + token: 'token' + updatedAt: '' + wishlist_two: + name: 'Wishlist Two' + channel: '@channel_us' + token: 'token' + updatedAt: '' + olivier_wishlist: + name: 'Olivier Wishlist' + shopUser: '@user_oliver' + channel: '@channel_us' + updatedAt: '' \ No newline at end of file diff --git a/tests/Integration/Repository/WishlistRepositoryTest.php b/tests/Integration/Repository/WishlistRepositoryTest.php index 1bf04da7..8d8e6d86 100644 --- a/tests/Integration/Repository/WishlistRepositoryTest.php +++ b/tests/Integration/Repository/WishlistRepositoryTest.php @@ -182,4 +182,20 @@ public function testItFindsOneWishlistByShopUserAndName(): void $missingResult = $this->repository->findOneByShopUserAndName($shopUser, 'Bruce Wishlist'); $this->assertNull($missingResult); } + + public function testItDeleteAllAnonymousUntilWishlists(): void + { + $this->loadFixturesFromFile('test_it_delete_all_anonymous_until_wishlists.yaml'); + + /** @var int $result */ + $result = $this->repository->deleteAllAnonymousUntil(new \DateTime('2024-01-01 00:00:00')); + + $this->assertIsInt($result); + $this->assertSame(1, $result); + + $wishlists = $this->repository->findAll(); + $this->assertCount(2, $wishlists); + $this->assertSame('Wishlist Two', $wishlists[0]->getName()); + $this->assertSame('Olivier Wishlist', $wishlists[1]->getName()); + } }