5

我目前正在开发一个多语言网站。对于多语言部分,我使用翻译器/poedit。我将所选语言存储在会话中。它工作正常。

模块.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。

正如我所说,一切正常,除了一件事。

我也在数据库中存储了一些与语言相关的数据,所以在我的模型中我需要知道当前的语言是什么。模型中的其他目的也需要当前语言。

所以我在每个模型的构造函数中包含以下代码:

$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;
            }
        )
    );
}

当然这是行不通的,但是能够做类似这样的事情会很棒,或者更通用的解决方案,没有必要将当前语言的值分配给工厂数组中每个模型的语言变量(例如,模型的通用引导程序,可以在一个地方为所有模型完成此分配)。

我的问题有解决方案吗?

谢谢您的帮助!

4

2 回答 2

4

如果您不想将语言注入到所有模型中,那么我至少可以想到另一种方法。

您可以使用初始化程序。初始化程序基本上允许您对通过服务管理器获取的服务执行初始化任务。因此,要使用这种方法,您必须通过服务管理器获取您的模型(至少是需要该语言的模型)。如果您需要注入任何依赖项,这就像将它们添加为可调用对象或工厂一样简单。关于初始化器的更多信息,你可以阅读我写的关于服务管理器的文章(向下滚动到关于初始化器的部分)。

因此,您可以做的是让您的模型实现一个接口,例如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;
    }
}

如果您愿意,您甚至可以创建一个具有上述代码的抽象基模型类,或者如果您想避免扩展一个类,可以编写一个简单的 trait。然后你的模型每个都可以扩展这个基类,但这取决于你的模型实际需要多少与当前语言一起工作。

现在为初始化程序。在我的文章中,我有一个如何通过方法调用添加它的示例,但您可能希望在配置文件中执行此操作(Application如果您使用 Zend Skeleton 应用程序,可能在模块中)。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);
                }
            },
        ),
    ),
);

上面的初始化程序检查从服务管理器获取的每个对象,看它是否实现了我们在上面创建的接口。如果是这样,则可以注入语言。在我的示例中,我检查它是否在会话中设置,如果没有,则从配置中获取它。您可能需要根据您的确切需求调整逻辑。请注意,初始化程序将在每个从服务管理器获取对象的时间,因此它确实增加了一点开销。然而,这并不重要,因为我们只是在服务未实现接口时进行简单的类型检查。另外,服务默认是共享的,这意味着一个服务只会被实例化一次;随后对同一服务的请求将重用先前实例化的请求。这有助于进一步限制开销。

于 2013-03-31T19:08:40.200 回答
0

我想您可以首先在您想要的任何模块的 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',
                    ),
                ),
            ),
        ),
    ),
);
于 2013-04-05T00:12:58.097 回答