Project

General

Profile

Actions

Bug #91530

closed

Empty optional arguments throws InvalidParameterException using the ExtbasePluginEnhancer

Added by Stefan Berger over 4 years ago. Updated about 2 years ago.

Status:
Closed
Priority:
Should have
Assignee:
Category:
Site Handling, Site Sets & Routing
Target version:
-
Start date:
2020-05-29
Due date:
% Done:

0%

Estimated time:
TYPO3 Version:
9
PHP Version:
Tags:
Complexity:
Is Regression:
Sprint Focus:
On Location Sprint

Description

Hi,

we try to migrate our old realurl setup into the new routing configurations.

Now, when we use the ExtbasePluginEnhancer for optional/empty arguments it throws the InvalidParameterException in the Symfony UrlGenerator in not cached situations for links, which will be rendered by this ExtbasePluginEnhancer:

Parameter "tx_example_pi1__category" for route "tx_example_pi1_0" must match "[^/]++" ("" given) to generate a corresponding URL.

We tried different settings like default or requirements but nothing work.

The example url: https://example.com/index.php?id=1&tx_example_pi1[category]=

Our setup is the following:

routeEnhancers:

  Example:
    type: Extbase
    extension: Example
    plugin: Pi1
    limitToPages:
      - 1
    routes:
      - routePath: '/category/{category_slug}'
        _controller: 'Category::list'
        _arguments:
          category_slug: category
    aspects:
      category_slug:
        type: PersistedAliasMapper
        tableName: sys_category
        routeFieldName: slug

It must be said in advance, that the old behavior was using the trailing slash, so we integrate that behavior as a ForceAppendingSlashDecorator like the example here: https://stackoverflow.com/a/53144597

We fixed the issue through and new configuration and an override of the ExtbasePluginEnhancer.

New configuration:

routeEnhancers:

  Example:
...
    optionalArguments:
      - tx_example_pi1__category
...

Override ExtbasePluginEnhancer:

...
use TYPO3\CMS\Core\Routing\Route;
use TYPO3\CMS\Extbase\Routing\ExtbasePluginEnhancer as ExtbasePluginEnhancerCore;

class ExtbasePluginEnhancer extends ExtbasePluginEnhancerCore
{
    protected function verifyRequiredParameters(Route $route, array $parameters): bool
    {
        if (parent::verifyRequiredParameters($route, $parameters)) {
            if (is_array($this->configuration['optionalArguments'])) {

                $compiledRoute = $route->compile();
                $deflatedParameters = $this->deflateParameters($route, $parameters);

                foreach($this->configuration['optionalArguments'] as $optionalArgument) {
                    foreach ($compiledRoute->getPathVariables() as $pathVariable) {
                        if (
                            $pathVariable == $optionalArgument
                            && (
                                !isset($deflatedParameters[$optionalArgument])
                                || $deflatedParameters[$optionalArgument] == ''
                            )
                        ) {
                            return false;
                        }
                    }
                }
            }
        }
        return true;
    }
}

Please investigate this. Thanks.

Actions #1

Updated by Oliver Hader over 4 years ago

  • Status changed from New to Needs Feedback

I did not look into the custom implementation since I guess the scenario could have been solved either with route defauls or a dedicated separate route.

defaults & requirements

Example:
    type: Extbase
    extension: Example
    plugin: Pi1
    ...
    defaults:
        category_slug: ''
    requirements:
        # weakening the `.+` default requirement
        category_slug: '.*'

separare route

Example:
    type: Extbase
    extension: Example
    plugin: Pi1
    routes:
      - routePath: '/category'
        _controller: 'Category::list'
      - routePath: '/category/{category_slug}'
        _controller: 'Category::list'
        _arguments:
          category_slug: category
Actions #2

Updated by Stefan Berger over 4 years ago

Hi Oliver,

thanks for your feedback. So I test both proposals, but unfortunately none of them work.

Maybe the solution could be skipping the routes, which contains empty arguments to prevent the InvalidParameterException? In the example above we use the config to prevent side effects.

Actions #3

Updated by Stefan Berger over 4 years ago

After some testing we found than also other Enhancers are affected. So we removed the config shown above and remove all empty parameters that are passed in the PageRouter::generateUri().

This will now fix the problems concerning the issue for all Enhancers without config.

class PageRouter extends PageRouterCore
{
    public function generateUri($route, array $parameters = [], string $fragment = '', string $type = ''): UriInterface
    {
        $removeEmptyParameter = function ($array) use (&$removeEmptyParameter) {
            $callback = function($value) {
                return !is_null($value) && $value !== '';
            };
            foreach ($array as &$value) {
                if (is_array($value)) {
                    $value = $removeEmptyParameter($value, $callback);
                }
            }
            return array_filter($array, $callback);
        };
        return parent::generateUri($route, $removeEmptyParameter($parameters), $fragment, $type);
    }
}
Actions #4

Updated by Oliver Hader about 2 years ago

  • Assignee set to Oliver Hader
  • Sprint Focus set to On Location Sprint
Actions #5

Updated by Oliver Hader about 2 years ago

I've tested this again with this complete example

routeEnhancers:
  PageTypeSuffix:
    type: PageType
    default: '/'
    index: ''
    map:
      '/': 0
  Example:
    type: Extbase
    extension: Example
    plugin: Pi1
    defaultController: 'Category::list'
    routes:
      - routePath: '/category'
        _controller: 'Category::list'
      - routePath: '/category/{category_slug}'
        _controller: 'Category::list'
        _arguments:
          category_slug: category

   # important, no `defaults` section here for `category_slug`
   # there are two explicits routes now
generateUri('?id=1&tx_example_pi1[category]=1')/page/category/name-of-category-1/
generateUri('?id=1&tx_example_pi1[controller]=Category')/page/category/ (tx_example_pi1[controller] is required to give a hint for that particular enhancer configuration)
  • basically this is the substitute for generateUri('?id=1&tx_example_pi1[category]='), which still fails - and this is expected since there must be a value, e.g. &tx_example_pi1[category]=0 would work)
  • removing all blank ('') parameters per default might be breaking, since some other mapper, enhancer, ... could rely/handler exactly those cases already

Thus, I currently don't see the necessity to remove blank parameters. In case that would be implemented, I should be very specific for particular parameters (not for all).

Actions #6

Updated by Oliver Hader about 2 years ago

  • Status changed from Needs Feedback to Closed
Actions #7

Updated by Oliver Hader about 2 years ago

Forgot to explain, why it fails. The PageType decorator adds a slash / to the end of each route path, which then looks like this:

/category/{tx_example_pi1_0}/

Since there must be a slash at the end, the parameter {tx_example_pi1_0} cannot be optional anymore. The only work-around would be to declare requirements.category_slug: '.*'. As a result, the generated URI would then be /category// (having two slashes at the end).

Actions

Also available in: Atom PDF