From 6d1c8a167e60202c98aba568822cb3e5a56251eb Mon Sep 17 00:00:00 2001 From: Ingolf Steinhardt Date: Wed, 15 Apr 2026 18:37:13 +0200 Subject: [PATCH 1/3] Fix FilterUrlBuilder if pageModel not null --- src/Filter/FilterUrlBuilder.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Filter/FilterUrlBuilder.php b/src/Filter/FilterUrlBuilder.php index a43a5e189..74f1fdfce 100644 --- a/src/Filter/FilterUrlBuilder.php +++ b/src/Filter/FilterUrlBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2025 The MetaModels team. + * (c) 2012-2026 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author Ingolf Steinhardt * @author Stefan Heimes * @author Andreas Fischer - * @copyright 2012-2025 The MetaModels team. + * @copyright 2012-2026 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -182,13 +182,15 @@ public function addFromCurrentRequest(FilterUrl $filterUrl, array $options = nul $filterUrl->setPageValue('id', \substr($routeName, 8)); $requestUri = \rawurldecode(\substr($request->getPathInfo(), 1)); - if (null === ($route = $request->attributes->get('_route_object'))) { - return; - } - assert($route instanceof Route); + if (null === ($pageModel = $request->attributes->get('pageModel'))) { + if (null === ($route = $request->attributes->get('_route_object'))) { + return; + } + assert($route instanceof Route); - $pageModel = $route->getDefault('pageModel'); - assert($pageModel instanceof PageModel); + $pageModel = $route->getDefault('pageModel'); + assert($pageModel instanceof PageModel); + } $length = $pageModel->urlSuffix ? -\strlen($pageModel->urlSuffix) : null; $start = ($pageModel->urlPrefix ? \strlen($pageModel->urlPrefix . '/') : 0) From 3e0afe8533c526a295034dd98695ee8e0ce3544f Mon Sep 17 00:00:00 2001 From: Ingolf Steinhardt Date: Thu, 30 Apr 2026 18:42:32 +0200 Subject: [PATCH 2/3] Fix FilterUrlBuilder if pageModel not null --- tests/Filter/FilterUrlBuilderTest.php | 57 ++++++++++++++++----------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/tests/Filter/FilterUrlBuilderTest.php b/tests/Filter/FilterUrlBuilderTest.php index 2f760361a..858bd8f2c 100644 --- a/tests/Filter/FilterUrlBuilderTest.php +++ b/tests/Filter/FilterUrlBuilderTest.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2025 The MetaModels team. + * (c) 2012-2026 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,13 +13,15 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2025 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2026 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\Test\Filter; +use Contao\PageModel; use MetaModels\Filter\FilterUrl; use MetaModels\Filter\FilterUrlBuilder; use PHPUnit\Framework\Attributes\CoversClass; @@ -47,10 +49,10 @@ public static function generateProvider(): array 'get2' => 'value', 'parameters' => '/auto/slug/sluggy', ], - 'page' => [ - 'alias' => 'page-alias', + 'page' => fn ($test) => $test->mockPage([ + 'alias' => 'alias', 'id' => 42, - ], + ]), 'get' => [ 'get2' => 'value', ], @@ -70,9 +72,9 @@ public static function generateProvider(): array 'parameters' => '/auto/slug/sluggy', 'get-param' => 'get-value', ], - 'page' => [ - 'id' => 42, - ], + 'page' => fn ($test) => $test->mockPage([ + 'id' => 42, + ]), 'get' => [ 'get2' => 'value', ], @@ -91,28 +93,28 @@ public static function generateProvider(): array /** * Test initialization. * - * @param string $expectedUrl The expected URL. - * @param array $expectedParameters The expected parameters. - * @param array $page The page array. - * @param array $get The GET parameters. - * @param array $slug The slug parameters. - * @param array $requestGet The GET parameters of the current request. - * @param string $requestUrl The current URL. - * - * @return void + * @param string $expectedUrl The expected URL. + * @param array $expectedParameters The expected parameters. + * @param callable(FilterUrlBuilderTest): PageModel $page The page array. + * @param array $get The GET parameters. + * @param array $slug The slug parameters. + * @param array $requestGet The GET parameters of the current request. + * @param string $requestUrl The current URL. */ #[DataProvider('generateProvider')] public function testGenerate( string $expectedUrl, array $expectedParameters, - array $page, + callable $page, array $get, array $slug, array $requestGet, string $requestUrl ): void { + $pageModel = $page($this); + $filterUrl = new FilterUrl( - $page, + $pageModel->row(), $get, $slug ); @@ -127,7 +129,7 @@ public function testGenerate( ->with($expectedUrl, $expectedParameters) ->willReturn('success'); - $requestStack = $this->mockRequestStack($requestGet, $requestUrl, $page['id']); + $requestStack = $this->mockRequestStack($requestGet, $requestUrl, $pageModel); $builder = new FilterUrlBuilder($generator, $requestStack); @@ -158,7 +160,7 @@ public function testGeneratesNonStandardPorts(): void new Request( ['get-param' => 'get-value'], [], - ['pageModel' => 42], + ['pageModel' => $this->mockPage(['alias' => 'folder/page', 'id' => 42])], [], [], [ @@ -179,7 +181,7 @@ public function testGeneratesNonStandardPorts(): void * @param array $requestGet The current GET parameters. * @param string $requestUrl The request URL. */ - private function mockRequestStack(array $requestGet, string $requestUrl, int $pageModel): RequestStack + private function mockRequestStack(array $requestGet, string $requestUrl, PageModel $pageModel): RequestStack { $requestStack = $this->getMockBuilder(RequestStack::class)->getMock(); $requestStack->method('getCurrentRequest')->willReturn( @@ -188,4 +190,15 @@ private function mockRequestStack(array $requestGet, string $requestUrl, int $pa return $requestStack; } + + private function mockPage(array $data): PageModel + { + $mock = $this->getMockBuilder(PageModel::class)->disableOriginalConstructor()->getMock(); + $mock->method('__get')->willReturnCallback( + fn (string $name) => $data[$name] ?? null + ); + $mock->method('row')->willReturn($data); + + return $mock; + } } From 47fb8efbd42ea66826dcb8fecdb0e3f656abd8a5 Mon Sep 17 00:00:00 2001 From: Ingolf Steinhardt Date: Tue, 5 May 2026 14:59:04 +0200 Subject: [PATCH 3/3] Fix unittest to handle page model --- src/Filter/FilterUrlBuilder.php | 5 +++-- tests/Filter/FilterUrlBuilderTest.php | 13 +++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Filter/FilterUrlBuilder.php b/src/Filter/FilterUrlBuilder.php index 74f1fdfce..1ccde73ed 100644 --- a/src/Filter/FilterUrlBuilder.php +++ b/src/Filter/FilterUrlBuilder.php @@ -195,9 +195,10 @@ public function addFromCurrentRequest(FilterUrl $filterUrl, array $options = nul $length = $pageModel->urlSuffix ? -\strlen($pageModel->urlSuffix) : null; $start = ($pageModel->urlPrefix ? \strlen($pageModel->urlPrefix . '/') : 0) + \strlen($pageModel->alias . '/'); - $fragments = \explode('/', \substr($requestUri, $start, $length)); + $slicedUri = \substr($requestUri, $start, $length); + $fragments = '' !== $slicedUri ? \explode('/', $slicedUri) : []; - if (1 === \count($fragments) % 2) { + if (!empty($fragments) && 1 === \count($fragments) % 2) { \array_unshift($fragments, 'auto_item'); } \array_unshift($fragments, $pageModel->alias); diff --git a/tests/Filter/FilterUrlBuilderTest.php b/tests/Filter/FilterUrlBuilderTest.php index 858bd8f2c..c7ab65944 100644 --- a/tests/Filter/FilterUrlBuilderTest.php +++ b/tests/Filter/FilterUrlBuilderTest.php @@ -72,9 +72,10 @@ public static function generateProvider(): array 'parameters' => '/auto/slug/sluggy', 'get-param' => 'get-value', ], - 'page' => fn ($test) => $test->mockPage([ - 'id' => 42, - ]), + 'page' => fn ($test) => $test->mockPage( + ['id' => 42, 'alias' => 'alias', 'urlSuffix' => '.html'], + ['id' => 42] + ), 'get' => [ 'get2' => 'value', ], @@ -160,7 +161,7 @@ public function testGeneratesNonStandardPorts(): void new Request( ['get-param' => 'get-value'], [], - ['pageModel' => $this->mockPage(['alias' => 'folder/page', 'id' => 42])], + ['pageModel' => $this->mockPage(['alias' => 'folder/page', 'id' => 42, 'urlSuffix' => '.html'])], [], [], [ @@ -191,13 +192,13 @@ private function mockRequestStack(array $requestGet, string $requestUrl, PageMod return $requestStack; } - private function mockPage(array $data): PageModel + private function mockPage(array $data, array $rowData = null): PageModel { $mock = $this->getMockBuilder(PageModel::class)->disableOriginalConstructor()->getMock(); $mock->method('__get')->willReturnCallback( fn (string $name) => $data[$name] ?? null ); - $mock->method('row')->willReturn($data); + $mock->method('row')->willReturn($rowData ?? $data); return $mock; }