diff --git a/.gitignore b/.gitignore index 5de5676cf..8bcc5d150 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ phpunit.xml composer.lock vendor/ +.idea diff --git a/src/Routing/RouteGenerator/NameInflector/DefaultPostfixInflector.php b/src/Routing/RouteGenerator/NameInflector/DefaultPostfixInflector.php index e81cf48e4..892464a82 100644 --- a/src/Routing/RouteGenerator/NameInflector/DefaultPostfixInflector.php +++ b/src/Routing/RouteGenerator/NameInflector/DefaultPostfixInflector.php @@ -4,7 +4,7 @@ /** * A route name inflector that appends the locale to the routes name except when the locale is the default locale. */ -class DefaultPostfixInflector implements RouteNameInflectorInterface +class DefaultPostfixInflector extends PostfixInflector { /** * @var string @@ -25,6 +25,6 @@ public function inflect($name, $locale) return $name; } - return $name.'.'.$locale; + return parent::inflect($name, $locale); } } diff --git a/src/Routing/RouteGenerator/NameInflector/PostfixInflector.php b/src/Routing/RouteGenerator/NameInflector/PostfixInflector.php index 7a59097e2..4455faa6c 100644 --- a/src/Routing/RouteGenerator/NameInflector/PostfixInflector.php +++ b/src/Routing/RouteGenerator/NameInflector/PostfixInflector.php @@ -1,16 +1,85 @@ unInflect($name, $locale); + + // locale does not match postfixed locale + if ($locale !== ($otherLocale = substr($name, - strlen($locale)))) { + if (is_null($routeCollection)) { + return false; + } + + // check if no another registered route does match, and then throw an exception + $otherRoute = $this->inflect($matchedRoute, $locale); + + $allRoutes = $routeCollection->getIterator(); + + // there is no valid route for the other locale + if ( ! key_exists($otherRoute, $allRoutes)) { + return false; + } + + // there is a valid route for the other locale, but does the pathinfo match? + $originalPathInfo = $allRoutes[$name]->getPath(); + $otherPathInfo = $allRoutes[$otherRoute]->getPath(); + + if ($originalPathInfo !== $otherPathInfo) { + // mismatch + return false; + } + } + + return true; } } diff --git a/src/Routing/RouteGenerator/NameInflector/RouteNameInflectorInterface.php b/src/Routing/RouteGenerator/NameInflector/RouteNameInflectorInterface.php index f20218ef0..45240e594 100644 --- a/src/Routing/RouteGenerator/NameInflector/RouteNameInflectorInterface.php +++ b/src/Routing/RouteGenerator/NameInflector/RouteNameInflectorInterface.php @@ -1,5 +1,6 @@ router->match($pathinfo); + $route = $match['_route']; + $locale = $match['_locale'] ?? ''; - // if a _locale parameter isset remove the .locale suffix that is appended to each route in I18nRoute - if (!empty($match['_locale']) && preg_match('#^(.+)\.'.preg_quote($match['_locale'], '#').'+$#', $match['_route'], $route)) { - $match['_route'] = $route[1]; + if (empty($locale)) { + // no point in trying to match when we have no locale + return $match; + } + + if ($this->routeNameInflector->isInflected($route, $locale)) { + + if ( ! $this->routeNameInflector->isValidMatch($route, $locale, $this->getRouteCollection())) { + throw new RouteNotFoundException('A BeSimple route was matched, but it is not valid.'); + } + + $match['_route'] = $this->routeNameInflector->unInflect($route, $locale); // now also check if we want to translate parameters: if (null !== $this->translator && isset($match['_translate'])) { diff --git a/tests/Routing/Loader/AnnotatedRouteControllerLoaderTest.php b/tests/Routing/Loader/AnnotatedRouteControllerLoaderTest.php index 967da950b..d1cf20c18 100644 --- a/tests/Routing/Loader/AnnotatedRouteControllerLoaderTest.php +++ b/tests/Routing/Loader/AnnotatedRouteControllerLoaderTest.php @@ -43,10 +43,10 @@ public function testRoutesWithLocales() $this->assertContainsOnlyInstancesOf('Symfony\Component\Routing\Route', $rc); $this->assertCount(4, $rc); - $this->assertEquals('/', $rc->get('besimple_i18nrouting_tests_fixtures_noprefix_index.en')->getPath()); - $this->assertEquals('/nl/', $rc->get('besimple_i18nrouting_tests_fixtures_noprefix_index.nl')->getPath()); - $this->assertEquals('/new', $rc->get('new_action.en')->getPath()); - $this->assertEquals('/nieuw', $rc->get('new_action.nl')->getPath()); + $this->assertEquals('/', $rc->get('besimple_i18nrouting_tests_fixtures_noprefix_index.be-simple-i18n.en')->getPath()); + $this->assertEquals('/nl/', $rc->get('besimple_i18nrouting_tests_fixtures_noprefix_index.be-simple-i18n.nl')->getPath()); + $this->assertEquals('/new', $rc->get('new_action.be-simple-i18n.en')->getPath()); + $this->assertEquals('/nieuw', $rc->get('new_action.be-simple-i18n.nl')->getPath()); } public function testRoutesWithPrefixedLocales() @@ -60,15 +60,15 @@ public function testRoutesWithPrefixedLocales() $this->assertContainsOnlyInstancesOf('Symfony\Component\Routing\Route', $rc); $this->assertCount(7, $rc); - $this->assertEquals('/en/', $rc->get('idx.en')->getPath()); - $this->assertEquals('/nl/', $rc->get('idx.nl')->getPath()); - $this->assertEquals('/fr/', $rc->get('idx.fr')->getPath()); + $this->assertEquals('/en/', $rc->get('idx.be-simple-i18n.en')->getPath()); + $this->assertEquals('/nl/', $rc->get('idx.be-simple-i18n.nl')->getPath()); + $this->assertEquals('/fr/', $rc->get('idx.be-simple-i18n.fr')->getPath()); - $this->assertEquals('/en/edit', $rc->get('edit.en')->getPath()); + $this->assertEquals('/en/edit', $rc->get('edit.be-simple-i18n.en')->getPath()); - $this->assertEquals('/en/new', $rc->get('new.en')->getPath()); - $this->assertEquals('/nl/nieuw', $rc->get('new.nl')->getPath()); - $this->assertEquals('/fr/nouveau', $rc->get('new.fr')->getPath()); + $this->assertEquals('/en/new', $rc->get('new.be-simple-i18n.en')->getPath()); + $this->assertEquals('/nl/nieuw', $rc->get('new.be-simple-i18n.nl')->getPath()); + $this->assertEquals('/fr/nouveau', $rc->get('new.be-simple-i18n.fr')->getPath()); } public function testRoutesWithStringPrefix() @@ -82,8 +82,8 @@ public function testRoutesWithStringPrefix() $this->assertContainsOnlyInstancesOf('Symfony\Component\Routing\Route', $rc); $this->assertCount(3, $rc); - $this->assertEquals('/color/', $rc->get('idx.en')->getPath()); - $this->assertEquals('/color/test', $rc->get('idx.test')->getPath()); + $this->assertEquals('/color/', $rc->get('idx.be-simple-i18n.en')->getPath()); + $this->assertEquals('/color/test', $rc->get('idx.be-simple-i18n.test')->getPath()); $this->assertEquals('/color/plain', $rc->get('new')->getPath()); } diff --git a/tests/Routing/Loader/XmlFileLoaderTest.php b/tests/Routing/Loader/XmlFileLoaderTest.php index c3aa6994a..f56871a0c 100644 --- a/tests/Routing/Loader/XmlFileLoaderTest.php +++ b/tests/Routing/Loader/XmlFileLoaderTest.php @@ -19,15 +19,15 @@ public function testBasicI18nRoute() $this->assertEquals( array( - 'homepage_locale.en' => new Route('/en/', array( + 'homepage_locale.be-simple-i18n.en' => new Route('/en/', array( '_locale' => 'en', '_controller' => 'TestBundle:Frontend:homepageLocale' )), - 'homepage_locale.de' => new Route('/de/', array( + 'homepage_locale.be-simple-i18n.de' => new Route('/de/', array( '_locale' => 'de', '_controller' => 'TestBundle:Frontend:homepageLocale' )), - 'homepage_locale.fr' => new Route('/fr/', array( + 'homepage_locale.be-simple-i18n.fr' => new Route('/fr/', array( '_locale' => 'fr', '_controller' => 'TestBundle:Frontend:homepageLocale' )), diff --git a/tests/Routing/Loader/YamlFileLoaderTest.php b/tests/Routing/Loader/YamlFileLoaderTest.php index 93f7e756f..fa9e21cf4 100644 --- a/tests/Routing/Loader/YamlFileLoaderTest.php +++ b/tests/Routing/Loader/YamlFileLoaderTest.php @@ -65,15 +65,15 @@ public function testBasicI18nRoute() $this->assertEquals( array( - 'homepage_locale.en' => new Route('/en/', array( + 'homepage_locale.be-simple-i18n.en' => new Route('/en/', array( '_locale' => 'en', '_controller' => 'TestBundle:Frontend:homepageLocale' )), - 'homepage_locale.de' => new Route('/de/', array( + 'homepage_locale.be-simple-i18n.de' => new Route('/de/', array( '_locale' => 'de', '_controller' => 'TestBundle:Frontend:homepageLocale' )), - 'homepage_locale.fr' => new Route('/fr/', array( + 'homepage_locale.be-simple-i18n.fr' => new Route('/fr/', array( '_locale' => 'fr', '_controller' => 'TestBundle:Frontend:homepageLocale' )), diff --git a/tests/Routing/RouteGenerator/I18nRouteGeneratorTest.php b/tests/Routing/RouteGenerator/I18nRouteGeneratorTest.php index de8605735..94d59d898 100644 --- a/tests/Routing/RouteGenerator/I18nRouteGeneratorTest.php +++ b/tests/Routing/RouteGenerator/I18nRouteGeneratorTest.php @@ -14,8 +14,8 @@ public function testGenerateRoutes() $this->assertEquals( array( - 'test.en' => new Route('/en/', array('_locale' => 'en')), - 'test.nl' => new Route('/nl/', array('_locale' => 'nl')), + 'test.be-simple-i18n.en' => new Route('/en/', array('_locale' => 'en')), + 'test.be-simple-i18n.nl' => new Route('/nl/', array('_locale' => 'nl')), ), $collection->all() ); @@ -79,8 +79,8 @@ public function testGenerateCollectionLocalizeRoutes() $this->assertEquals( array( - 'test.en' => new Route('/en/test', array('_locale' => 'en')), - 'test.nl' => new Route('/nl/test', array('_locale' => 'nl')), + 'test.be-simple-i18n.en' => new Route('/en/test', array('_locale' => 'en')), + 'test.be-simple-i18n.nl' => new Route('/nl/test', array('_locale' => 'nl')), 'test_localized' => new Route('/en/testing', array('_locale' => 'en')), ), $localizedCollection->all() diff --git a/tests/Routing/RouteGenerator/NameInflector/DefaultPostfixInflectorTest.php b/tests/Routing/RouteGenerator/NameInflector/DefaultPostfixInflectorTest.php index f7a9b0412..92f6509b2 100644 --- a/tests/Routing/RouteGenerator/NameInflector/DefaultPostfixInflectorTest.php +++ b/tests/Routing/RouteGenerator/NameInflector/DefaultPostfixInflectorTest.php @@ -2,6 +2,7 @@ namespace BeSimple\I18nRoutingBundle\Tests\Routing\RouteGenerator\NameInflector; use BeSimple\I18nRoutingBundle\Routing\RouteGenerator\NameInflector\DefaultPostfixInflector; +use BeSimple\I18nRoutingBundle\Routing\RouteGenerator\NameInflector\PostfixInflector; class DefaultPostfixInflectorTest extends \PHPUnit_Framework_TestCase { @@ -14,7 +15,7 @@ public function testInflect() $inflector->inflect('route.name', 'en') ); $this->assertSame( - 'route.name.nl', + 'route.name' . PostfixInflector::INFIX .'nl', $inflector->inflect('route.name', 'nl') ); } diff --git a/tests/Routing/RouteGenerator/NameInflector/PostfixInflectorTest.php b/tests/Routing/RouteGenerator/NameInflector/PostfixInflectorTest.php index d0c2f7b42..0b437d312 100644 --- a/tests/Routing/RouteGenerator/NameInflector/PostfixInflectorTest.php +++ b/tests/Routing/RouteGenerator/NameInflector/PostfixInflectorTest.php @@ -11,12 +11,59 @@ public function testInflect() $inflector = new PostfixInflector(); $this->assertSame( - 'route.name.en', + 'route.name.be-simple-i18n.en', $inflector->inflect('route.name', 'en') ); $this->assertSame( - 'route.name.nl', + 'route.name.be-simple-i18n.nl', $inflector->inflect('route.name', 'nl') ); } + + public function testUnInflect() + { + $inflector = new PostfixInflector(); + $this->assertSame('test', $inflector->unInflect('test' . $inflector::INFIX . 'nl', $locale = 'nl')); + } + + public function testisInflected() + { + $inflector = new PostfixInflector(); + $this->assertFalse($inflector->isInflected('test', 'nl'), "'test' is not at BeSimple route"); + $this->assertTrue($inflector->isInflected('test' . PostfixInflector::INFIX . 'nl', 'nl'), 'Should have been a BeSimple route.'); + } + + public function testIsValidMatch() + { + $inflector = new PostfixInflector(); + $this->assertTrue($inflector->isValidMatch('test' . PostfixInflector::INFIX . 'nl', 'nl')); + $this->assertFalse($inflector->isValidMatch('test' . PostfixInflector::INFIX . 'nl', 'en')); + + // see if it works with more than one locale in a collection, all with the same path spec + $routeMock = $this->getMockBuilder('Symfony\Component\Routing\Route') + ->disableOriginalConstructor()->getMock(); + $routeMock->expects($this->any())->method('getPath')->willReturn('/{_locale}/sites'); + + $routes = [ + $inflector->inflect('sites', 'nl') => $routeMock, + $inflector->inflect('sites', 'de') => $routeMock, + $inflector->inflect('sites', 'se') => $routeMock, + $inflector->inflect('sites', 'fr') => $routeMock, + $inflector->inflect('sites', 'es') => $routeMock, + ]; + + $routeCollection = $this->getMock('Symfony\Component\Routing\RouteCollection'); + $routeCollection->expects($this->any())->method('getIterator')->willReturn(new \ArrayIterator($routes)); + + $this->assertTrue($inflector->isValidMatch('sites' . PostfixInflector::INFIX . 'nl', 'nl')); + $this->assertTrue($inflector->isValidMatch('sites' . PostfixInflector::INFIX . 'de', 'de')); + $this->assertTrue($inflector->isValidMatch('sites' . PostfixInflector::INFIX . 'se', 'se')); + $this->assertTrue($inflector->isValidMatch('sites' . PostfixInflector::INFIX . 'fr', 'fr')); + $this->assertTrue($inflector->isValidMatch('sites' . PostfixInflector::INFIX . 'es', 'es')); + + $this->assertFalse($inflector->isValidMatch('sites' . PostfixInflector::INFIX . 'es', 'nl')); + $this->assertFalse($inflector->isValidMatch('sites' . PostfixInflector::INFIX . 'de', 'nl')); + $this->assertFalse($inflector->isValidMatch('sites' . PostfixInflector::INFIX . 'fr', 'nl')); + $this->assertFalse($inflector->isValidMatch('sites' . PostfixInflector::INFIX . 'se', 'nl')); + } } diff --git a/tests/Routing/RouterTest.php b/tests/Routing/RouterTest.php index 06a54c9e1..750f6a033 100644 --- a/tests/Routing/RouterTest.php +++ b/tests/Routing/RouterTest.php @@ -2,9 +2,9 @@ namespace BeSimple\I18nRoutingBundle\Tests\Routing; +use BeSimple\I18nRoutingBundle\Routing\RouteGenerator\NameInflector\PostfixInflector; use BeSimple\I18nRoutingBundle\Routing\Router; use Symfony\Component\Routing\Exception\RouteNotFoundException; -use Symfony\Component\Routing\RequestContext; class RouterTest extends \PHPUnit_Framework_TestCase { @@ -15,13 +15,13 @@ public function testMatchLocaleRoute() ->expects($this->at(0)) ->method('match') ->with($this->equalTo('/foo')) - ->will($this->returnValue(array('_route' => 'test.en', '_locale' => 'en'))) + ->will($this->returnValue(array('_route' => 'test' . PostfixInflector::INFIX . 'en', '_locale' => 'en'))) ; $parentRouter - ->expects($this->at(1)) + ->expects($this->at(2)) ->method('match') ->with($this->equalTo('/bar')) - ->will($this->returnValue(array('_route' => 'test.de', '_locale' => 'de'))) + ->will($this->returnValue(array('_route' => 'test' . PostfixInflector::INFIX . 'de', '_locale' => 'de'))) ; $router = new Router($parentRouter); @@ -41,7 +41,7 @@ public function testMatchTranslateStringField() $parentRouter->expects($this->any()) ->method('match') ->with($this->equalTo('/foo/beberlei')) - ->will($this->returnValue(array('_route' => 'test.en', '_locale' => 'en', '_translate' => 'name', 'name' => 'beberlei'))) + ->will($this->returnValue(array('_route' => 'test' . PostfixInflector::INFIX . 'en', '_locale' => 'en', '_translate' => 'name', 'name' => 'beberlei'))) ; $translator = $this->getMock('BeSimple\I18nRoutingBundle\Routing\Translator\AttributeTranslatorInterface'); $translator @@ -63,7 +63,7 @@ public function testGenerateI18n() $parentRouter = $this->mockParentRouter(); $parentRouter->expects($this->once()) ->method('generate') - ->with($this->equalTo('test_route.en'), $this->equalTo(array('foo' => 'bar')), $this->equalTo(false)) + ->with($this->equalTo('test_route' . PostfixInflector::INFIX . 'en'), $this->equalTo(array('foo' => 'bar')), $this->equalTo(false)) ; $router = new Router($parentRouter); @@ -129,7 +129,7 @@ public function testGenerateI18nTranslated() $parentRouter = $this->mockParentRouter(); $parentRouter->expects($this->once()) ->method('generate') - ->with($this->equalTo('test_route.en'), $this->equalTo(array('foo' => 'baz')), $this->equalTo(false)) + ->with($this->equalTo('test_route' . PostfixInflector::INFIX . 'en'), $this->equalTo(array('foo' => 'baz')), $this->equalTo(false)) ; $translator = $this->getMock('BeSimple\I18nRoutingBundle\Routing\Translator\AttributeTranslatorInterface'); $translator @@ -143,13 +143,32 @@ public function testGenerateI18nTranslated() $router->generate('test_route', array('foo' => 'bar', 'translate' => 'foo', 'locale' => 'en'), false); } + /** + * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException + */ + public function testTheConfiguredLocaleIsTheSameAsThePrefixLocaleWhenMatched() + { + $parentRouter = $this->getMock('Symfony\Component\Routing\RouterInterface'); + + $parentRouter->expects($this->once()) + ->method('match') + ->with($this->equalTo('/en/testen')) + ->will($this->returnValue(array('_route' => 'test' . PostfixInflector::INFIX . 'nl', '_locale' => 'en'))); + + $router = new Router($parentRouter); + + $this->assertInstanceOf(Router::class, $router); + + $match = $router->match('/en/testen'); + } + public function testGenerateI18nTranslatedContextLocale() { $parentRouter = $this->mockParentRouter(); $parentRouter->expects($this->once()) ->method('generate') - ->with($this->equalTo('test_route.fr'), $this->equalTo(array('foo' => 'baz')), $this->equalTo(false)) + ->with($this->equalTo('test_route' . PostfixInflector::INFIX . 'fr'), $this->equalTo(array('foo' => 'baz')), $this->equalTo(false)) ; $translator = $this->getMock('BeSimple\I18nRoutingBundle\Routing\Translator\AttributeTranslatorInterface'); $translator @@ -229,7 +248,7 @@ public function testGenerateWithDefaultLocaleButWithoutRoute() ->method('generate') ->withConsecutive( array('test_route', array('foo' => 'bar'), false), - array('test_route.en', array('foo' => 'bar'), false) + array('test_route' . PostfixInflector::INFIX . 'en', array('foo' => 'bar'), false) ) ->willThrowException(new RouteNotFoundException()); ;