From aed62a1386eb611fc9330bf3d25f498cc46f545b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joachim=20L=C3=B8vgaard?= Date: Sat, 2 Jan 2021 17:06:26 +0100 Subject: [PATCH 1/5] Adding not found resource to log 404s --- composer.json | 1 + src/DependencyInjection/Configuration.php | 22 +++++- src/EventListener/NotFoundSubscriber.php | 32 +++++++-- src/Model/NotFound.php | 69 +++++++++++++++++++ src/Model/NotFoundInterface.php | 32 +++++++++ .../config/doctrine/model/NotFound.orm.xml | 23 +++++++ tests/Application/.env | 2 +- 7 files changed, 175 insertions(+), 6 deletions(-) create mode 100644 src/Model/NotFound.php create mode 100644 src/Model/NotFoundInterface.php create mode 100644 src/Resources/config/doctrine/model/NotFound.orm.xml diff --git a/composer.json b/composer.json index 0731cd5..b5ecfb2 100644 --- a/composer.json +++ b/composer.json @@ -28,6 +28,7 @@ "webmozart/assert": "^1.9" }, "require-dev": { + "doctrine/doctrine-bundle": "^1.12.13", "phpspec/phpspec": "^6.1", "phpunit/phpunit": "^8.5", "roave/security-advisories": "dev-master", diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 6b6c211..dba3d44 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -5,9 +5,11 @@ namespace Setono\SyliusRedirectPlugin\DependencyInjection; use Setono\SyliusRedirectPlugin\Form\Type\RedirectType; +use Setono\SyliusRedirectPlugin\Model\NotFound; use Setono\SyliusRedirectPlugin\Model\Redirect; use Setono\SyliusRedirectPlugin\Repository\RedirectRepository; use Sylius\Bundle\ResourceBundle\Controller\ResourceController; +use Sylius\Bundle\ResourceBundle\Form\Type\DefaultResourceType; use Sylius\Bundle\ResourceBundle\SyliusResourceBundle; use Sylius\Component\Resource\Factory\Factory; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; @@ -26,7 +28,9 @@ public function getConfigTreeBuilder(): TreeBuilder $rootNode ->addDefaultsIfNotSet() ->children() - ->scalarNode('driver')->defaultValue(SyliusResourceBundle::DRIVER_DOCTRINE_ORM)->end() + ->scalarNode('driver') + ->defaultValue(SyliusResourceBundle::DRIVER_DOCTRINE_ORM) + ->end() ->integerNode('remove_after') ->info('0 means disabled. If the value is > 0 then redirects that have not been accessed in the last x days will be removed') ->defaultValue(0) @@ -61,6 +65,22 @@ private function addResourcesSection(ArrayNodeDefinition $node): void ->end() ->end() ->end() + ->arrayNode('not_found') + ->addDefaultsIfNotSet() + ->children() + ->variableNode('options')->end() + ->arrayNode('classes') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('model')->defaultValue(NotFound::class)->cannotBeEmpty()->end() + ->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end() + ->scalarNode('repository')->cannotBeEmpty()->end() + ->scalarNode('factory')->defaultValue(Factory::class)->end() + ->scalarNode('form')->defaultValue(DefaultResourceType::class)->cannotBeEmpty()->end() + ->end() + ->end() + ->end() + ->end() ->end() ->end() ->end() diff --git a/src/EventListener/NotFoundSubscriber.php b/src/EventListener/NotFoundSubscriber.php index 19d8766..5c728b9 100644 --- a/src/EventListener/NotFoundSubscriber.php +++ b/src/EventListener/NotFoundSubscriber.php @@ -5,17 +5,21 @@ namespace Setono\SyliusRedirectPlugin\EventListener; use Doctrine\Common\Persistence\ObjectManager; +use Setono\SyliusRedirectPlugin\Model\NotFoundInterface; +use Setono\SyliusRedirectPlugin\Model\RedirectionPath; use Setono\SyliusRedirectPlugin\Resolver\RedirectionPathResolverInterface; use Sylius\Component\Channel\Context\ChannelContextInterface; +use Sylius\Component\Resource\Factory\FactoryInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\ExceptionEvent; use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\KernelEvents; use Webmozart\Assert\Assert; -class NotFoundSubscriber implements EventSubscriberInterface +final class NotFoundSubscriber implements EventSubscriberInterface { /** @var ObjectManager */ private $objectManager; @@ -26,14 +30,19 @@ class NotFoundSubscriber implements EventSubscriberInterface /** @var RedirectionPathResolverInterface */ private $redirectionPathResolver; + /** @var FactoryInterface */ + private $notFoundFactory; + public function __construct( ObjectManager $objectManager, ChannelContextInterface $channelContext, - RedirectionPathResolverInterface $redirectionPathResolver + RedirectionPathResolverInterface $redirectionPathResolver, + FactoryInterface $notFoundFactory ) { $this->objectManager = $objectManager; $this->channelContext = $channelContext; $this->redirectionPathResolver = $redirectionPathResolver; + $this->notFoundFactory = $notFoundFactory; } public static function getSubscribedEvents(): array @@ -59,11 +68,26 @@ public function onKernelException(ExceptionEvent $event): void ); if ($redirectionPath->isEmpty()) { - return; + $this->log($event->getRequest()); + } else { + $this->redirect($event, $redirectionPath); } - $redirectionPath->markAsAccessed(); $this->objectManager->flush(); + } + + private function log(Request $request): void + { + /** @var NotFoundInterface $notFound */ + $notFound = $this->notFoundFactory->createNew(); + $notFound->onRequest($request); + + $this->objectManager->persist($notFound); + } + + private function redirect(ExceptionEvent $event, RedirectionPath $redirectionPath): void + { + $redirectionPath->markAsAccessed(); $lastRedirect = $redirectionPath->last(); Assert::notNull($lastRedirect); diff --git a/src/Model/NotFound.php b/src/Model/NotFound.php new file mode 100644 index 0000000..884d705 --- /dev/null +++ b/src/Model/NotFound.php @@ -0,0 +1,69 @@ +id; + } + + public function getUrl(): ?string + { + return $this->url; + } + + public function setUrl(string $url): void + { + $this->url = $url; + } + + public function getCount(): int + { + return $this->count; + } + + public function setCount(int $count): void + { + $this->count = $count; + } + + public function getLastRequestAt(): ?DateTimeInterface + { + return $this->lastRequestAt; + } + + public function setLastRequestAt(DateTimeInterface $lastRequestAt): void + { + $this->lastRequestAt = $lastRequestAt; + } + + public function onRequest(Request $request): void + { + ++$this->count; + $this->lastRequestAt = new DateTime(); + $this->url = $request->getUri(); + } +} diff --git a/src/Model/NotFoundInterface.php b/src/Model/NotFoundInterface.php new file mode 100644 index 0000000..6895484 --- /dev/null +++ b/src/Model/NotFoundInterface.php @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + diff --git a/tests/Application/.env b/tests/Application/.env index 203a87a..5dc3167 100644 --- a/tests/Application/.env +++ b/tests/Application/.env @@ -12,7 +12,7 @@ APP_SECRET=EDITME # Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url # For a sqlite database, use: "sqlite:///%kernel.project_dir%/var/data.db" # Set "serverVersion" to your server version to avoid edge-case exceptions and extra database calls -DATABASE_URL=mysql://root:root@127.0.0.1/setono_sylius_redirect_%kernel.environment%?serverVersion=5.5 +DATABASE_URL=mysql://root@127.0.0.1/setono_sylius_redirect_%kernel.environment%?serverVersion=5.5 ###< doctrine/doctrine-bundle ### ###> symfony/swiftmailer-bundle ### From 0a7c44fca5f9b2b93710d49852ddff32f6164198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joachim=20L=C3=B8vgaard?= Date: Sat, 2 Jan 2021 17:13:56 +0100 Subject: [PATCH 2/5] Check for existing not found entity --- src/DependencyInjection/Configuration.php | 3 ++- src/EventListener/NotFoundSubscriber.php | 20 +++++++++++++----- src/Repository/NotFoundRepository.php | 21 +++++++++++++++++++ .../NotFoundRepositoryInterface.php | 13 ++++++++++++ 4 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 src/Repository/NotFoundRepository.php create mode 100644 src/Repository/NotFoundRepositoryInterface.php diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index dba3d44..19c6e9f 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -7,6 +7,7 @@ use Setono\SyliusRedirectPlugin\Form\Type\RedirectType; use Setono\SyliusRedirectPlugin\Model\NotFound; use Setono\SyliusRedirectPlugin\Model\Redirect; +use Setono\SyliusRedirectPlugin\Repository\NotFoundRepository; use Setono\SyliusRedirectPlugin\Repository\RedirectRepository; use Sylius\Bundle\ResourceBundle\Controller\ResourceController; use Sylius\Bundle\ResourceBundle\Form\Type\DefaultResourceType; @@ -74,7 +75,7 @@ private function addResourcesSection(ArrayNodeDefinition $node): void ->children() ->scalarNode('model')->defaultValue(NotFound::class)->cannotBeEmpty()->end() ->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end() - ->scalarNode('repository')->cannotBeEmpty()->end() + ->scalarNode('repository')->defaultValue(NotFoundRepository::class)->cannotBeEmpty()->end() ->scalarNode('factory')->defaultValue(Factory::class)->end() ->scalarNode('form')->defaultValue(DefaultResourceType::class)->cannotBeEmpty()->end() ->end() diff --git a/src/EventListener/NotFoundSubscriber.php b/src/EventListener/NotFoundSubscriber.php index 5c728b9..66055c6 100644 --- a/src/EventListener/NotFoundSubscriber.php +++ b/src/EventListener/NotFoundSubscriber.php @@ -7,6 +7,7 @@ use Doctrine\Common\Persistence\ObjectManager; use Setono\SyliusRedirectPlugin\Model\NotFoundInterface; use Setono\SyliusRedirectPlugin\Model\RedirectionPath; +use Setono\SyliusRedirectPlugin\Repository\NotFoundRepositoryInterface; use Setono\SyliusRedirectPlugin\Resolver\RedirectionPathResolverInterface; use Sylius\Component\Channel\Context\ChannelContextInterface; use Sylius\Component\Resource\Factory\FactoryInterface; @@ -33,16 +34,21 @@ final class NotFoundSubscriber implements EventSubscriberInterface /** @var FactoryInterface */ private $notFoundFactory; + /** @var NotFoundRepositoryInterface */ + private $notFoundRepository; + public function __construct( ObjectManager $objectManager, ChannelContextInterface $channelContext, RedirectionPathResolverInterface $redirectionPathResolver, - FactoryInterface $notFoundFactory + FactoryInterface $notFoundFactory, + NotFoundRepositoryInterface $notFoundRepository ) { $this->objectManager = $objectManager; $this->channelContext = $channelContext; $this->redirectionPathResolver = $redirectionPathResolver; $this->notFoundFactory = $notFoundFactory; + $this->notFoundRepository = $notFoundRepository; } public static function getSubscribedEvents(): array @@ -78,11 +84,15 @@ public function onKernelException(ExceptionEvent $event): void private function log(Request $request): void { - /** @var NotFoundInterface $notFound */ - $notFound = $this->notFoundFactory->createNew(); - $notFound->onRequest($request); + $notFound = $this->notFoundRepository->findOneByUrl($request->getUri()); + if (null === $notFound) { + /** @var NotFoundInterface $notFound */ + $notFound = $this->notFoundFactory->createNew(); - $this->objectManager->persist($notFound); + $this->objectManager->persist($notFound); + } + + $notFound->onRequest($request); } private function redirect(ExceptionEvent $event, RedirectionPath $redirectionPath): void diff --git a/src/Repository/NotFoundRepository.php b/src/Repository/NotFoundRepository.php new file mode 100644 index 0000000..f1184a8 --- /dev/null +++ b/src/Repository/NotFoundRepository.php @@ -0,0 +1,21 @@ +createQueryBuilder('o') + ->andWhere('o.url = :url') + ->setParameter('url', $url) + ->getQuery() + ->getOneOrNullResult() + ; + } +} diff --git a/src/Repository/NotFoundRepositoryInterface.php b/src/Repository/NotFoundRepositoryInterface.php new file mode 100644 index 0000000..bb1161d --- /dev/null +++ b/src/Repository/NotFoundRepositoryInterface.php @@ -0,0 +1,13 @@ + Date: Sat, 2 Jan 2021 17:27:51 +0100 Subject: [PATCH 3/5] Add ignored property --- src/EventListener/NotFoundSubscriber.php | 5 +++++ src/Model/NotFound.php | 13 +++++++++++++ .../config/doctrine/model/NotFound.orm.xml | 1 + 3 files changed, 19 insertions(+) diff --git a/src/EventListener/NotFoundSubscriber.php b/src/EventListener/NotFoundSubscriber.php index 66055c6..ac86597 100644 --- a/src/EventListener/NotFoundSubscriber.php +++ b/src/EventListener/NotFoundSubscriber.php @@ -20,6 +20,11 @@ use Symfony\Component\HttpKernel\KernelEvents; use Webmozart\Assert\Assert; +/** + * This subscriber listens to 404s in the application. It has two outcomes, either it: + * - redirects the user to a specified in the redirect table, or + * - logs the 404, so the user can act upon it + */ final class NotFoundSubscriber implements EventSubscriberInterface { /** @var ObjectManager */ diff --git a/src/Model/NotFound.php b/src/Model/NotFound.php index 884d705..f4c7516 100644 --- a/src/Model/NotFound.php +++ b/src/Model/NotFound.php @@ -22,6 +22,9 @@ class NotFound implements NotFoundInterface /** @var int */ protected $count = 0; + /** @var bool */ + protected $ignored = false; + /** @var DateTimeInterface */ protected $lastRequestAt; @@ -50,6 +53,16 @@ public function setCount(int $count): void $this->count = $count; } + public function isIgnored(): bool + { + return $this->ignored; + } + + public function setIgnored(bool $ignored): void + { + $this->ignored = $ignored; + } + public function getLastRequestAt(): ?DateTimeInterface { return $this->lastRequestAt; diff --git a/src/Resources/config/doctrine/model/NotFound.orm.xml b/src/Resources/config/doctrine/model/NotFound.orm.xml index b732bd7..bc110cc 100644 --- a/src/Resources/config/doctrine/model/NotFound.orm.xml +++ b/src/Resources/config/doctrine/model/NotFound.orm.xml @@ -12,6 +12,7 @@ + From b85b9293cfef57fc40217e29e5d6366274bf7581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joachim=20L=C3=B8vgaard?= Date: Sat, 2 Jan 2021 17:30:45 +0100 Subject: [PATCH 4/5] Fixed service definition --- src/Resources/config/services/event_listener.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Resources/config/services/event_listener.xml b/src/Resources/config/services/event_listener.xml index b1b69f4..e7c83c9 100644 --- a/src/Resources/config/services/event_listener.xml +++ b/src/Resources/config/services/event_listener.xml @@ -15,13 +15,15 @@ - - + + + + From 312d042d05c55571aa0e394a6129e89d846a86eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joachim=20L=C3=B8vgaard?= Date: Sat, 2 Jan 2021 21:52:40 +0100 Subject: [PATCH 5/5] Typo --- src/EventListener/NotFoundSubscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EventListener/NotFoundSubscriber.php b/src/EventListener/NotFoundSubscriber.php index ac86597..eba9a04 100644 --- a/src/EventListener/NotFoundSubscriber.php +++ b/src/EventListener/NotFoundSubscriber.php @@ -22,7 +22,7 @@ /** * This subscriber listens to 404s in the application. It has two outcomes, either it: - * - redirects the user to a specified in the redirect table, or + * - redirects the user to a destination specified in the redirect table, or * - logs the 404, so the user can act upon it */ final class NotFoundSubscriber implements EventSubscriberInterface