mxc-commons / mxc-layoutscheme
Zend Framework 2 模块,允许定义布局方案并将自定义布局应用于路由、模块、控制器和动作。
Requires
- php: >=5.3.3
- zendframework/zend-eventmanager: >=2.1
- zendframework/zend-filter: ~2.1
- zendframework/zend-modulemanager: >=2.1
- zendframework/zend-mvc: >=2.1
- zendframework/zend-servicemanager: >=2.1
- zendframework/zend-stdlib: ~2.1
- zendframework/zend-view: ~2.1
README
版本 0.5.0 由 Frank Hein 和 mxc-commons 团队创建。
MxcLayoutScheme 是 maxence 开源倡议的一部分,由德国 maxence business consulting gmbh 支持。
简介
您是否曾经想要根据路由匹配或特定模块、控制器或动作应用不同的布局 phtml?这正是 MxcLayoutScheme 可以为您做到的。
MxcLayoutScheme 允许动态交换渲染器使用的布局模板。您定义布局方案,它是一组选择布局的规则。在每种方案中,您可以为一个特定的路由匹配分配一个独特的布局。此外,您可以为每个模块、控制器和动作定义一个独特的布局。
MxcLayoutScheme 支持配置子 ViewModels 与相关视图模板一起渲染到您定义的捕获中。
此外,MxcLayoutScheme 拦截调度错误。您可以像为路由和控制器做的那样,为特定的错误代码和 HTTP 状态代码应用布局。
MxcLayoutScheme 提供一个事件接口,允许您在引导时选择应用的布局方案。
要求
- Zend Framework 2(最新 master 版本)
特性 / 目标
MxcLayoutScheme 的主要设计目标是尽可能将布局特定设置封装在应用的布局视图模板中。我们希望在控制器动作中尽可能少地处理布局。这样,控制器程序员可以专注于页面 'content'
部分,而不管目标布局(它可能对不同的目标平台(JQuery、JQuery Mobile、Dojo、... 等)非常不同)。
1. 提供动态分配布局的能力
- 基于当前匹配的路由
- 对于每个模块
- 对于每个控制器
- 对于控制器实现的每个动作
2. 提供模块、控制器、动作和路由的层次匹配
- 路由匹配优于动作匹配
- 动作匹配优于控制器匹配
- 控制器匹配优于模块匹配
- 模块匹配优于全局设置
3. 将布局选择规则封装到布局方案中
4. 支持通过配置将子 ViewModels 添加到布局中
5. 允许根据自定义标准选择活动布局方案
- 您可以根据提供的事件选择布局方案选择。
6. 允许为每个布局方案配置全局默认布局
7. 提供预处理和后处理的钩子
8. 提供控制器插件以控制方案选择和布局变量设置
9. 提供对调度错误的支持
10. 提供对内容视图模板的支持
在当前版本中,您可以通过 layoutScheme
控制器插件在控制器动作中分配布局变量。或者,您可以为预处理和后处理提供事件处理器。我们在此提供一个示例。
安装
主要设置
通过克隆项目
- 将此项目克隆到您的
./vendor/
目录。
使用 composer
-
在您的 composer.json 中添加此项目
json "require": { "mxc-commons/mxc-generics": "dev-master, "mxc-commons/mxc-layoutscheme": "dev-master" }
-
现在运行以下命令,让 composer 下载 MxcLayoutScheme:
bash $ php composer.phar update
安装后
-
在您的
application.config.php
文件中启用它。php <?php return array( 'modules' => array( // ... 'MxcGenerics', 'MxcLayoutScheme', ), // ... );
-
将选项文件 'mxclayoutscheme.global.php.dist' 复制到您的配置\autoload 目录。将其重命名为 'mxclayoutscheme.global.php'。
选项
MxcLayoutScheme 模块具有设置和选择布局方案的选项。安装 MxcLayoutScheme 后,将 ./vendor/maxence/MxcLayoutScheme/config/mxclayoutscheme.global.php.dist
复制到 ./config/autoload/mxclayoutscheme.global.php
,并根据需要更改值。
选项分为两个部分
-
defaults - 控制服务操作的设置
-
options - 不同布局方案的设置
'mxclayoutscheme' => array( 'defaults' => array( 'active_scheme' => 'myScheme', //-- name of your layout scheme definition 'enable_mca_layouts' => true, //-- apply layouts based on //-- module, controller, action 'enable_route_layouts' => true //-- apply layouts based on routes` 'enable_error_layouts' => true; //-- apply layouts for dispatch errors 'enable_status_layouts' => true; //-- apply layouts based on response status codes ), 'options' => array( <scheme-definition>, ... ), );
-
active_scheme - 默认激活的方案名称。默认为
zf2-standard
。 -
enable_mca_layouts - 模块、控制器、操作的应用规则用于布局选择。默认:
true
-
enable_route_layouts - 模块、控制器、操作的应用规则用于布局选择。默认:
true
-
enable_status_layouts - 根据状态码的规则在调度错误时应用于布局选择。默认:
true
-
enable_error_layouts - 根据事件错误码的规则在调度错误时应用于布局选择。默认:
true
每个 <scheme-definition>
都分为四个部分
'myScheme' => array(
'mca_layouts' => array(
'options' => array(
<mca-rule-definition>,
...
),
'defaults => array(
<default-settings>
),
),
'route_layouts' => array(
'options' => array(
<route-rule-definition>,
...
),
'defaults => array(
<default-settings>
),
),
'error_layouts' => array(
'options' => array(
<error-rule-definition>,
...
),
'defaults => array(
<default-settings>
),
),
'status_layouts' => array(
'options' => array(
<status-rule-definition>,
...
),
'defaults => array(
<default-settings>
),
),
);
这些部分都是可选的(无论 enable_xxxLayouts
设置如何)。
规则定义键
所有 <xxx-rule-definition>
都具有相同的结构。数组键指定规则,值指定要应用的布局模板。
<mca_layouts>
<mca_layouts>
<mca_layouts>
的键类似于 <moduleName>[\<controllerName>[\<actionName>]]
,其中 <moduleName>
是模块名称,<controllerName>
是注册到 ControllerLoader 的控制器名称,<actionName>
是控制器操作的名称。
Examples
Generic
'MyModule' //--- applies to all controllers in module
'MyModule\MyController' //--- applies to all actions of a controller
'MyModule\MyController\MyAction' //--- applies to a particular controller action
ZfcUser controller (registered as zfcuser)
'ZfcUser' //--- applies to all controllers of ZfcUser (there is only one ;)
'ZfcUser\zfcuser' //--- applies to the controller registered with 'zfcuser'
//--- (ZfcUser\Controller\UserController)
'ZfcUser\zfcuser\login' //--- login action
MxcUserManagement controller (registered as 'MxcUserManagement\Controller\UserManagement')
'MxcUserManagement' //--- applies to all controllers of module MxcUserManagement
'MxcUserManagement\MxcUserManagement\Controller\UserManagement'
//--- applies to controller registered with 'MxcUserManagement\Controller\UserManagement'
//--- *** In this rule the first occurance of'MxcUserManagement' identifies the module.
//--- *** The second occurance is part of the controller registration string.
'MxcUserManagement\MxcUserManagement\Controller\UserManagement\index' //--- index action
操作规则在控制器规则之前评估。控制器规则在模块规则之前评估。因此,如果您为同一模块应用操作规则和模块规则,则模块规则适用于所有控制器和操作,但有一个具有自己规则的特定操作。
<route_layouts>
<route_layouts>
<route_layouts>
的键类似于 <route>
,其中 <route>
是注册的路线。
<status_layouts>
<status_layouts>
<status_layouts>
的键类似于 <status>
,其中 <status>
是状态码(字符串类型)。
Example
'403' //--- applies to status code 403
<error_layouts>
<error_layouts>
<error_layouts>
的键类似于 <error>
,其中 <error>
是由 MvcEvent
返回的错误码(字符串类型)。
规则定义值
每个规则定义都是类似 <capture> => <template>
的值列表。特殊的捕获 'layout'
定义应用于根 ViewModel 的布局。所有其他捕获定义应用于根 ViewModel 的子 ViewModel,使用捕获 <capture>
和模板 <template>
。模板名称必须由 TemplatePathStack 或 TemplateMap 解析器解析。
Example mca rule:
'MyModule\MyController\index' => array(
'layout' => 'layout/layout',
'panelLeft' => 'layout/panel-left',
'header' => 'layout/header',
'footer' => 'layout/footer',
),
规则构建
构建要应用的布局集时,服务使用 'defaults' 部分的值初始化集。然后,如果规则匹配,则与规则关联的特定集覆盖/扩展默认集。
Example
route_layouts => array(
'options' => array(
'home' => array(
'panelLeft' => 'layout/panel-left'
),
),
'defaults' => array(
'layout' => 'layout/layout',
'header' => 'layout/header',
'footer' => 'layout/footer',
),
),
访问路由 home
将匹配相应的规则。应用默认值,然后匹配规则的设置。因此,生成的模板集是
Example result
array(
'panelLeft' => 'layout/panel-left'
'layout' => 'layout/layout',
'header' => 'layout/header',
'footer' => 'layout/footer',
),
MxcLayoutScheme 的工作原理
- 在引导过程中,MxcLayoutScheme 以低优先级(-2000)将钩子连接到应用程序 EventManager 的路由事件。
- 在Bootstrap阶段,MxcLayoutScheme实例化控制器插件
'layoutScheme'
以注入应用程序的ServiceManager实例的引用。 - 在路由阶段,MxcLayoutScheme评估当前匹配的路由、模块名称、控制器名称和操作名称。
- 然后,MxcLayoutScheme触发一个
MxcLayoutSchemeService::HOOK_PRE_SELECT_SCHEME
事件。如果您在Bootstrap中为该事件注册了事件处理器,您可以使用$e->getTarget()->setActiveScheme($schemeName)
设置活动方案,其中$schemeName
是您选择的名称。或者,您可以在控制器操作中使用控制器插件设置活动方案:$this->layoutScheme()->setActiveScheme($schemeName)
。 - 然后,MxcLayoutScheme加载当前活动方案。
- MxcLayoutScheme检查
route_layouts
中与匹配的路由名称匹配的键。如果存在该键,则应用注册到该匹配的布局模板。如果规则定义了子视图模型,则它们将与(可选地)定义的默认子视图模型合并并应用到布局中。如果匹配继续到11。 - 然后,MxcLayoutScheme检查
mca_layouts
中与Module\Conroller\Action匹配的键。如果存在该键,则应用注册到该匹配的布局模板。如果规则定义了子视图模型,则它们将与(可选地)定义的默认子视图模型合并并应用到布局中。如果匹配继续到11。 - 然后,MxcLayoutScheme检查
mca_layouts
中与Module\Conroller匹配的键。如果存在该键,则应用注册到该匹配的布局模板。如果规则定义了子视图模型,则它们将与(可选地)定义的默认子视图模型合并并应用到布局中。如果匹配继续到11。 - 然后,MxcLayoutScheme检查
mca_layouts
中与Module匹配的键。如果存在该键,则应用注册到该匹配的布局模板。如果规则定义了子视图模型,则它们将与(可选地)定义的默认子视图模型合并并应用到布局中。如果匹配继续到11。 - 然后,MxcLayoutScheme检查
default
中的键global
。如果存在该键,则应用注册到该匹配的布局模板。如果规则定义了子视图模型,则它们将与(可选地)定义的默认子视图模型合并并应用到布局中。 - 最后,MxcLayoutScheme触发一个
MxcLayoutSchemeService::HOOK_POST_LAYOUT_SELECT
事件。您可以为该事件注册事件处理器以进行自定义后处理。例如:使用控制器插件将变量分配给所选布局ViewModel及其子ViewModel。下面是一个示例
在应用子布局时,MxcLayoutScheme维护对子ViewModel的引用,供'layoutScheme'
控制器插件使用。控制器插件允许您在控制器操作中应用变量到子视图模型。
MxcLayoutScheme如何处理分发错误
在Bootstrap阶段,MxcLayoutScheme使用优先级-1000挂钩到dispatch.error事件。
当发生分发错误时,MxcLayoutScheme首先检查从$event->getError()
的错误代码适用的规则。如果没有规则适用,MxcLayoutScheme接着检查从$event->getResponse()->getStatusCode()的状态代码适用的规则。
MxcLayoutScheme如何处理内容模板
内容模板可以通过类似于其他模板的规则指定。<capture> => <template>
。
虽然子布局在onRoute时附加到根布局,但内容模板在分发事件上附加。'MxcLayoutScheme'检查注册到content
捕获的子模板。如果找到,它将替换其视图模板。如果没有内容视图模型,MxcLayoutScheme创建一个,并将配置的模板附加到content
捕获。
#####注意:如果您需要在控制器操作中设置内容视图模板,并希望它覆盖给定的布局方案定义,您需要通知布局方案跳过其内容视图模板定义(如果存在)。通过使用控制器插件,您可以通过 $this->layoutScheme()->useControllerContentTemplate()
来完成此操作。
选择活动方案的示例事件处理器
use MxcLayoutScheme\Service\LayoutSchemeService;
...
public function onBootstrap(MvcEvent $e)
{
$app = $e->getApplication();
$em = $app->getEventManager();
$sem = $em->getSharedManager();
$sem->attach('MxcLayoutScheme\Service\LayoutSchemeService',
LayoutSchemeService::HOOK_PRE_SELECT_SCHEME,
function($e) {
switch ($weather) {
case 'sunny':
$schemeName = 'sun';
break;
case 'rainy':
$schemeName = 'umbrella';
break;
default:
$schemeName = 'default';
break;
}
$e->getTarget()->setActiveScheme($schemeName);
}, 100);
}
选择布局后执行的示例后处理事件处理器
use MxcLayoutScheme\Service\LayoutSchemeService;
...
public function onBootstrap(MvcEvent $e)
{
$app = $e->getApplication();
$em = $app->getEventManager();
$sem = $em->getSharedManager();
$sem->attach('MxcLayoutScheme\Service\LayoutSchemeService',
LayoutSchemeService::HOOK_POST_SELECT_LAYOUT,
function($e) {
$variables = array(
'berliner' => 'Ich',
'ein' => 'bin',
'bin' => 'ein',
'ich' => 'Berliner! :)'
);
// apply this set of variables to the layout view model and all registered
// registered child view models
$e->getTarget()->setVariables($variables);
}, 100);
}
特殊的子模板名称
子视图模型由一个数组条目 <capture> => <templateName>
定义。 templateName
是在 ViewManager 的 template_map
中显式注册的模板的名称,或者是一个可以通过 ViewManager 的 template_path_stack
自动解析的模板名称。
此外,您还可以使用两个保留值:'<default>'
和 '<none>'
(包括 < 和 >)。
####templateName '<default>'
如果您将 <default>
指定为 templateName,MxcLayoutScheme 将计算一个由 TemplatePathStack
解析器解析的模板名称。根据实际的 <module>
、<controller>
和 <action>
,将 <capture> => <default>
规则应用于字符串 <module>\<controller>\<action>-<capture>
,并将结果分配给小写的 templateName。
######示例 1
假设模块为 Reporting
,控制器类 WbsController
,动作是 listAction
。假设子 ViewModel 定义为 'header' => '<default>'
。
templateName 计算为 'reporting\wbs\list-header'
。如果您在模块目录的 view\reporting\wbs\
文件夹中提供一个名为 list-header.phtml
的模板,则 TemplatePathStack
解析器在渲染布局的 header
捕获时(通过布局模板中的 <?php echo $this->header ?>
)可以找到它。
######示例 2
假设模块为 Reporting
,控制器类 WbsController
,动作是 prjListAction
。假设子 ViewModel 定义为 'panelLeft' => '<default>'
。
templateName 计算为 'reporting\wbs\prj-list-panel-left'
。
####templateName '<none>'
如果您将 '<none>'
指定为 templateName,则省略特定捕获的计算。在 'defaults'
部分中指定 '<none>'
规则是不必要的。这相当于根本未指定该特定捕获。
'<none>'
规则用于覆盖特定路由规则或 mca 规则的 'defaults'
。
######示例
给定一个名为 'layout\master'
的布局模板,它渲染捕获 panelLeft
和 content
。 panelLeft
提供标准左侧导航。可能看起来像这样
master.phtml:
<html>
<header>
...
</header>
<body>
<?php if ($this->panelLeft) : ?>
<div data-role="panel-left">
<?php echo $this->panelLeft ?>
</div>
<?php endif; ?>
<div data-role="content">
<?php echo $this->content ?>
</div>
</body>
</html>
在某些情况下,您可能不想渲染左侧导航。不允许匿名用户在应用程序中导航的登录页面是一个很好的例子。
如果我们定义了一个布局方案 'master'
,如下所示,默认子 ViewModel 将应用于捕获 'panelLeft'
和模板 'layout\leftNavigation'
。每当 MxcLayoutScheme 分配布局 'layout\master'
时,都会应用此子 ViewModel。mca 规则 'ZfcUser\User\login'
通过定义 'panelLeft' => '<none>'
覆盖默认的 'panelLeft'
设置。结果标记完全不包含 <div data-role="panel-left"> ... </div>
部分。
mxclayout_scheme.global.php:
return array(
'options' => array(
'master' => array(
'mca_layouts' => array(
'options' => array(
'ZfcUser\zfcuser\login' => array(
'layout' => 'layout\master',
'panelLeft' => '<none>',
),
),
'defaults' => array(
'panelLeft' => 'layout\leftNavigation',
),
),
),
),
'defaults':
'active_scheme' => 'master',
'enable_mca_layouts => true,
'enable_route_layouts => true,
'enable_error_layouts => true,
'enable_status_layouts => true,
);
控制器插件
MxcLayoutScheme 注册了一个控制器插件,以便可以访问应用布局的子视图模型。在控制器操作中,您可以通过 $this->layoutScheme
访问控制器插件。
layoutScheme
提供以下接口
getActiveScheme(): 获取当前活动的布局方案。
getChildViewModel($capture): 返回注册给 $capture 捕获的子视图模型。如果没有注册,则返回 null。
getChildViewModels(): 返回子视图模型的数组,如 array ( 'capture' => ViewModel )
setVariables($variables, $override = false): 请参考 ViewModels 的 setVariables
方法以了解参数规范。layoutScheme setVariables
函数将 $variables
提供的相同变量应用到控制器布局和所有注册的子视图模型。
useControllerContentTemplate($flag = true): MxcLayoutScheme 通过其配置支持提供内容模板(capture = 'content'
)。如果您想通过控制器动作设置自己的模板来覆盖 MxcLayoutScheme 的配置,您应该通过调用 useControllerContentTemplate()
告知 MxcLayoutScheme 不要覆盖您的设置。
#####注意
如果您想或需要将不同集合的变量分配给主布局和子布局,可以通过显式访问和赋值来完成。
示例
$this->layout()->setVariables($varMain); // assign variables to the layout's ViewModel
$this->layoutScheme()->getChildViewModel('panelLeft')->setVariables($varPanelLeft); // assign variables to leftPanel child
备注
如果您没有定义方案或活动方案没有注册主布局,则应用 ViewManager 配置。如果您在方案中定义了主布局(捕获 layout
),则它将覆盖 ViewManager 配置。
MxcLayoutScheme 的常见用途包括
- 基于移动检测和不同的移动路由定义,为移动设备应用不同的布局
- 为认证用户和匿名用户或基于用户角色应用不同的布局
- 为功能模块(主题)应用不同的布局
致谢
MxcLayoutScheme 受启发于由 Evan Coury 编写的 EdpModuleLayouts。EdpModuleLayouts 是一个轻量级模块,允许设置特定模块的布局。
许可证
MxcLayoutScheme 在新 BSD 许可证下发布。请参阅 license.txt
。