diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 7b123cf..62f28cb 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -266,7 +266,8 @@ getBodyParams getBodyParams - + + $matches $matches $matches $matches diff --git a/src/ContentValidationListener.php b/src/ContentValidationListener.php index 1094f51..8046352 100644 --- a/src/ContentValidationListener.php +++ b/src/ContentValidationListener.php @@ -221,7 +221,10 @@ public function onRoute(MvcEvent $e) $inputFilter = $this->getInputFilter($inputFilterService); - if ($isCollection && ! in_array($method, $this->methodsWithoutBodies)) { + if ( + $isCollection && ! in_array($method, $this->methodsWithoutBodies) + && ! $inputFilter instanceof CollectionInputFilter + ) { $collectionInputFilter = new CollectionInputFilter(); $collectionInputFilter->setInputFilter($inputFilter); $inputFilter = $collectionInputFilter; diff --git a/test/ContentValidationListenerTest.php b/test/ContentValidationListenerTest.php index 2fbe4ba..e489cd7 100644 --- a/test/ContentValidationListenerTest.php +++ b/test/ContentValidationListenerTest.php @@ -2945,4 +2945,53 @@ public function testRemoveEmptyDataIsNotSetSoEmptyDataAreNotRemoved(): void $dataParams->getBodyParams() ); } + + /** + * @dataProvider listMethods + */ + public function testDoNotOverwriteCustomCollectionInputFilter(string $method): void + { + $services = new ServiceManager(); + + $customCollectionInputFilter = new TestAsset\CustomCollectionInputFilter(); + $customCollectionInputFilter->setInputFilter( + [ + 'foo' => [ + 'name' => 'foo', + 'required' => false, + ], + ] + ); + + $services->setService(TestAsset\CustomCollectionInputFilter::class, $customCollectionInputFilter); + $listener = new ContentValidationListener( + [ + 'Foo' => ["{$method}_COLLECTION" => TestAsset\CustomCollectionInputFilter::class], + ], + $services, + [ + 'Foo' => 'foo_id', + ] + ); + + $request = new HttpRequest(); + $request->setMethod($method); + + $matches = $this->createRouteMatch(['controller' => 'Foo']); + + $dataParams = new ParameterDataContainer(); + $dataParams->setBodyParams([['foo' => '']]); + + $event = new MvcEvent(); + $event->setName('route'); + $event->setRequest($request); + $event->setRouteMatch($matches); + $event->setParam('LaminasContentNegotiationParameterData', $dataParams); + // if ContentValidationListener overwrites CustomCollectionInputFilter with instance of CollectionInputFilter + // it sets CustomCollectionInputFilter to CollectionInputFilter::inputFilter property + // as a consequence it throws InvalidArgumentException in CollectionInputFilter::setData() + $this->assertNull($listener->onRoute($event)); + $inputFilter = $event->getParam('Laminas\ApiTools\ContentValidation\InputFilter'); + $this->assertInstanceOf(TestAsset\CustomCollectionInputFilter::class, $inputFilter); + } } diff --git a/test/TestAsset/CustomCollectionInputFilter.php b/test/TestAsset/CustomCollectionInputFilter.php new file mode 100644 index 0000000..ebd7713 --- /dev/null +++ b/test/TestAsset/CustomCollectionInputFilter.php @@ -0,0 +1,18 @@ +