@life 在Solar自定义Filter过滤器 一文中讲述了如何在Solar中添加自己的过滤器,讲得很不错。呵呵:), @life 和@libear 加入Solar 中国 没多长时间,但贡献却非常突出,给Solar 中国 注入了更有活力的思想。
回到正题,本文也是讨论在Solar中自定义Filter,那么为什么要写一篇同样主题的文章呢?看完你就知道了。
讲这个之前我认为有必要先讲一下life提到的自定义Filter如何实现的。那么之前又还要再讲一下Solar_Sql_Model_Record 这个类。
关于这个类,相信大家不会陌生,当我们每次从对象注册表中获取模型目录对象后,再利用模型对象获取的就是Record对象。
01.
$this
->_model = Solar_Registry::get(
'model_catalog'
);
02.
// $this->item就是模型记录对象
03.
$this
->item =
$this
->_model->currentProducts->fetchNew();
04.
$this
->form =
$this
->item->newForm(
array
(
05.
'digs_out'
=>
array
(
'label'
=>
'请输入免单需顶数:'
,),
06.
'discout'
=>
array
(
'label'
=>
'请输入折扣:'
,
'invalid'
=>
array
(
'hello, libear'
,
'hello, life'
)),
07.
'product_id'
=>
array
(
'type'
=>
'hidden'
,),
08.
'period_id'
=>
array
(
'type'
=>
'hidden'
,),
09.
));
这就创建了大家非常熟悉的form对象,之后怎么做大家都知道的。那么filter是在什么时候验证的呢?如何保证Solar能找到我们自定义的Filter呢?
filter事件是在$this->item->save()方法调用的时候触发的,来看代码:在Solar_Sql_Model_Record::save() 方法又调用了Solar_Sql_Model_Record::_save() 方法,在_save()方法中判断当前的保存操作是插入还是更新:
1.
// insert or update based on newness
2.
if
(
$this
->isNew()) {
3.
$this
->_insert();
4.
}
else
{
5.
$this
->_update();
6.
}
在Solar_Sql_Model_Record::_insert() 和Solar_Sql_Model_Record::_update() 方法中分别调用了Solar_Sql_Model_Record::filter() 方法。当参数为空时,filter方法直接调用Solar_Sql_Model_Record::newFilter() 方法,获得当前的filter对象,进行验证并准备好invalid消息:
01.
// apply filters
02.
$valid
=
$filter
->applyChain(
$this
);
03.
04.
// retain invalids
05.
$invalid
=
$filter
->getChainInvalid();
06.
07.
// ...省略部分代码
08.
// was it valid?
09.
if
(!
$valid
) {
10.
// use custom validation messages per column when available
11.
foreach
(
$invalid
as
$key
=>
$old
) {
12.
$locale_key
=
"INVALID_"
.
strtoupper
(
$key
);
13.
$new
=
$this
->_model->locale(
$locale_key
);
14.
if
(
$new
!=
$locale_key
) {
15.
$invalid
[
$key
] =
$new
;
16.
}
17.
}
18.
19.
$this
->_invalid =
$invalid
;
20.
throw
$this
->_exception(
'ERR_INVALID'
,
$this
->_invalid);
21.
}
那么Solar这样就能找到我们自定义的Filter么?还是不行,还要继续看Solar_Sql_Model_Record::newFilter() 方法, newFilter()方法的主要工作是实例化当前的Filter对象,
1.
// create a filter object based on the model's filter class
2.
$filter
= Solar::factory(
$this
->_model->filter_class);
大家肯定想了解下$this->_model->filter_class的内容吧?:) 还要麻烦大家查看一下Solar_Sql_Model::_fixFilterClass() 方法。这个方法设置了当前的Filter类。
1.
$this
->_filter_class =
$stack
->load(
'Filter'
);
接着,Solar_Sql_Model_Record::newFilter() 把filter规则加入Filter对象,最后把本地字符串的对象设置为当前过滤器。filter规则来自几个方面:
1. 在模型文件中定义的, i.e., $this->_addFilter()
2. 手动使用$this->addFilter定义的
3. 表中定义为非空的及自动增长的字段
01.
// set filters as specified by the model
02.
foreach
(
$this
->_model->filters
as
$key
=>
$list
) {
03.
// skip table cols that are not part of the fetch cols
04.
if
(in_array(
$key
,
$skip
)) {
05.
continue
;
06.
}
07.
$filter
->addChainFilters(
$key
,
$list
);
08.
}
09.
10.
// set filters added to this record
11.
foreach
(
$this
->_filters
as
$key
=>
$list
) {
12.
$filter
->addChainFilters(
$key
,
$list
);
13.
}
14.
15.
// set which elements are required by the table itself
16.
foreach
(
$this
->_model->table_cols
as
$key
=>
$info
) {
17.
if
(
$info
[
'autoinc'
]) {
18.
// autoinc are not required
19.
$flag
= false;
20.
}
elseif
(in_array(
$key
,
$this
->_model->sequence_cols)) {
21.
// auto-sequence are not required
22.
$flag
= false;
23.
}
else
{
24.
// go with the col info
25.
$flag
=
$info
[
'require'
];
26.
}
27.
28.
// set the requirement flag
29.
$filter
->setChainRequire(
$key
,
$flag
);
30.
}
31.
32.
// tell the filter to use the model for locale strings
33.
$filter
->setChainLocaleObject(
$this
->_model);
这一部分算是讲完了,但是如果仅仅是这样处理的话当我们使用Solar_Form创建表单并加入filter规则的时候,系统会提示找不到 filter规则,因为Solar仅在Solar_Filter_名字空间下查找我们自定义的Filter对象,而我们自定义的Filter对象在 Admin_Filter_名字空间下,也就是说系统根本没有实例化Admin_Filter类。在上面的解决方案 中,Solar_Sql_Model_Record实例化了Admin_Filter,并使用Admin_Filter进行filter操作,因为这里没 有用到模型,所以我们只能手动操作。
首先,在config.php文件中加入Solar_Filter的配置项,把Admin_Filter_名字空间加入Solar_Filter::_stack中:
1.
$config
[
'Solar_Filter'
] =
array
(
2.
'classes'
=>
array
(
'Admin_Filter'
),
3.
);
这样的话系统就能找到我们自定义的filter了,不过local string仍然不行,我们还需要手动设置当前的filter local object,有两种方法:
1. 通过Solar_Fomr::setFilterLocaleObject 方法手动设置filter local object
1.
$filter
= Solar::factory(
"Admin_Filter"
);
2.
$this
->form->setFilterLocaleObject(
$filter
);
2. 在实例化Solar_Form时指定local filter object
1.
$this
->form = Solar::factory(
"Solar_Form"
,
2.
array
(
'filter'
=> Solar::factory(
'Admin_Filter'
)));
可以看到不管用哪种方法都需要手动实例化Admin_Filter类。
下一篇讲讲Solar_Form、Solar_Sql_Model_Record和Solar_Filter三者交织在一起的复杂关系。