I want to get a list of cities, where each city name is linked and refers a page for this city:
The links (created in the view script) look like this:
http://project.loc/catalog/Berlin (in the HTML source code url-encoded: Berlin)
http://project.loc/catalog/Erlangen (in the HTML source code url-encoded: Erlangen)
http://project.loc/catalog/Nürnberg (in the HTML source code url-encoded: N%C3%BCrnberg)
"Berlin", "Erlangen" etc. work, but if the city name contains a german special character (ä
, ö
, ü
, Ä
, Ö
, Ü
, or ß
) like "Nürnberg", a 404 error occurs:
A 404 error occurred Page not found. The requested URL could not be matched by routing. No Exception available
Why? And how to get this working?
Thanks in advance!
EDIT:
My router settings:
'router' => array(
'routes' => array(
'catalog' => array(
'type' => 'literal',
'options' => array(
'route' => '/catalog',
'defaults' => array(
'controller' => 'Catalog\Controller\Catalog',
'action' => 'list-cities',
),
),
'may_terminate' => true,
'child_routes' => array(
'city' => array(
'type' => 'segment',
'options' => array(
'route' => '/:city',
'constraints' => array(
'city' => '[a-zA-ZäöüÄÖÜß0-9_-]*',
),
'defaults' => array(
'controller' => 'Catalog\Controller\Catalog',
'action' => 'list-sports',
),
),
'may_terminate' => true,
'child_routes' => array(
// ...
),
),
),
),
),
),
You need to change your constraints, you can use a regular expression which will match UTF8 characters, something like this:
'/[\p{L}]+/u'
Notice the /u modifier (unicode).
EDIT:
The problem is resolved.
Explanation:
The RegEx Route maches the URIs with preg_match(...)
(line 116 or 118 of Zend\Mvc\Router\Http\Regex). In order to mach a string with "special chars" (128+) one must pass the pattern modifier u
to preg_match(...)
. Like this:
$thisRegex = '/catalog/(?<city>[\p{L}]*)';
$regexStr = '(^' . $thisRegex . '$)u'; // <-- here
$path = '/catalog/Nürnberg';
$matches = array();
preg_match($regexStr, $path, $matches);
And since RegEx Route passes a url-enccoded string to preg_match(...)
, it's furthermode needed to decode the string first:
$thisRegex = '/catalog/(?<city>[\p{L}]*)';
$regexStr = '(^' . $thisRegex . '$)u';
$path = rawurldecode('/catalog/N%C3%BCrnberg');
$matches = array();
preg_match($regexStr, $path, $matches);
These two steps are not provided in the RegEx Route, so that preg_match(...)
gets a steing like '/catalog/N%C3%BCrnberg'
and tries to mach it to a regex like '/catalog/(?<city>[\\p{L}]*)/u'
The solution is to use a custom RegEx Route. Here is an example.