jlem / context
基于多个上下文组合配置设置的简单库
Requires
- php: >=5.3.0
- jlem/arrayok: dev-master
This package is not auto-updated.
Last update: 2024-09-24 16:08:26 UTC
README
基于多个上下文组合配置设置的简单库
用例
假设你有一个页面,显示来自不同制造商(包括评论)的各种汽车型号或部件的信息,但是 根据制造商、你所在的国籍和用户组的不同,此页面的格式、布局和信息可能会有所不同
- 福特有一个调校卡车部分,而本田没有
- 但是福特英国不显示这个,因为调校卡车在英国不那么受欢迎
- 英国的所有制造商页面都显示为“日 月 年”格式的日期
- 管理员可以查看所有评论(包括待定/软删除的评论)以及与评论关联的IP地址
- 然而,让我们假设英国有隐私法律禁止向非员工/所有者显示IP地址
- 版主可以查看所有公开评论以及查看/批准待定评论
- 普通用户只能看到公开可见的评论
哇,这么多高度具体的企业规则,你不觉得吗?
为了满足这些企业规则,你可能想做一些类似这样的事情
if ($country === 'uk') { if ($manufacturer === 'ford') { ... } else { ... } } else if (....) { ... }
你明白我的意思。然而,这很快就会变成意大利面条代码,尤其是如果这种逻辑散布在应用程序的不同层次——控制器、模型、视图/模板、javascript。上帝保佑,如果你不得不添加一个新的制造商或更改加拿大本田的展示方式...
也许你可以通过不同的URI来处理一些这些业务规则,这些URI指向不同的控制器,加载不同的视图,但这可能不是最佳选择,可能会导致代码重复。
肯定有更好的方法来处理这些企业规则和上下文特性,对吧?
有!
示例
基本示例
你可能会在引导过程中早期完成所有这些,但你可以在请求周期中的任何时间点这样做,在你需要使用数据之前
1. 配置你的变体,使其与适当的上下文相关。
$config = [ // 'common' is the fallback default value for all variants. // Useful for defining commonalities shared by all contexts 'common' => [ 'show_tuner_truck_module' => true, 'date_format' => 'M j, Y', 'comment_query_criteria' => 'Acme\Comment\Criteria\Member', // Give this to a repository 'show_comment_ip' => false ], // 'defaults' are the default configurations for each specific context value // These override 'common' configs, if present 'defaults' => [ 'UK' => [ 'date_format' => 'j M, Y', 'show_comment_ip' => false ], 'Honda' => [ 'show_tuner_truck_module' => false ], 'Admin' => [ 'comment_query_criteria' => 'Acme\Comment\Criteria\Admin', // Give this to a repository 'show_comment_ip' => true ], 'Moderator' => [ 'comment_query_criteria' => 'Acme\Comment\Criteria\Moderator' // Give this to a repository ] // 'conditions' represent configurations for arbitrary *combinations* of contexts // These override both 'defaults' and 'common', if the context of the request matches ], 'conditions' => [ 'ford_uk' => new Condition(['country' => 'UK', 'manufacturer' => 'Ford'], ['show_tuner_truck_module' => false]) ] ];
2. 然后,定义请求本身的上下文(顺序很重要!)
关于顺序的说明:顺序决定了Context及其过滤器应用array_merge
的顺序。它从第一个到最后一个覆盖,从最后一个到第一个回退。也就是说,在下面的defaults
过滤器中,'Ford'
覆盖了'UK'
,而'UK'
覆盖了'Admin'
。然而,如果在'Admin'
中定义了任何在'UK'
或'Ford'
中未定义的配置键,则它仍然包含在配置数组中,归功于array_merge
$context = [ 'user' => 'Admin', // maybe get this from Session 'country' => 'UK', // maybe from a subdomain or user-agent query as part of the request 'manufacturer' => 'Ford' // maybe from a query param, route slug, or what have you ];
3. 创建上下文并使用所需的过滤器(顺序很重要!)
$Context = new Context($context); $Context->addFilter('common', new CommonFilter($config)); $Context->addFilter('defaults', new DefaultsFilter($config)); $Context->addFilter('conditions', new ConditionsFilter($config));
4. 获取表示请求上下文的配置
$filteredConfig = $Context->get();
基于步骤2中定义的上下文,上面的调用将返回以下数组
[ 'show_tuner_truck_module' => false, // Determined by the 'ford_uk' condition 'date_format' => 'j M, Y' // Determined by the 'UK' default 'comment_query_criteria' => 'Acme\Comment\Criteria\Admin' // Determined by the 'Admin' default 'show_comment_ip' => false // Determined by the 'UK' default ]
但是等等,为什么英国的默认值'show_comment_ip'
覆盖了由'Admin'
默认值兄弟相同的配置设置?因为上下文在步骤#2中定义的顺序。尽管幕后Context使用了'UK'
和'Admin'
的默认值,但由于'UK'
上下文是在'Admin'
上下文之后设置的,所以它具有优先级。我们可以更改此顺序以获得不同的结果,具体取决于我们的需求
更改上下文顺序
如果您愿意,您可以在请求周期中的任何时间改变所有过滤器的上下文顺序。
选项 1:全局改变顺序
$Context->reorderContext('country.user.manufacturer'); $Context->reorderContext(['country', 'user', 'manufacturer']); // optional array syntax $filteredContext = $Context->get();
现在应该返回
[ 'show_tuner_truck_module' => false, // Determined by the 'ford_uk' condition 'date_format' => 'j M, Y' // Determined by the 'UK' default 'comment_query_criteria' => 'Acme\Comment\Criteria\Admin' // Determined by the 'Admin' default 'show_comment_ip' => true // Determined by the 'Admin' default ]
注意,这里您是通过上下文键重新排序,而不是定义一个新的整个上下文数组。这样做的原因是,您可以按底层上下文重新排序,而不必担心这些上下文的值。
选项 2:按过滤器改变顺序
除了为所有过滤器全局改变上下文之外,您还可以为某些过滤器指定某些上下文顺序。这些顺序将始终覆盖任何全局上下文重新排序。
$Context->getFilter('defaults')->reorderContext('manufacturer.user.country'); $Context->reorderFilterContext('defaults', 'manufacturer.user.country'); // Alternative $Context->getFilter('conditions')->reorderContext('country.manufacturer.user'); $Context->reorderFilterContext('conditions', 'country.manufacturer.user'); // Alternative
减少上下文
即使您的初始上下文包含三个方面,您在重新排序时也不一定需要利用所有三个方面。
$Context->reorderContext('country.manufacturer'); // Context has become: [ 'country' => 'UK', 'manufacturer' => 'Ford' ];
通过上述操作,您已有效地将 'user'
从上下文作用域中删除,然后相应地重新排序,这意味着对于此请求将忽略 'Admin'
默认设置。
如果您只想重新排序一个或两个方面,请将 false
作为第二个参数传递以保留所有上下文方面,但将指定的方面放在前面
$Context->reorderContext('country.manufacturer', false); // Context has become: [ 'country' => 'UK', 'manufacturer' => 'Ford', 'user' => 'Admin' // Was not specified, so it stays on the end (array_merge behavior) ];
重置上下文顺序
注意 当提供重新排序时(与使用数据完全重置相反),它们会改变上下文和过滤器对象使用最初给定的上下文数据的方式,但不会改变最初给定的上下文数据本身。这意味着随后的 get()
将继续使用之前给定的上下文顺序,但可以使用以下方式随时重置到原始(或最后提供的)上下文数据:
$Context->resetContextOrder(); // Context is now back to: [ 'user' => 'Admin', 'country' => 'UK', 'manufacturer' => 'Ford' ];
或者按过滤器重置
$Context->getFilter('defaults')->resetContextOrder();
注意,如果已定义全局重新排序,则过滤器仍将继承此设置,除非也重置全局顺序(或为过滤器提供所需的顺序
禁用过滤器
$Context->disableFilter('defaults');
现在只使用 'common'
和 'conditions'
过滤器来处理请求。要重新启用它
$Context->enableFilter('defaults');
禁用上下文
$Context->disableContext();
这实际上只使用 'common'
过滤器,该过滤器根本不使用上下文。换句话说,它等同于禁用除 'common'
过滤器之外的所有过滤器。
要重新启用它
$Context->enableContext();
过滤器文档
过滤器是上下文解析系统的核心。每个过滤器负责解析和优先处理其自己的配置数组部分。默认过滤器处理 'defaults' 配置组,条件过滤器处理 'conditions' 等。每个都根据其自己的方式使用提供的上下文数据和顺序来预先过滤其配置组,然后由主上下文对象组装。这意味着扩展上下文的行为就像注册一个过滤器并为它提供要解析的配置数据一样简单。
条件过滤器
条件过滤器允许您动态修改其条件和条件本身。
添加新的条件
您可以使用 $ConditionFilter->addCondition(string $name, Condition $condition)
动态添加新的条件。
$ConditionFilter->addCondition('us_admin', new Condition(['country' => 'US', 'user' => 'Admin'], ['some' => 'config']));
第一个参数是一个任意的名称,用于标识条件。这个名称除了查找之外不用于其他任何事情。第二个参数是一个 Condition
对象。
Condition
本身接受两个数组作为参数:第一个是上下文数据应匹配的条件,第二个是在条件匹配上下文时应使用的新的配置数据。这种新的配置数据遵循所有其他配置数据相同的规则:如果是新的/唯一的,则将其附加到合并的主配置中。如果它包含与现有配置项相同的配置键,则将其覆盖(如果条件匹配的话!)
条件目前不级联:只有最后一个定义的条件与给定的上下文匹配时才会使用。