diff --git a/CHANGELOG.md b/CHANGELOG.md
index 59507c4..4b0d576 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,18 @@
## [Unreleased]
+### Added
+- \#80 Support for multiple features in route definition @ajgarlag
+
### Changed
- \#82 Restore support for Symfony 4.4 LTS @ajgarlag
+## [4.0.2]
+### Changed
+- Enabled support for twig v3
+
+## [4.0.1]
+### Fix
+- Fix subscriber for invokable controllers @vitsadecky
+
## [4.0.0]
### Changed
- \#67 Add compatibility for Symfony 5 @migo315
@@ -10,6 +21,14 @@
- \#64 Add Travis tests for Symony 5 @migo315
- \#59 Update PHP requirements @migo315
+## [3.6.0]
+### Added
+- Support for multiple features in route definition @ajgarlag
+
+## [3.5.1]
+### Fix
+- Fix subscriber for invokable controllers @vitsadecky
+
## [3.5.0]
### Changed
- \#69 Support for twig 3 @migo315
diff --git a/docs/route.md b/docs/route.md
index 8b74eec..d68465e 100644
--- a/docs/route.md
+++ b/docs/route.md
@@ -85,7 +85,88 @@ or via xml
AppBundle:Blog:list
feature_123
-
+
+
+
+```
+
+To check for multiple features, pass them as an array to the route definition.
+
+```php
+// src/AppBundle/Controller/BlogController.php
+// src/Controller/BlogController.php
+namespace AppBundle\Controller;
+
+use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+
+class BlogController extends Controller
+{
+ /**
+ * @Route("/blog/{page}", defaults={"_feature": {"feature_123", "feature_456"}})
+ */
+ public function listAction($page)
+ {
+ // ...
+ }
+
+ /**
+ * @Route("/blog/{slug}")
+ */
+ public function showAction($slug)
+ {
+ // ...
+ }
+}
+```
+or via yml
+
+```yml
+# app/config/routing.yml
+blog_list:
+ path: /blog/{page}
+ defaults: { _controller: AppBundle:Blog:list, _feature: ['feature_456', 'feature_789'] }
+
+# Symfony 3.4 / 4.0
+blog_list:
+ path: /blog/{page}
+ controller: AppBundle:Blog:list
+ defaults: { _feature: ['feature_456', 'feature_789'] }
+
+blog_show:
+```
+
+or via xml
+
+```xml
+
+
+
+
+
+ AppBundle:Blog:list
+
+
+ feature_123
+ feature_456
+
+
+
+
+
+
+ AppBundle:Blog:list
+
+
+ feature_123
+ feature_456
+
+
+
+
```
diff --git a/src/Listener/RoutingMetadataSubscriber.php b/src/Listener/RoutingMetadataSubscriber.php
index 3ecbac7..abd8a4e 100644
--- a/src/Listener/RoutingMetadataSubscriber.php
+++ b/src/Listener/RoutingMetadataSubscriber.php
@@ -54,9 +54,11 @@ public function onKernelController(ControllerEvent $event)
return;
}
- $featureName = $event->getRequest()->attributes->get(static::FEATURE_KEY);
- if (!$this->manager->isActive($featureName)) {
- throw new NotFoundHttpException('Feature for this class is not active.');
+ $featureNames = (array) $event->getRequest()->attributes->get(static::FEATURE_KEY);
+ foreach ($featureNames as $featureName) {
+ if (!$this->manager->isActive($featureName)) {
+ throw new NotFoundHttpException('Feature for this class is not active.');
+ }
}
}
diff --git a/tests/Listener/RoutingMetadataSubscriberTest.php b/tests/Listener/RoutingMetadataSubscriberTest.php
index e0257c6..f7fb9c1 100644
--- a/tests/Listener/RoutingMetadataSubscriberTest.php
+++ b/tests/Listener/RoutingMetadataSubscriberTest.php
@@ -7,6 +7,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
@@ -123,4 +124,64 @@ private function createControllerEvent($request): ControllerEvent
HttpKernelInterface::MASTER_REQUEST
);
}
+
+ /**
+ * Test features are not active
+ *
+ * @return void
+ */
+ public function testRouteWithMultipleFeaturesIsNotActive()
+ {
+ $this->expectException(NotFoundHttpException::class);
+
+ $request = new Request([], [], ['_feature' => ['feature_abc', 'feature_def']]);
+
+ $event = new ControllerEvent(
+ $this->createMock(HttpKernelInterface::class),
+ function () {
+ return new Response();
+ },
+ $request,
+ HttpKernelInterface::MASTER_REQUEST
+ );
+
+ $manager = $this->createMock(FeatureManagerInterface::class);
+ $manager
+ ->expects(static::exactly(2))
+ ->method('isActive')
+ ->withConsecutive(['feature_abc'], ['feature_def'])
+ ->willReturnOnConsecutiveCalls(true, false);
+
+ $subscriber = new RoutingMetadataSubscriber($manager);
+ $subscriber->onKernelController($event);
+ }
+
+ /**
+ * Test features are active
+ *
+ * @return void
+ */
+ public function testRouteWithMultipleFeaturesIsActive()
+ {
+ $request = new Request([], [], ['_feature' => ['feature_abc', 'feature_def']]);
+
+ $event = new ControllerEvent(
+ $this->createMock(HttpKernelInterface::class),
+ function () {
+ return new Response();
+ },
+ $request,
+ HttpKernelInterface::MASTER_REQUEST
+ );
+
+ $manager = $this->createMock(FeatureManagerInterface::class);
+ $manager
+ ->expects(static::exactly(2))
+ ->method('isActive')
+ ->withConsecutive(['feature_abc'], ['feature_def'])
+ ->willReturnOnConsecutiveCalls(true, true);
+
+ $subscriber = new RoutingMetadataSubscriber($manager);
+ $subscriber->onKernelController($event);
+ }
}