1)静态配置layout
ZendFramework2项目在启动过程中,会读取配置文件中的layout配置。
layout配置默认放在Application模块的config\module.config.php配置文件中的‘view_manager’项中,如下:
- 'view_manager' => array(
- 'layout' => 'layout/layout',
- ),
- class ViewManager extends AbstractListenerAggregate
- {
- public function getLayoutTemplate()
- {
- $layout = 'layout/layout';
- if (isset($this->config['layout'])) {
- $layout = $this->config['layout'];
- }
- return $layout;
- }
- }
如果Application模块和其他模块中都配置了layout项的话,根据各个模块的加载顺序,后加载的模块的layout配置会覆盖掉先加载的模块的layout配置,即zf2会采用最后一个加载的模块的layout配置。如下:
- class ConfigListener extends AbstractListener implements
- ConfigMergerInterface,
- ListenerAggregateInterface
- {
- public function onMergeConfig(ModuleEvent $e)
- {
- foreach ($this->configs as $config) {//遍历各个模块的配置
- $this->mergedConfig = ArrayUtils::merge($this->mergedConfig, $config);//合并各个模块的配置
- }
- return $this;
- }
- }
- abstract class ArrayUtils
- {
- public static function merge(array $a, array $b, $preserveNumericKeys = false)
- {
- foreach ($b as $key => $value) {
- if (array_key_exists($key, $a)) {
- if (is_int($key) && !$preserveNumericKeys) {
- $a[] = $value;
- } elseif (is_array($value) && is_array($a[$key])) {
- $a[$key] = static::merge($a[$key], $value, $preserveNumericKeys);
- } else {
- $a[$key] = $value;//相同的配置项,后者的value会覆盖前者的value
- }
- } else {
- $a[$key] = $value;
- }
- }
- return $a;
- }
- }
- 'modules' => array(
- 'Application',
- 'User',
- 'Admin',
- ),
既然zf2对layout配置的获取是根据模块的加载顺序采用最后一个,那么zf2对于layout的.phtml文件的获取是怎么样的?
1)zf2会按照模块的加载顺序,在各个模块的view目录下查找最后采用的layout配置对应的.phtml文件;
2)zf2采用最后一个从模块的view目录下获取的layout对应的.phtml文件。
例如:上面例子中,zf2采用了Admin模块的layout配置“layout/layout”,该layout配置对应的.phtml文件是模块的view目录下的\layout\layout.phtml文件,如果'Application'和'User'这2个模块的view目录下都有\layout\layout.phtml文件,而'Admin'模块的view目录下没有\layout\layout.phtml文件,那么,根据模块的加载顺序'Application'->'User'->'Admin',zf2最后采用的layout文件就是'User'模块的view目录下的\layout\layout.phtml文件。
因此,如果采用静态配置layout,zf2项目就只有一个layout,这样对实际项目开发而言可能不够。例如,一个商城网站可能会有“顾客”、“店铺主”和“管理员”这三种角色,“顾客”可能需要一个能浏览商城的layout和一个能付款收货的个人中心layout,“店铺主”可能需要能管理货物上下架、收款发货的店铺管理layout,“管理员”可能需要能查看商城所有交易信息的商城管理layout。因此,就需要动态设置layout。
2)动态设置layout
ZendFramework2项目的mvc过程中,会有一个MvcEvent对象贯穿始终,通过该对象,可以动态设置layout:
- $viewModel = $e->getViewModel();//($e instanceof MvcEvent == true);
- if ($viewModel)
- $viewModel->setTemplate("layout/layout");
假定这样配置路由:
- 'goods' => array(
- 'type' => 'segment',
- 'options' => array(
- 'route' => '/goods[/][:action][/:id]',
- 'constraints' => array(
- 'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
- 'id' => '[0-9]+',
- ),
- 'defaults' => array(
- 'controller' => 'Goods\Controller\Goods',
- 'action' => 'index',
- ),
- ),
- ),
- 'customer' => array(
- 'type' => 'segment',
- 'options' => array(
- 'route' => '/customer[/][:action][/:id]',
- 'constraints' => array(
- 'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
- 'id' => '[0-9]+',
- ),
- 'defaults' => array(
- 'controller' => 'Customer\Controller\Customer',
- 'action' => 'admin',
- ),
- ),
- ),
- 'shopowner' => array(
- 'type' => 'segment',
- 'options' => array(
- 'route' => '/shopowner[/][:action][/:id]',
- 'constraints' => array(
- 'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
- 'id' => '[0-9]+',
- ),
- 'defaults' => array(
- 'controller' => 'Shopowner\Controller\Shopowner',
- 'action' => 'admin',
- ),
- ),
- ),
- 'admin' => array(
- 'type' => 'segment',
- 'options' => array(
- 'route' => '/admin[/][:action][/:id]',
- 'constraints' => array(
- 'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
- 'id' => '[0-9]+',
- ),
- 'defaults' => array(
- 'controller' => 'Admin\Controller\Admin',
- 'action' => 'admin',
- ),
- ),
- ),
然后,在Application模块的\view\layout\目录下面可以分别放置4个layout文件:goods.phtml、customer.phtml、shopowner.phtml和admin.phtml。
ZendFramework2项目的启动过程,依次会触发loadModules、loadModule、bootstrap、route、dispatch......等事件,其中对layout配置的读取是在loadModule事件中完成的,而对url的路由解析是在route事件中完成的,因此,可以设置一个route事件的listener,在该listener中完成动态设置layout。
Application模块Module.php的onBootstrap是bootstrap事件被触发后最后一个被执行的listener,可以在该函数中attach一个route事件的listener:
- class Module
- {
- public function onBootstrap(MvcEvent $e)
- {
- $eventManager = $e->getApplication()->getEventManager();
- $moduleRouteListener = new ModuleRouteListener();
- $moduleRouteListener->attach($eventManager);
- $eventManager->attach(MvcEvent::EVENT_ROUTE, array($this, 'onRoute'), -9000);//attach一个route事件的listener
- }
- }
- class Module
- {
- public function onRoute(MvcEvent $e)
- {
- $route = $e->getRouteMatch()->getMatchedRouteName();
- if ($route == 'user')//顾客个人中心
- $e->getViewModel()->setTemplate("layout/customer");
- else if ($route == 'shopowner')//店铺主管理后台
- $e->getViewModel()->setTemplate("layout/shopowner");
- else if ($route == 'admin')//管理员后台
- $e->getViewModel()->setTemplate("layout/admin");
- else//浏览商品
- $e->getViewModel()->setTemplate("layout/goods");
- }
- }