diff --git a/src/ConfigProvider/BundleConfigProvider.php b/src/ConfigProvider/BundleConfigProvider.php
new file mode 100644
index 0000000..6511a2d
--- /dev/null
+++ b/src/ConfigProvider/BundleConfigProvider.php
@@ -0,0 +1,103 @@
+
+ * @license MIT
+ */
+
+namespace Terminal42\UrlRewriteBundle\ConfigProvider;
+
+use Terminal42\UrlRewriteBundle\RewriteConfig;
+use Terminal42\UrlRewriteBundle\RewriteConfigInterface;
+
+class BundleConfigProvider implements ConfigProviderInterface
+{
+ /**
+ * @var array
+ */
+ private $entries = [];
+
+ /**
+ * BundleConfigProvider constructor.
+ *
+ * @param array $entries
+ */
+ public function __construct(array $entries = [])
+ {
+ $this->entries = $entries;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function find(string $id): ?RewriteConfigInterface
+ {
+ if (!array_key_exists($id, $this->entries)) {
+ return null;
+ }
+
+ return $this->createConfig($id, $this->entries[$id]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function findAll(): array
+ {
+ if (count($this->entries) === 0) {
+ return [];
+ }
+
+ $configs = [];
+
+ foreach ($this->entries as $id => $entry) {
+ if (($config = $this->createConfig((string) $id, $entry)) !== null) {
+ $configs[] = $config;
+ }
+ }
+
+ return $configs;
+ }
+
+ /**
+ * Create the config.
+ *
+ * @param string $id
+ * @param array $data
+ *
+ * @return null|RewriteConfig
+ */
+ private function createConfig(string $id, array $data): ?RewriteConfig
+ {
+ if (!isset($data['request']['path'], $data['response']['code'])) {
+ return null;
+ }
+
+ $config = new RewriteConfig($id, $data['request']['path'], (int) $data['response']['code']);
+
+ // Request hosts
+ if (isset($data['request']['hosts'])) {
+ $config->setRequestHosts($data['request']['hosts']);
+ }
+
+ // Request condition
+ if (isset($data['request']['condition'])) {
+ $config->setRequestCondition($data['request']['condition']);
+ }
+
+ // Request requirements
+ if (isset($data['request']['requirements'])) {
+ $config->setRequestRequirements($data['request']['requirements']);
+ }
+
+ // Response URI
+ if (isset($data['response']['uri'])) {
+ $config->setResponseUri($data['response']['uri']);
+ }
+
+ return $config;
+ }
+}
diff --git a/src/ConfigProvider/ChainConfigProvider.php b/src/ConfigProvider/ChainConfigProvider.php
new file mode 100644
index 0000000..b9b479e
--- /dev/null
+++ b/src/ConfigProvider/ChainConfigProvider.php
@@ -0,0 +1,82 @@
+
+ * @license MIT
+ */
+
+namespace Terminal42\UrlRewriteBundle\ConfigProvider;
+
+use Terminal42\UrlRewriteBundle\RewriteConfigInterface;
+
+class ChainConfigProvider implements ConfigProviderInterface
+{
+ /**
+ * @var array
+ */
+ private $providers = [];
+
+ /**
+ * Add the config provider.
+ *
+ * @param ConfigProviderInterface $provider
+ */
+ public function addProvider(ConfigProviderInterface $provider): void
+ {
+ $this->providers[] = $provider;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function find(string $id): ?RewriteConfigInterface
+ {
+ list($class, $id) = explode(':', $id);
+
+ /** @var ConfigProviderInterface $provider */
+ foreach ($this->providers as $provider) {
+ if ($class === $this->getProviderIdentifier($provider) && ($config = $provider->find($id)) !== null) {
+ return $config;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function findAll(): array
+ {
+ $configs = [];
+
+ /** @var ConfigProviderInterface $provider */
+ foreach ($this->providers as $provider) {
+ $providerConfigs = $provider->findAll();
+
+ /** @var RewriteConfigInterface $config */
+ foreach ($providerConfigs as $config) {
+ $config->setIdentifier($this->getProviderIdentifier($provider) . ':' . $config->getIdentifier());
+ }
+
+ $configs = array_merge($configs, $providerConfigs);
+ }
+
+ return $configs;
+ }
+
+ /**
+ * Get the provider identifier
+ *
+ * @param ConfigProviderInterface $provider
+ *
+ * @return string
+ */
+ private function getProviderIdentifier(ConfigProviderInterface $provider): string
+ {
+ return get_class($provider);
+ }
+}
diff --git a/src/ConfigProvider/ConfigProviderInterface.php b/src/ConfigProvider/ConfigProviderInterface.php
new file mode 100644
index 0000000..ec6b93b
--- /dev/null
+++ b/src/ConfigProvider/ConfigProviderInterface.php
@@ -0,0 +1,32 @@
+
+ * @license MIT
+ */
+
+namespace Terminal42\UrlRewriteBundle\ConfigProvider;
+
+use Terminal42\UrlRewriteBundle\RewriteConfigInterface;
+
+interface ConfigProviderInterface
+{
+ /**
+ * Find the config.
+ *
+ * @param string $id
+ *
+ * @return RewriteConfigInterface|null
+ */
+ public function find(string $id): ?RewriteConfigInterface;
+
+ /**
+ * Find all configs.
+ *
+ * @return array
+ */
+ public function findAll(): array;
+}
diff --git a/src/ConfigProvider/DatabaseConfigProvider.php b/src/ConfigProvider/DatabaseConfigProvider.php
new file mode 100644
index 0000000..b1a0bc3
--- /dev/null
+++ b/src/ConfigProvider/DatabaseConfigProvider.php
@@ -0,0 +1,130 @@
+
+ * @license MIT
+ */
+
+namespace Terminal42\UrlRewriteBundle\ConfigProvider;
+
+use Contao\StringUtil;
+use Doctrine\DBAL\Connection;
+use Doctrine\DBAL\Exception\TableNotFoundException;
+use Terminal42\UrlRewriteBundle\RewriteConfig;
+use Terminal42\UrlRewriteBundle\RewriteConfigInterface;
+
+class DatabaseConfigProvider implements ConfigProviderInterface
+{
+ /**
+ * @var Connection
+ */
+ private $connection;
+
+ /**
+ * DatabaseConfigProvider constructor.
+ *
+ * @param Connection $connection
+ */
+ public function __construct(Connection $connection)
+ {
+ $this->connection = $connection;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function find(string $id): ?RewriteConfigInterface
+ {
+ try {
+ $data = $this->connection->fetchAssoc('SELECT * FROM tl_url_rewrite WHERE id=?', [$id]);
+ } catch (\PDOException | TableNotFoundException $e) {
+ return null;
+ }
+
+ if (false === $data) {
+ return null;
+ }
+
+ return $this->createConfig($data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function findAll(): array
+ {
+ try {
+ $records = $this->connection->fetchAll('SELECT * FROM tl_url_rewrite');
+ } catch (\PDOException | TableNotFoundException $e) {
+ return [];
+ }
+
+ if (count($records) === 0) {
+ return [];
+ }
+
+ $configs = [];
+
+ foreach ($records as $record) {
+ if (($config = $this->createConfig($record)) !== null) {
+ $configs[] = $config;
+ }
+ }
+
+ return $configs;
+ }
+
+ /**
+ * Create the config.
+ *
+ * @param array $data
+ *
+ * @return null|RewriteConfig
+ */
+ private function createConfig(array $data): ?RewriteConfig
+ {
+ if (!isset($data['id'], $data['type'], $data['requestPath'], $data['responseCode'])) {
+ return null;
+ }
+
+ $config = new RewriteConfig((string) $data['id'], $data['requestPath'], (int) $data['responseCode']);
+
+ // Hosts
+ if (isset($data['requestHosts'])) {
+ $config->setRequestHosts(StringUtil::deserialize($data['requestHosts'], true));
+ }
+
+ // Response URI
+ if (isset($data['responseUri'])) {
+ $config->setResponseUri($data['responseUri']);
+ }
+
+ switch ($data['type']) {
+ // Basic type
+ case 'basic':
+ if (isset($data['requestRequirements'])) {
+ $requirements = [];
+
+ foreach (StringUtil::deserialize($data['requestRequirements'], true) as $requirement) {
+ if ($requirement['key'] !== '' && $requirement['value'] !== '') {
+ $requirements[$requirement['key']] = $requirement['value'];
+ }
+ }
+
+ $config->setRequestRequirements($requirements);
+ }
+ break;
+ // Expert type
+ case 'expert':
+ $config->setRequestCondition($data['requestCondition']);
+ break;
+ default:
+ throw new \RuntimeException(sprintf('Unsupported database record config type: %s', $data['type']));
+ }
+
+ return $config;
+ }
+}
diff --git a/src/Controller/RewriteController.php b/src/Controller/RewriteController.php
index 8db21d4..cff8138 100644
--- a/src/Controller/RewriteController.php
+++ b/src/Controller/RewriteController.php
@@ -12,18 +12,19 @@
use Contao\CoreBundle\Framework\ContaoFrameworkInterface;
use Contao\InsertTags;
-use Doctrine\DBAL\Connection;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Terminal42\UrlRewriteBundle\ConfigProvider\ConfigProviderInterface;
+use Terminal42\UrlRewriteBundle\RewriteConfigInterface;
class RewriteController
{
/**
- * @var Connection
+ * @var ConfigProviderInterface
*/
- private $db;
+ private $configProvider;
/**
* @var ContaoFrameworkInterface
@@ -33,12 +34,12 @@ class RewriteController
/**
* RewriteController constructor.
*
- * @param Connection $db
+ * @param ConfigProviderInterface $configProvider
* @param ContaoFrameworkInterface $framework
*/
- public function __construct(Connection $db, ContaoFrameworkInterface $framework)
+ public function __construct(ConfigProviderInterface $configProvider, ContaoFrameworkInterface $framework)
{
- $this->db = $db;
+ $this->configProvider = $configProvider;
$this->framework = $framework;
}
@@ -53,17 +54,18 @@ public function __construct(Connection $db, ContaoFrameworkInterface $framework)
*/
public function indexAction(Request $request): Response
{
- if (!$request->attributes->has('_url_rewrite') || !($rewriteId = $request->attributes->getInt('_url_rewrite'))) {
- throw new RouteNotFoundException('There _url_rewrite attribute is missing');
+ if (!$request->attributes->has('_url_rewrite')) {
+ throw new RouteNotFoundException('The _url_rewrite attribute is missing');
}
- $config = $this->db->fetchAssoc('SELECT * FROM tl_url_rewrite WHERE id=?', [$rewriteId]);
+ $rewriteId = $request->attributes->get('_url_rewrite');
+ $config = $this->configProvider->find($rewriteId);
- if (false === $config || !isset($config['responseCode'])) {
+ if (null === $config) {
throw new RouteNotFoundException(sprintf('URL rewrite config ID %s does not exist', $rewriteId));
}
- $responseCode = (int) $config['responseCode'];
+ $responseCode = $config->getResponseCode();
if (410 === $responseCode) {
return new Response(Response::$statusTexts[$responseCode], $responseCode);
@@ -77,20 +79,17 @@ public function indexAction(Request $request): Response
/**
* Generate the URI.
*
- * @param Request $request
- * @param array $config
+ * @param Request $request
+ * @param RewriteConfigInterface $config
*
* @return string|null
*/
- private function generateUri(Request $request, array $config): ?string
+ private function generateUri(Request $request, RewriteConfigInterface $config): ?string
{
- if (!isset($config['responseUri'])) {
+ if (($uri = $config->getResponseUri()) === null) {
return null;
}
- $uri = $config['responseUri'];
-
- // Parse the URI
$uri = $this->replaceWildcards($request, $uri);
$uri = $this->replaceInsertTags($uri);
diff --git a/src/DependencyInjection/Compiler/ConfigProviderPass.php b/src/DependencyInjection/Compiler/ConfigProviderPass.php
new file mode 100644
index 0000000..dfe90d0
--- /dev/null
+++ b/src/DependencyInjection/Compiler/ConfigProviderPass.php
@@ -0,0 +1,71 @@
+
+ * @license MIT
+ */
+
+namespace Terminal42\UrlRewriteBundle\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+class ConfigProviderPass implements CompilerPassInterface
+{
+ use PriorityTaggedServiceTrait;
+
+ /**
+ * @var string
+ */
+ private $alias;
+
+ /**
+ * @var string
+ */
+ private $chain;
+
+ /**
+ * @var string
+ */
+ private $tag;
+
+ /**
+ * ConfigProviderPass constructor.
+ *
+ * @param string $alias
+ * @param string $chain
+ * @param $tag
+ */
+ public function __construct($alias, $chain, $tag)
+ {
+ $this->alias = $alias;
+ $this->chain = $chain;
+ $this->tag = $tag;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function process(ContainerBuilder $container)
+ {
+ $services = $this->findAndSortTaggedServices($this->tag, $container);
+
+ // If there's only one service or chain service is not present alias the first service
+ if ((count($services) === 1 && count($services[0]) === 1) || !$container->hasDefinition($this->chain)) {
+ $container->setAlias($this->alias, (string) $services[0]);
+
+ return;
+ }
+
+ $definition = $container->findDefinition($this->chain);
+
+ // Add providers to the chain
+ foreach ($services as $service) {
+ $definition->addMethodCall('addProvider', [$service]);
+ }
+ }
+}
diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php
new file mode 100644
index 0000000..eef4136
--- /dev/null
+++ b/src/DependencyInjection/Configuration.php
@@ -0,0 +1,79 @@
+
+ * @license MIT
+ */
+
+namespace Terminal42\UrlRewriteBundle\DependencyInjection;
+
+use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+use Symfony\Component\Config\Definition\ConfigurationInterface;
+use Terminal42\UrlRewriteBundle\RewriteConfigInterface;
+
+class Configuration implements ConfigurationInterface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getConfigTreeBuilder()
+ {
+ $treeBuilder = new TreeBuilder();
+
+ $rootNode = $treeBuilder->root('terminal42_url_rewrite');
+ $rootNode
+ ->children()
+ ->booleanNode('backend_management')
+ ->info('Enable the rewrites management in Contao backend.')
+ ->defaultTrue()
+ ->end()
+ ->arrayNode('entries')
+ ->arrayPrototype()
+ ->children()
+ ->arrayNode('request')
+ ->children()
+ ->scalarNode('path')
+ ->info('The request path to match.')
+ ->isRequired()
+ ->cannotBeEmpty()
+ ->end()
+ ->arrayNode('hosts')
+ ->info('An array of hosts to match.')
+ ->scalarPrototype()->end()
+ ->end()
+ ->arrayNode('requirements')
+ ->info('Additional requirements to match.')
+ ->scalarPrototype()->end()
+ ->end()
+ ->scalarNode('condition')
+ ->info('Request condition in Symfony\'s Expression Language to match.')
+ ->end()
+ ->end()
+ ->end()
+ ->arrayNode('response')
+ ->children()
+ ->integerNode('code')
+ ->info('The response code.')
+ ->defaultValue(301)
+ ->validate()
+ ->ifNotInArray(RewriteConfigInterface::VALID_RESPONSE_CODES)
+ ->thenInvalid('Invalid response code %s.')
+ ->end()
+ ->end()
+ ->scalarNode('uri')
+ ->info('The response redirect URI. Irrelevant if response code is set to 410.')
+ ->end()
+ ->end()
+ ->end()
+ ->end()
+ ->end()
+ ->end()
+ ->end()
+ ;
+
+ return $treeBuilder;
+ }
+}
diff --git a/src/DependencyInjection/Terminal42UrlRewriteExtension.php b/src/DependencyInjection/Terminal42UrlRewriteExtension.php
index 3fa4465..27b952f 100644
--- a/src/DependencyInjection/Terminal42UrlRewriteExtension.php
+++ b/src/DependencyInjection/Terminal42UrlRewriteExtension.php
@@ -14,18 +14,33 @@
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
+use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;
-class Terminal42UrlRewriteExtension extends Extension
+class Terminal42UrlRewriteExtension extends ConfigurableExtension
{
/**
* {@inheritdoc}
*/
- public function load(array $configs, ContainerBuilder $container): void
+ protected function loadInternal(array $mergedConfig, ContainerBuilder $container)
{
$loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('listener.yml');
$loader->load('services.yml');
+
+ $hasBackendManagement = (bool) $mergedConfig['backend_management'];
+
+ // Set the "backend management" parameter
+ $container->setParameter('terminal42_url_rewrite.backend_management', $hasBackendManagement);
+
+ // Remove the database provider if backend management is not available
+ if (!$hasBackendManagement) {
+ $container->removeDefinition('terminal42_url_rewrite.provider.database');
+ }
+
+ // Set the entries as argument for bundle config provider
+ if (isset($mergedConfig['entries']) && $container->hasDefinition('terminal42_url_rewrite.provider.bundle')) {
+ $container->getDefinition('terminal42_url_rewrite.provider.bundle')->setArguments([$mergedConfig['entries']]);
+ }
}
}
diff --git a/src/EventListener/RewriteContainerListener.php b/src/EventListener/RewriteContainerListener.php
index 8c21bac..bf34e5b 100644
--- a/src/EventListener/RewriteContainerListener.php
+++ b/src/EventListener/RewriteContainerListener.php
@@ -13,6 +13,7 @@
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\Response;
+use Terminal42\UrlRewriteBundle\RewriteConfigInterface;
class RewriteContainerListener
{
@@ -91,7 +92,7 @@ public function getResponseCodes(): array
{
$options = [];
- foreach ([301, 302, 303, 307, 410] as $code) {
+ foreach (RewriteConfigInterface::VALID_RESPONSE_CODES as $code) {
$options[$code] = $code.' '.Response::$statusTexts[$code];
}
@@ -99,7 +100,7 @@ public function getResponseCodes(): array
}
/**
- * Generate the examples
+ * Generate the examples.
*
* @return string
*/
diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml
index 4a00bc3..182e3e7 100644
--- a/src/Resources/config/services.yml
+++ b/src/Resources/config/services.yml
@@ -2,12 +2,33 @@ services:
terminal42_url_rewrite.rewrite_controller:
class: Terminal42\UrlRewriteBundle\Controller\RewriteController
arguments:
- - "@database_connection"
+ - "@terminal42_url_rewrite.provider"
- "@contao.framework"
terminal42_url_rewrite.rewrite_loader:
class: Terminal42\UrlRewriteBundle\Routing\UrlRewriteLoader
arguments:
- - "@database_connection"
+ - "@terminal42_url_rewrite.provider"
tags:
- { name: routing.loader }
+
+ terminal42_url_rewrite.provider:
+ alias: terminal42_url_rewrite.provider.chain
+
+ terminal42_url_rewrite.provider.bundle:
+ class: Terminal42\UrlRewriteBundle\ConfigProvider\BundleConfigProvider
+ public: false
+ tags:
+ - { name: terminal42_url_rewrite.provider, priority: 64 }
+
+ terminal42_url_rewrite.provider.chain:
+ class: Terminal42\UrlRewriteBundle\ConfigProvider\ChainConfigProvider
+ public: false
+
+ terminal42_url_rewrite.provider.database:
+ class: Terminal42\UrlRewriteBundle\ConfigProvider\DatabaseConfigProvider
+ public: false
+ arguments:
+ - "@database_connection"
+ tags:
+ - { name: terminal42_url_rewrite.provider, priority: 32 }
diff --git a/src/Resources/contao/config/config.php b/src/Resources/contao/config/config.php
index 65e682e..ceafcca 100644
--- a/src/Resources/contao/config/config.php
+++ b/src/Resources/contao/config/config.php
@@ -8,9 +8,12 @@
* @license MIT
*/
-$GLOBALS['BE_MOD']['system']['url_rewrites'] = [
- 'tables' => ['tl_url_rewrite'],
-];
+/**
+ * Add the backend module if allowed.
+ */
+if (\System::getContainer()->getParameter('terminal42_url_rewrite.backend_management')) {
+ $GLOBALS['BE_MOD']['system']['url_rewrites'] = ['tables' => ['tl_url_rewrite']];
+}
/*
* Hooks
diff --git a/src/Resources/contao/dca/tl_url_rewrite.php b/src/Resources/contao/dca/tl_url_rewrite.php
index 6cd8e2f..788132f 100644
--- a/src/Resources/contao/dca/tl_url_rewrite.php
+++ b/src/Resources/contao/dca/tl_url_rewrite.php
@@ -181,3 +181,10 @@
],
],
];
+
+/*
+ * Remove the DCA if not allowed
+ */
+if (!\System::getContainer()->getParameter('terminal42_url_rewrite.backend_management')) {
+ unset($GLOBALS['TL_DCA']['tl_url_rewrite']);
+}
diff --git a/src/Resources/contao/languages/de/tl_url_rewrite.php b/src/Resources/contao/languages/de/tl_url_rewrite.php
index 73a440b..2c91d34 100644
--- a/src/Resources/contao/languages/de/tl_url_rewrite.php
+++ b/src/Resources/contao/languages/de/tl_url_rewrite.php
@@ -42,7 +42,7 @@
'expert' => ['komplex', 'Erlaubt die Anfrage-Zuordnung auf Basis der Symfony Expression Language. Für weitere Informationen besuchen Sie bitte diese Seite.'],
];
-/**
+/*
* Examples
*/
$GLOBALS['TL_LANG']['tl_url_rewrite']['examples'] = [
diff --git a/src/Resources/contao/languages/en/tl_url_rewrite.php b/src/Resources/contao/languages/en/tl_url_rewrite.php
index ac0c201..aae444c 100644
--- a/src/Resources/contao/languages/en/tl_url_rewrite.php
+++ b/src/Resources/contao/languages/en/tl_url_rewrite.php
@@ -42,7 +42,7 @@
'expert' => ['Expert', 'Allows to define the request condition using the Expression Language. For more information please visit this link.'],
];
-/**
+/*
* Examples
*/
$GLOBALS['TL_LANG']['tl_url_rewrite']['examples'] = [
diff --git a/src/RewriteConfig.php b/src/RewriteConfig.php
new file mode 100644
index 0000000..b03fd3c
--- /dev/null
+++ b/src/RewriteConfig.php
@@ -0,0 +1,181 @@
+
+ * @license MIT
+ */
+
+namespace Terminal42\UrlRewriteBundle;
+
+class RewriteConfig implements RewriteConfigInterface
+{
+ /**
+ * @var string
+ */
+ private $identifier;
+
+ /**
+ * @var string
+ */
+ private $requestPath = '';
+
+ /**
+ * @var array
+ */
+ private $requestHosts = [];
+
+ /**
+ * @var array
+ */
+ private $requestRequirements = [];
+
+ /**
+ * @var string|null
+ */
+ private $requestCondition;
+
+ /**
+ * @var int
+ */
+ private $responseCode = 301;
+
+ /**
+ * @var string|null
+ */
+ private $responseUri;
+
+ /**
+ * RewriteConfig constructor.
+ *
+ * @param string $identifier
+ * @param string $requestPath
+ * @param int $responseCode
+ */
+ public function __construct(string $identifier, string $requestPath, int $responseCode = 301)
+ {
+ $this->identifier = $identifier;
+ $this->setRequestPath($requestPath);
+ $this->setResponseCode($responseCode);
+ }
+
+ /**
+ * @return string
+ */
+ public function getIdentifier(): string
+ {
+ return $this->identifier;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function setIdentifier(string $identifier): void
+ {
+ $this->identifier = $identifier;
+ }
+
+ /**
+ * @return string
+ */
+ public function getRequestPath(): string
+ {
+ return $this->requestPath;
+ }
+
+ /**
+ * @param string $requestPath
+ */
+ public function setRequestPath(string $requestPath): void
+ {
+ $this->requestPath = $requestPath;
+ }
+
+ /**
+ * @return array
+ */
+ public function getRequestHosts(): array
+ {
+ return $this->requestHosts;
+ }
+
+ /**
+ * @param array $requestHosts
+ */
+ public function setRequestHosts(array $requestHosts): void
+ {
+ $this->requestHosts = array_unique(array_filter($requestHosts));
+ }
+
+ /**
+ * @return array
+ */
+ public function getRequestRequirements(): array
+ {
+ return $this->requestRequirements;
+ }
+
+ /**
+ * @param array $requestRequirements
+ */
+ public function setRequestRequirements(array $requestRequirements): void
+ {
+ $this->requestRequirements = $requestRequirements;
+ }
+
+ /**
+ * @return null|string
+ */
+ public function getRequestCondition(): ?string
+ {
+ return $this->requestCondition;
+ }
+
+ /**
+ * @param null|string $requestCondition
+ */
+ public function setRequestCondition(string $requestCondition): void
+ {
+ $this->requestCondition = $requestCondition;
+ }
+
+ /**
+ * @return int
+ */
+ public function getResponseCode(): int
+ {
+ return $this->responseCode;
+ }
+
+ /**
+ * @param int $responseCode
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setResponseCode(int $responseCode): void
+ {
+ if (!\in_array($responseCode, self::VALID_RESPONSE_CODES, true)) {
+ throw new \InvalidArgumentException(sprintf('Invalid response code: %s', $responseCode));
+ }
+
+ $this->responseCode = $responseCode;
+ }
+
+ /**
+ * @return null|string
+ */
+ public function getResponseUri(): ?string
+ {
+ return $this->responseUri;
+ }
+
+ /**
+ * @param null|string $responseUri
+ */
+ public function setResponseUri(string $responseUri): void
+ {
+ $this->responseUri = $responseUri;
+ }
+}
diff --git a/src/RewriteConfigInterface.php b/src/RewriteConfigInterface.php
new file mode 100644
index 0000000..eaed98b
--- /dev/null
+++ b/src/RewriteConfigInterface.php
@@ -0,0 +1,56 @@
+
+ * @license MIT
+ */
+
+namespace Terminal42\UrlRewriteBundle;
+
+interface RewriteConfigInterface
+{
+ const VALID_RESPONSE_CODES = [301, 302, 303, 307, 410];
+
+ /**
+ * @return string
+ */
+ public function getIdentifier(): string;
+
+ /**
+ * @param string $identifier
+ */
+ public function setIdentifier(string $identifier): void;
+
+ /**
+ * @return string
+ */
+ public function getRequestPath(): string;
+
+ /**
+ * @return array
+ */
+ public function getRequestHosts(): array;
+
+ /**
+ * @return array
+ */
+ public function getRequestRequirements(): array;
+
+ /**
+ * @return null|string
+ */
+ public function getRequestCondition(): ?string;
+
+ /**
+ * @return int
+ */
+ public function getResponseCode(): int;
+
+ /**
+ * @return null|string
+ */
+ public function getResponseUri(): ?string;
+}
diff --git a/src/Routing/UrlRewriteLoader.php b/src/Routing/UrlRewriteLoader.php
index 0a2f587..8c83513 100644
--- a/src/Routing/UrlRewriteLoader.php
+++ b/src/Routing/UrlRewriteLoader.php
@@ -12,19 +12,18 @@
namespace Terminal42\UrlRewriteBundle\Routing;
-use Contao\StringUtil;
-use Doctrine\DBAL\Connection;
-use Doctrine\DBAL\Exception\TableNotFoundException;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
+use Terminal42\UrlRewriteBundle\ConfigProvider\ConfigProviderInterface;
+use Terminal42\UrlRewriteBundle\RewriteConfigInterface;
class UrlRewriteLoader extends Loader
{
/**
- * @var Connection
+ * @var ConfigProviderInterface
*/
- private $db;
+ private $configProvider;
/**
* Has been already loaded?
@@ -36,11 +35,11 @@ class UrlRewriteLoader extends Loader
/**
* UrlRewriteLoader constructor.
*
- * @param Connection $db
+ * @param ConfigProviderInterface $configProvider
*/
- public function __construct(Connection $db)
+ public function __construct(ConfigProviderInterface $configProvider)
{
- $this->db = $db;
+ $this->configProvider = $configProvider;
}
/**
@@ -54,22 +53,18 @@ public function load($resource, $type = null): RouteCollection
$this->loaded = true;
$collection = new RouteCollection();
+ $configs = $this->configProvider->findAll();
- try {
- $rewrites = $this->db->fetchAll('SELECT * FROM tl_url_rewrite');
- } catch (\PDOException | TableNotFoundException $e) {
- return $collection;
- }
-
- if (0 === count($rewrites)) {
+ if (0 === count($configs)) {
return $collection;
}
$count = 0;
- foreach ($rewrites as $rewrite) {
+ /** @var RewriteConfigInterface $config */
+ foreach ($configs as $config) {
/** @var Route $route */
- foreach ($this->generateRoutes($rewrite) as $route) {
+ foreach ($this->generateRoutes($config) as $route) {
if ($route !== null) {
$collection->add('url_rewrite_'.$count++, $route);
}
@@ -90,19 +85,13 @@ public function supports($resource, $type = null): bool
/**
* Generate the routes.
*
- * @param array $config
+ * @param RewriteConfigInterface $config
*
* @return \Generator
*/
- private function generateRoutes(array $config): \Generator
+ private function generateRoutes(RewriteConfigInterface $config): \Generator
{
- $hosts = [];
-
- // Parse the hosts from config
- if (isset($config['requestHosts'])) {
- /** @var array $hosts */
- $hosts = array_unique(array_filter(StringUtil::deserialize($config['requestHosts'], true)));
- }
+ $hosts = $config->getRequestHosts();
if (count($hosts) > 0) {
foreach ($hosts as $host) {
@@ -116,31 +105,25 @@ private function generateRoutes(array $config): \Generator
/**
* Create the route object.
*
- * @param array $config
- * @param string|null $host
+ * @param RewriteConfigInterface $config
+ * @param string|null $host
*
* @return Route|null
*/
- private function createRoute(array $config, string $host = null): ?Route
+ private function createRoute(RewriteConfigInterface $config, string $host = null): ?Route
{
- if (!isset($config['id'], $config['type'], $config['requestPath'])) {
- return null;
- }
+ $route = new Route($config->getRequestPath());
+ $route->setDefault('_controller', 'terminal42_url_rewrite.rewrite_controller:indexAction');
+ $route->setDefault('_url_rewrite', $config->getIdentifier());
+ $route->setRequirements($config->getRequestRequirements());
- switch ($config['type']) {
- case 'basic':
- $route = $this->createBasicRoute($config);
- break;
- case 'expert':
- $route = $this->createExpertRoute($config);
- break;
- default:
- throw new \InvalidArgumentException(sprintf('Unsupported database record config type: %s', $config['type']));
+ // Set the condition
+ if (($condition = $config->getRequestCondition()) !== null) {
+ $route->setCondition($condition);
+ } else {
+ $route->setMethods('GET');
}
- $route->setDefault('_controller', 'terminal42_url_rewrite.rewrite_controller:indexAction');
- $route->setDefault('_url_rewrite', $config['id']);
-
// Set the host
if (null !== $host) {
$route->setHost($host);
@@ -148,49 +131,4 @@ private function createRoute(array $config, string $host = null): ?Route
return $route;
}
-
- /**
- * Create the basic route.
- *
- * @param array $config
- *
- * @return Route
- */
- private function createBasicRoute(array $config): Route
- {
- $route = new Route($config['requestPath']);
- $route->setMethods('GET');
-
- // Set the requirements
- if (isset($config['requestRequirements'])) {
- /** @var array $requirements */
- $requirements = StringUtil::deserialize($config['requestRequirements'], true);
- $requirements = array_filter($requirements, function ($item) {
- return $item['key'] !== '' && $item['value'] !== '';
- });
-
- if (count($requirements) > 0) {
- foreach ($requirements as $requirement) {
- $route->setRequirement($requirement['key'], $requirement['value']);
- }
- }
- }
-
- return $route;
- }
-
- /**
- * Create the expert route.
- *
- * @param array $config
- *
- * @return Route
- */
- private function createExpertRoute(array $config): Route
- {
- $route = new Route($config['requestPath']);
- $route->setCondition($config['requestCondition']);
-
- return $route;
- }
}
diff --git a/src/Terminal42UrlRewriteBundle.php b/src/Terminal42UrlRewriteBundle.php
index 139a01b..4d31768 100644
--- a/src/Terminal42UrlRewriteBundle.php
+++ b/src/Terminal42UrlRewriteBundle.php
@@ -14,6 +14,7 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
+use Terminal42\UrlRewriteBundle\DependencyInjection\Compiler\ConfigProviderPass;
class Terminal42UrlRewriteBundle extends Bundle
{
@@ -22,5 +23,10 @@ class Terminal42UrlRewriteBundle extends Bundle
*/
public function build(ContainerBuilder $container): void
{
+ $container->addCompilerPass(new ConfigProviderPass(
+ 'terminal42_url_rewrite.provider',
+ 'terminal42_url_rewrite.provider.chain',
+ 'terminal42_url_rewrite.provider'
+ ));
}
}
diff --git a/tests/DependencyInjection/ConfigurationTest.php b/tests/DependencyInjection/ConfigurationTest.php
new file mode 100644
index 0000000..7e39a7d
--- /dev/null
+++ b/tests/DependencyInjection/ConfigurationTest.php
@@ -0,0 +1,75 @@
+assertInstanceOf(Configuration::class, new Configuration());
+ }
+
+ public function testValidConfig()
+ {
+ $yaml = Yaml::parse(file_get_contents(__DIR__ . '/../Fixtures/config_valid.yml'));
+ $config = (new Processor())->processConfiguration(new Configuration(), $yaml);
+
+ $expected = [
+ 'backend_management' => true,
+ 'entries' => [
+ [
+ 'request' => [
+ 'path' => 'find/{address}',
+ 'hosts' => [],
+ 'requirements' => [],
+ ],
+ 'response' => [
+ 'code' => 303,
+ 'uri' => 'https://www.google.com/maps?q={address}',
+ ],
+ ],
+ [
+ 'request' => [
+ 'path' => 'news/{news}',
+ 'requirements' => ['news' => '\d+'],
+ 'hosts' => [],
+ ],
+ 'response' => [
+ 'code' => 301,
+ 'uri' => '{{news_url::{news}|absolute}}'
+ ],
+ ],
+ [
+ 'request' => [
+ 'path' => 'home.php',
+ 'hosts' => ['localhost'],
+ 'condition' => "context.getMethod() == 'GET' and request.query.has('page')",
+ 'requirements' => [],
+ ],
+ 'response' => [
+ 'uri' => '{{link_url::{page}|absolute}}',
+ 'code' => 301,
+ ],
+ ],
+ ],
+ ];
+
+ static::assertSame($expected, $config);
+ }
+
+ public function testInvalidConfig()
+ {
+ $this->expectException(InvalidConfigurationException::class);
+
+ $yaml = Yaml::parse(file_get_contents(__DIR__ . '/../Fixtures/config_invalid.yml'));
+ (new Processor())->processConfiguration(new Configuration(), $yaml);
+ }
+}
diff --git a/tests/Fixtures/config_invalid.yml b/tests/Fixtures/config_invalid.yml
new file mode 100644
index 0000000..cea45df
--- /dev/null
+++ b/tests/Fixtures/config_invalid.yml
@@ -0,0 +1,5 @@
+terminal42_url_rewrite:
+ entries:
+ -
+ request: { path: 'find/{address}' }
+ response: { code: 404, uri: 'https://www.google.com/maps?q={address}' }
diff --git a/tests/Fixtures/config_valid.yml b/tests/Fixtures/config_valid.yml
new file mode 100644
index 0000000..8f538e8
--- /dev/null
+++ b/tests/Fixtures/config_valid.yml
@@ -0,0 +1,18 @@
+terminal42_url_rewrite:
+ backend_management: true
+ entries:
+ -
+ request: { path: 'find/{address}' }
+ response: { code: 303, uri: 'https://www.google.com/maps?q={address}' }
+
+ -
+ request: { path: 'news/{news}', requirements: {news: '\d+'} }
+ response: { code: 301, uri: '{{news_url::{news}|absolute}}' }
+
+ -
+ request:
+ path: 'home.php'
+ hosts: ['localhost']
+ condition: "context.getMethod() == 'GET' and request.query.has('page')"
+ response:
+ uri: '{{link_url::{page}|absolute}}'