Last active
March 18, 2017 18:39
Zend Framework 2 Localized route concept
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @author Kakhramonov Javlonbek <kakjavlon@gmail.com> | |
* @version 1.0.0 | |
*/ | |
namespace Application\Factory; | |
use Zend\ServiceManager\ServiceLocatorInterface; | |
use Zend\Mvc\Service\RouterConfigTrait; | |
use Zend\Mvc\Router\RouteStackInterface; | |
use Zend\ServiceManager\FactoryInterface; | |
class LocalizedTreeRouteFactory implements FactoryInterface | |
{ | |
use RouterConfigTrait; | |
/** | |
* Create and return the HTTP router | |
* | |
* Retrieves the "router" key of the Config service, and uses it | |
* to instantiate the router. Uses the TreeRouteStack implementation by | |
* default. | |
* | |
* @param ServiceLocatorInterface $serviceManager | |
* @param string $name | |
* @param null|array $options | |
* @return RouteStackInterface | |
*/ | |
public function createService(ServiceLocatorInterface $serviceManager) | |
{ | |
$config = $serviceManager->has('config') ? $serviceManager->get('config') : []; | |
$translatorConfig = $config['translator']; | |
// Defaults | |
$class = 'Application\Router\LocalizedTreeRouteStack'; | |
$config = isset($config['router']) ? $config['router'] : []; | |
$localizedRouteParams = [ | |
'locale' => $translatorConfig['locale'], | |
/** | |
* You can change it to fit to your logic | |
* in this case application config has | |
* $config['translator']['locales'] key that has structure: | |
* | |
* [ | |
* 'locale_name' => 'Language name', | |
* 'locale_name2' => 'Language name 2', | |
* ] | |
* | |
* Notice that 'locale_name' key will be used by router to assemble | |
* and match routes e.g route will be like: | |
* locale_name/your/routes or | |
* /your/routes for default locale | |
*/ | |
'locales' => $translatorConfig['locales'], | |
]; | |
if (is_array($config['default_params'])) { | |
$config['default_params'] = array_merge( | |
$localizedRouteParams, | |
$config['default_params'] | |
); | |
} else { | |
$config['default_params'] = $localizedRouteParams; | |
} | |
return $this->createRouter($class, $config, $serviceManager); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @author Kakhramonov Javlonbek <kakjavlon@gmail.com> | |
* @version 1.0.2 | |
*/ | |
namespace Application\Router; | |
use Zend\Mvc\Router\Http\RouteMatch; | |
use Zend\Mvc\Router\Http\TreeRouteStack; | |
use Zend\Stdlib\RequestInterface as Request; | |
use Zend\Uri\Uri; | |
use Zend\Uri\UriFactory; | |
use Application\Exception; | |
class LocalizedTreeRouteStack extends TreeRouteStack | |
{ | |
/** | |
* @var string | |
*/ | |
protected $locale; | |
/** | |
* $options['default_params']['locales'] structure must be like: | |
* [ | |
* 'locale_name' => 'language name or whatever you want', | |
* 'en-us' => 'English', | |
* 'ru_RU' => 'Russian', | |
* 'en_GB' => 'British', | |
* 'de_de' => 'German' | |
* ] | |
* | |
* Notice that 'locale_name' key will be used by router to assemble | |
* and match routes e.g route will be like: | |
* locale_name/your/routes or | |
* /your/routes for default locale that is in $options['default_params']['locale'] | |
* | |
* @inheritDoc | |
*/ | |
public static function factory($options = []) | |
{ | |
if (!isset($options['default_params']['locale']) || !isset($options['default_params']['locales'])) { | |
throw new Exception\RuntimeException( | |
sprintf( | |
"%s::factory expects default 'locales' and 'locale' option", | |
get_class(self) | |
) | |
); | |
} | |
return parent::factory($options); | |
} | |
/** | |
* Simple modification for localization | |
* Adds locale to the uri | |
* | |
* @inheritdoc | |
*/ | |
public function assemble(array $params = [], array $options = []) | |
{ | |
$assembled = parent::assemble($params, $options); | |
$locale = $this->locale; | |
if (isset($options['locale']) && $options['locale']) { | |
$locale = trim($options['locale']); | |
} | |
$isDefaultNeeded = isset($options['force_canonical']) && $options['force_canonical']; | |
$isDefaultNeeded = $isDefaultNeeded | |
|| isset($options['keep_default_locale']) | |
&& $options['keep_default_locale']; | |
// if it is not required remove default locale | |
if (!$isDefaultNeeded && $locale === $this->defaultParams['locale']) { | |
unset($locale); | |
} | |
if (isset($locale) && $locale) { | |
if (!isset($this->defaultParams['locales'][$locale])) { | |
throw new Exception\RuntimeException( | |
sprintf("Invalid locale '%s'", $locale) | |
); | |
} | |
/** | |
* @var Uri $uri | |
*/ | |
$uri = UriFactory::factory($assembled); | |
$uri->setPath(sprintf('/%s%s', $locale, $uri->getPath())); | |
return $uri; | |
} | |
return $assembled; | |
} | |
/** | |
* Checks request for locale | |
* Does little modification of request uri (removes locale from it) | |
* and sets back after match if needed | |
* | |
* @inheritdoc | |
*/ | |
public function match(Request $request, $pathOffset = null, array $options = []) | |
{ | |
$locale = $this->localeFromUri($request->getUri()); | |
if ($locale) { | |
/** | |
* replace uri | |
* | |
* @var Uri $originalUri | |
* @var Uri $modifiedUri | |
*/ | |
$originalUri = $request->getUri(); | |
$this->locale = $locale; | |
$modifiedUri = clone $originalUri; | |
$parts = explode('/', $modifiedUri->getPath()); | |
// temporary unset locale | |
unset($parts[array_search($locale, $parts)]); | |
$modifiedUri->setPath(join('/', $parts)); | |
$request->setUri($modifiedUri); | |
} | |
$routeMatch = parent::match($request, $pathOffset, $options); | |
// if original uri is not null then reset it to request | |
if (isset($originalUri) && $originalUri) { | |
$request->setUri($originalUri); | |
$this->setRequestUri($originalUri); | |
} | |
// add locale param to route match | |
if ($routeMatch instanceof RouteMatch && $this->locale) { | |
$routeMatch->setParam('locale', $this->locale); | |
} | |
return $routeMatch; | |
} | |
/** | |
* Returns matched locale from | |
* | |
* @param Uri $uri | |
* | |
* @return string|null | |
*/ | |
private function localeFromUri(Uri $uri) | |
{ | |
$path = $uri->getPath(); | |
$parts = explode('/', $path); | |
$locales = $this->defaultParams['locales']; | |
if (is_array($locales) && !empty($locales)) { | |
foreach ($parts as $key => $value) { | |
if (isset($locales[$value])) { | |
$match = $key; | |
} | |
} | |
} | |
if (isset($match)) { | |
// first match will be app locale | |
$locale = $parts[$match]; | |
return $locale; | |
} else { | |
return null; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @author Kakhramonov Javlonbek <kakjavlon@gmail.com> | |
* @version 1.0.1 | |
*/ | |
namespace Application\Listener; | |
use Zend\ServiceManager\ServiceManager; | |
use Zend\EventManager\AbstractListenerAggregate; | |
use Zend\EventManager\EventManagerInterface; | |
use Zend\Mvc\MvcEvent; | |
use Zend\Mvc\Router\Http\RouteMatch; | |
/** | |
* Example route listener to prepare locale settings | |
* Change it the way you want don't forget to attach it to your EventManager | |
*/ | |
class RouteListener extends AbstractListenerAggregate | |
{ | |
/** | |
* @param EventManagerInterface $events | |
* @param int $priority | |
*/ | |
public function attach(EventManagerInterface $events, $priority = 1) | |
{ | |
$events->attach(MvcEvent::EVENT_ROUTE, [$this, 'afterRoute'], -1001); | |
} | |
public function afterRoute(MvcEvent $e) | |
{ | |
$routeMatch = $e->getRouteMatch(); | |
if (!$routeMatch instanceof RouteMatch) { | |
return; | |
} | |
$this->setLocale( | |
$routeMatch->getParam('locale'), | |
$e->getApplication()->getServiceManager() | |
); | |
} | |
/** | |
* Sets locale where needed | |
* | |
* @param string $locale | |
* @param ContainerInterface $services | |
* | |
* @return void | |
*/ | |
private function setLocale($locale, ServiceManager $services) | |
{ | |
$translator = $services->get('translator'); | |
$translator->setLocale($locale); | |
// there you can set locale to services you need | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
1 step. Add line below to your service_manager config
2 step. Register listener to your event manager
3 step. Your translator config must have locales key that must be like
Step 3 is optional you can change route factory to use another config
The config above will make you route: