routesOfPlugin = $this->configuration['routes'] ?? []; // Only set the namespace if the plugin+extension keys are given. This allows to also use "namespace" property // instead from the parent constructor. if ( $this->namespace === '' && isset($this->configuration['extension']) && isset($this->configuration['plugin']) ) { $extensionName = $this->configuration['extension']; $pluginName = $this->configuration['plugin']; $extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionName))); $pluginSignature = strtolower($extensionName . '_' . $pluginName); $this->namespace = 'tx_' . $pluginSignature; } return; } /** * {@inheritdoc} */ public function enhanceForMatching(RouteCollection $collection): void { $i = 0; /** @var Route $defaultPageRoute */ $defaultPageRoute = $collection->get('default'); foreach ($this->routesOfPlugin as $configuration) { $route = $this->getVariant($defaultPageRoute, $configuration); $collection->add($this->namespace . '_' . $i++, $route); } } /** * {@inheritdoc} */ protected function getVariant(Route $defaultPageRoute, array $configuration): Route { $arguments = $configuration['_arguments'] ?? []; unset($configuration['_arguments']); $variableProcessor = $this->getVariableProcessor(); $routePath = $this->modifyRoutePath($configuration['routePath']); $routePath = $variableProcessor->deflateRoutePath($routePath, $this->namespace, $arguments); unset($configuration['routePath']); $options = array_merge($defaultPageRoute->getOptions(), ['_enhancer' => $this, 'utf8' => true, '_arguments' => $arguments]); $route = new Route(rtrim($defaultPageRoute->getPath(), '/') . '/' . ltrim($routePath, '/'), [], [], $options); $defaults = array_merge_recursive( $defaultPageRoute->getDefaults(), $variableProcessor->deflateKeys($this->configuration['defaults'] ?? [], $this->namespace, $arguments) ); // only keep `defaults` that are actually used in `routePath` $defaults = $this->filterValuesByPathVariables( $route, $defaults ); // apply '_controller' to route defaults $defaults = array_merge_recursive( $defaults, array_intersect_key($configuration, ['_controller' => true]) ); $route->setDefaults($defaults); $this->applyRouteAspects($route, $this->aspects, $this->namespace); $this->applyRequirements($route, $this->configuration['requirements'] ?? [], $this->namespace); $this->applyStaticVariables($route, $this->configuration['static'] ?? [], $this->namespace); return $route; } /** * {@inheritdoc} */ public function enhanceForGeneration(RouteCollection $collection, array $originalParameters): void { if (!is_array($originalParameters[$this->namespace] ?? null)) { return; } // apply default controller and action names if not set in parameters if (!$this->hasControllerActionValues($originalParameters[$this->namespace]) && !empty($this->configuration['defaultController']) ) { $this->applyControllerActionValues( $this->configuration['defaultController'], $originalParameters[$this->namespace], true ); } $i = 0; /** @var Route $defaultPageRoute */ $defaultPageRoute = $collection->get('default'); foreach ($this->routesOfPlugin as $configuration) { $variant = $this->getVariant($defaultPageRoute, $configuration); // The enhancer tells us: This given route does not match the parameters if (!$this->verifyRequiredParameters($variant, $originalParameters)) { continue; } $parameters = $originalParameters; unset($parameters[$this->namespace]['action']); unset($parameters[$this->namespace]['controller']); $compiledRoute = $variant->compile(); // contains all given parameters, even if not used as variables in route $deflatedParameters = $this->deflateParameters($variant, $parameters); $variables = array_flip($compiledRoute->getPathVariables()); $mergedParams = array_replace($variant->getDefaults(), $deflatedParameters); // all params must be given, otherwise we exclude this variant // (it is allowed that $variables is empty - in this case variables are // "given" implicitly through controller-action pair in `_controller`) if (array_diff_key($variables, $mergedParams)) { continue; } $variant->addOptions(['deflatedParameters' => $deflatedParameters]); $collection->add($this->namespace . '_' . $i++, $variant); } } /** * A route has matched the controller/action combination, so ensure that these properties * are set to tx_blogexample_pi1[controller] and tx_blogexample_pi1[action]. * * @param array $parameters Actual parameter payload to be used * @param array $internals Internal instructions (_route, _controller, ...) */ public function inflateParameters(array $parameters, array $internals = []): array { $parameters = $this->getVariableProcessor() ->inflateNamespaceParameters($parameters, $this->namespace); $parameters[$this->namespace] = $parameters[$this->namespace] ?? []; // Invalid if there is no controller given, so this enhancers does not do anything if (empty($internals['_controller'] ?? null)) { return $parameters; } $this->applyControllerActionValues( $internals['_controller'], $parameters[$this->namespace], false ); return $parameters; } /** * Check if controller+action combination matches */ protected function verifyRequiredParameters(Route $route, array $parameters): bool { if (!is_array($parameters[$this->namespace])) { return false; } if (!$route->hasDefault('_controller')) { return false; } $controller = $route->getDefault('_controller'); [$controllerName, $actionName] = explode('::', $controller); if (!isset($parameters[$this->namespace]['controller']) || $controllerName !== $parameters[$this->namespace]['controller']) { return false; } if (!isset($parameters[$this->namespace]['action']) || $actionName !== $parameters[$this->namespace]['action']) { return false; } return true; } /** * Check if action and controller are not empty. */ protected function hasControllerActionValues(array $target): bool { return !empty($target['controller']) && !empty($target['action']); } /** * Add controller and action parameters so they can be used later-on. * * @param array $target Reference to target array to be modified * @param bool $tryUpdate Try updating action value - but only if controller value matches */ protected function applyControllerActionValues(string $controllerActionValue, array &$target, bool $tryUpdate = false) { if (!str_contains($controllerActionValue, '::')) { return; } [$controllerName, $actionName] = explode('::', $controllerActionValue, 2); // use default action name if controller matches if ($tryUpdate && empty($target['action']) && $controllerName === ($target['controller'] ?? null)) { $target['action'] = $actionName; // use default controller name if action is defined (implies: non-default-controllers must be given) } elseif ($tryUpdate && empty($target['controller']) && !empty($target['action'])) { $target['controller'] = $controllerName; // fallback and override } else { $target['controller'] = $controllerName; $target['action'] = $actionName; } } }