В настоящее время я разрабатываю многоязычный веб-сайт. Для многоязычной части я использую переводчик / poedit. Я храню выбранный язык в сеансе. Он работает нормально.
module.php:
public function onBootstrap(MvcEvent $e) { // ... $session = new Container('base'); if ($session->language !== NULL) { $e->getApplication()->getServiceManager()->get('translator')->setLocale($session->language); } }
Действие для установки языка в контроллере:
public function setLanguageAction() { $language = $this->params()->fromRoute('language'); if (isset($this->languages[$language])) { $session = new Container('base'); if ($language == 'en') { $session->language = NULL; } else { $session->language = $language; } } return $this->redirect()->toRoute('home'); }
В module.config.php для локали по умолчанию установлено значение en.
Как я сказал, все работает нормально, за исключением одного.
Я также храню некоторые данные, зависящие от языка, в DB, поэтому в моих моделях мне нужно знать, что такое текущий язык. Текущий язык необходим и для других целей в моделях.
Поэтому я включаю следующий код в каждую конструкторскую функцию каждой модели:
$session = new Container('base'); if ($session->language !== NULL) { $this->language = $session->language; } else { $this->language = 'default'; }
Я думаю, что это не лучшее решение. У меня слишком много моделей, чтобы всегда включать этот код.
Я хотел бы знать, есть ли решение автоматически передать переменную $ language во все мои модели, например, из функции Module.php / getServiceConfig:
public function getServiceConfig() { $session = new Container('base'); return array( 'factories' => array( 'Application\Model\SomeThing' => function($sm) { $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter'); $c = new SomeThing($dbAdapter); $c->language = $session->language; return $c; } ) ); }
Конечно, это не работает, но было бы здорово сделать что-то подобное или более общее решение, когда нет необходимости присваивать значение текущего языка языковой переменной каждой модели в заводском массиве (например, общий бутстрап для моделей, где это назначение может быть выполнено для всех моделей в одном месте).
Есть ли решение для моей проблемы?
Спасибо за помощь!
M
Если вы не хотите вводить язык во все ваши модели, то я могу думать хотя бы о другом пути с головы.
Вы можете использовать инициализатор. Инициализатор в основном позволяет выполнять задачи инициализации служб, получаемых через диспетчер служб. Таким образом, чтобы использовать этот подход, вы должны получить свои модели (по крайней мере те, которые требуют языка) через менеджера сервиса. Это так же просто, как добавлять их как invokables или, возможно, фабрики, если вам нужно вводить любые зависимости. Для получения дополнительной информации об инициализаторах вы можете прочитать статью, которую я написал о диспетчере сервисов (прокрутите вниз до раздела об инициализаторах).
Итак, вы можете сделать, чтобы ваши модели реализовали интерфейс, например LanguageAwareInterface
.
namespace User\Model; interface LanguageAwareInterface { /** * Gets the language * * @return string */ public function getLanguage(); /** * Sets the language * * @param string $language */ public function setLanguage($language); }
Затем вы можете сделать следующее в своей модели.
namespace User\Model; class MyModel implements \User\Model\LanguageAwareInterface { /** * @var string $language The current language */ protected $language; /** * Gets the language * * @return string */ public function getLanguage() { return $this->language; } /** * Sets the language * * @param string $language */ public function setLanguage($language) { $this->language = $language; } }
Если вы хотите, вы можете даже создать абстрактный базовый класс модели, который имеет вышеуказанный код, или, возможно, написать простую характеристику, если вы хотите избежать расширения класса. Тогда ваши модели могут расширить этот базовый класс, но это зависит от того, сколько из ваших моделей действительно должно работать с текущим языком.
Теперь для инициализатора. В моей статье у меня есть пример того, как добавить его с вызовами методов, но есть вероятность, что вы захотите сделать это в своем файле конфигурации (возможно, в модуле Application
если вы используете приложение скелета Zend). Вы можете либо вернуть массив (или указать на файл конфигурации) из модуля Module
в getServiceConfig()
, либо просто добавить его в YourModule/config/module.config.php
под ключ service_manager
. Официальная документация не дает никаких примеров этого; на самом деле, инициализаторы – единственное, чего не хватает. Однако он должен работать.
return array( /* Other configuration here */ 'service_manager' => array( 'initializers' => array( 'language' => function($service, $sm) { // $service is the service that is being fetched from the service manager // $sm is the service manager instance if ($service instanceof \User\Model\LanguageAwareInterface) { $session = new \Zend\Session\Container('base'); if ($session->language === null) { // Fetch default language from configuration $config = $sm->get('Config'); $session->language = $config['translator']['locale']; } $service->setLanguage($session->language); } }, ), ), );
Вышеупомянутый инициализатор проверяет каждый объект, который извлекается из диспетчера служб, чтобы увидеть, реализует ли он интерфейс, который мы создали выше. Если да, язык может быть введен. В моем примере я проверяю, установлен ли он в сеансе, а если нет, выберите его из конфигурации. Возможно, вам придется настраивать логику в зависимости от ваших конкретных потребностей. Обратите внимание, что инициализатор будет запускаться каждый раз, когда вы извлекаете объект из диспетчера служб, и, таким образом, он добавляет немного накладных расходов. Это, однако, не имеет значения, потому что мы проводим просто проверку типа, когда служба не реализует интерфейс. Кроме того, службы разделяются по умолчанию, что означает, что служба будет создаваться только один раз; последующие запросы для одной и той же службы затем повторно используют ранее созданный экземпляр. Это помогает еще больше ограничить накладные расходы.
Я бы предположил, что сначала вы можете зарегистрировать свою модель сеанса в диспетчере служб в Module.php любого модуля, который вы хотите, чтобы он был доступен по всему миру, и использовать эту службу в любое время, когда вам нужно получить доступ к сеансу.
public function getServiceConfig() { return array( 'factories' => array( 'AppSession' => function($sm) { $session = new \Path\To\Session(); $session->doSomething(); return $session; } ) ); }
Когда вы регистрируете свои различные модели в своем приложении в диспетчере служб, тогда вводите свою сессионную службу.
public function getServiceConfig() { return array( 'factories' => array( 'Application\Model\SomeThing' => function($sm) { $c = new SomeThing(); // You will have to create the set and get functions. $c->setDbAdapterService($sm->get('Zend\Db\Adapter\Adapter')); $c->setSessionService($sm->get('AppSession')); return $c; } ) ); }
И на вашем контроллере также может возникнуть смысл вводить сеанс с самого начала, чтобы сеанс был готов и ожидал вас.
В вашем module.config.php …
return array( 'controllers' => array( 'factories' => array( 'Application\Controller\Something' => function ($sm) { $locator = $sm->getServiceLocator(); $c = new Application\Controller\SomethingController(); $c->setSessionService($locator->get('AppSession')); return $c; }, ), ), 'router' => array( 'routes' => array( 'myroute' => array( 'type' => 'Zend\Mvc\Router\Http\Segment', 'options' => array( 'route' => '[/:action]', 'defaults' => array( 'controller' => 'Application\Controller\Something', 'action' => 'index', ), ), ), ), ), );