第四章:验证和想象图
1 开始
我们将对上一回开发的程序作如下改善
没有输入数据时出错误提示
姓名只需输入一次
可以自动更新
防止一次投稿被重载两次
处理HTML特殊字符
输入数据限长
保存所有记录,但只显示固定期限的数据
在模板中调整数据显示格式,使改变页面设计更方便
2 利用验证功能判断是否输入数据
Mojavi配备了名为Validate的输入验证功能,它具有判断数字大小,字符串长度,邮箱地址格式等功能。我们先试验一下最简单的判断数据是否输入功能。在Mochat/actions/IndexAction.class.php中追加3个方法。
//Validate功能有效
function validate (&$controller, &$request, &$user)
{
return TRUE;
}
//Validator登陆
function registerValidators (&$validatorManager, &$controller, &$request, &$user)
{
//姓名:必须输入
$validatorManager->setRequired('name', TRUE, '请输入姓名');
}
//处理错误
function handleError (&$controller, &$request, &$user)
{
//运行getDefaultView(GET方式),转到画面处理
return $this->getDefaultView ($controller, $request, $user);
}
Validate功能使用方法
1.function validate()激活validate并返回TRUE
2.function registerValidators()设定进行什么样的验证
3.function handleError()进行出错处理
function validate()一旦返回TRUE,Validate会运行在function getRequestMethods()中设定的验证。产生错误时运行function handleError(),没有错误时运行function Execute()。function handleError()除了用于验证之外,还用于判断类似用POST方式访问只能接受GET方式访问的程序时发生的错误。下面我们认识一下function registerValidators()的$validatorManager的功能
$validatorManager->setRequired('name', TRUE, '请输入姓名');
一旦参数name没有被输入就会产生“请输入姓名”的错误提示
void setRequired (string $name, bool $required, [string $message = 'Required'])
$name : 参数名
$required : 需要时设TRUE、不需要的话设FALSE
$message : 错误提示内容
3 Validate:StringValidator
POST方式下因为没有限制输入数据的长度,会接受庞大的数据量。利用Validator可以限制输入数据的长度。
姓名:限长20byte
发言:限长200byte
对Mochat/actions/IndexAction.class.php文件内的 function registerValidators ()作如下编辑
//Validator登录
function registerValidators (&$validatorManager, &$controller, &$request, &$user)
{
//姓名:必须输入
$validatorManager->setRequired('name', TRUE, '请输入姓名');
//姓名限长:20byte
$validator =& new StringValidator($controller);
$criteria = array('max_error' => '姓名过长。', 'max' => 20);
$validator->initialize($criteria);
$validatorManager->register('name', $validator);
//发言:可以不输入
$validatorManager->setRequired('comment', FALSE);
//发言限长:200byte
$validator =& new StringValidator($controller);
$criteria = array('max_error' => '发言过长。', 'max' => 200);
$validator->initialize($criteria);
$validatorManager->register('comment', $validator);
}
在文件Mochat/actions/IndexAction.class.php的开头应追加
require_once(VALIDATOR_DIR . 'StringValidator.class.php');
StringValidator类主要用来设定字符串的最大长度,最小长度。以及特定字符是不是允许被输入的类。如果不是判断是否输入数据,要用如下方法使用Validate
通过require_once调用VALIDATOR_DIR(通常mojavi/opt/validators)下的Validator类
Validator类实例化
通过设置Valodator条件,准备好参数序列,使Valodator初始化
在ValidatorManager里登录Validator
StringValidator的参数序列是如下使用的
max int 字符串最大长度
min int 字符串最小长度
max_error 字符串过长的错误提示
min_error 字符串过短的错误提示
allowed bool 特定字符是不是允许被输入
chars array 上一个条件的参数
chars_error Value contains an invalid character 上两个条件的错误提示
trim bool TRUE 是否去掉前后空格
下面我们为max和max_error赋值,形成参数序列,并命名为$criteria,然后运行$validator->initialize($criteria)为Validator初始化
把设定好的Validator登录到ValidatorManager当中
void register (string $param, Validator &$validator)
$param : 验证对象的参数名
$validator : Validator
这是对name和comment的设定
需要注意的是对于不是必填项的参数,要入下使用Validator
//发言:可以不输入
$validatorManager->setRequired('comment', FALSE);
如果setRequired函数没有设FALSE,该参数被自动是为必填项
Mojavi的Validator类还有很多其他功能。
4 错误提示
错误提示被保存在$request内,用下面两个方法可以取出。
string getError (string $name)
根据$name提出错误提示
array &getErrors ()
取出所有的错误提示
下面我们试验一下getErrors(),在Mochat/views/IndexView_input.class.php中追加
<?php
class IndexView extends View
{
function & execute (&$controller, &$request, &$user)
{
//获得信息并处理
$data = $request->getAttribute('data');
$body = '<HR>';
//错误提示
$error = implode('<br>', $request->getErrors());
while($line = array_pop($data)){
$body = $body. $line . "<HR>";
}
//完成想象图
$renderer =& new Renderer($controller, $request, $user);
$renderer->setTemplate('mochat_input.tpl');
$renderer->setAttribute('body', $body);
$renderer->setAttribute('error', $error);
return $renderer;
}
}
?>
$error = implode('<br>', $request->getErrors())是把各错误提示用<BR>连接并存入$error,然后再用setAttribute处理
下面处理模板,在Mochat/templates/mochat_input.tpl中追加
<html>
<head>
<title>Mochat - 聊天</title>
</head>
<body>
<form method="POST" action="./?module=Mochat">
姓名:<input type=text name="name">
发言:<input type=text name="comment" size=50>
<input type=submit value="发言">
</form>
<?= $template['error'] ?>
<?= $template['body'] ?>
</body>
</html>
然后再http://localhost/mojavi/?module=Mochat下试运行程序
5 保留姓名
每次发言都要输入姓名很不方便,我们要把程序改善成可以保留姓名的形式。在模板中把姓名设置到input的Value当中是很轻松的。按如下编辑文件
Mochat/actions/IndexAction.class.php
function getDefaultView (&$controller, &$request, &$user)
{
…
//将数据传给属性
$request->setAttribute('data', $data);
//将姓名传给属性
$request->setAttribute('name',$request->getParameter('name'));
return VIEW_INPUT;
}
Mochat/views/IndexView_input.class.php
function & execute (&$controller, &$request, &$user)
{
…
$renderer->setAttribute('error', $error);
$renderer->setAttribute('name', $request->getAttribute('name'));
return $renderer;
}
Mochat/templates/mochat_input.tpl
…
<form method="POST" action="./?module=Mochat">
姓名:<input type=text name="name" value="<?= $template['name'] ?>">
发言:<input type=text name="comment" size=50>
<input type=submit value="发言">
…
这样程序就可以保留姓名了,处理流程如下
1.在活动中通过参数取得姓名内容,设置到Request当中。
2.在显示文件中从Request取出姓名传入Renderer。
3.在模板中将姓名设置到input当中。
当然,如果不在活动中将姓名设入Request,而是在显示文件中直接从Request提出设入Renderer也是可以的,但是为了做到分工明确,使活动准备数据,显示文件准备显示,最好还是这样做。
另外,getParameter()在没有参数时返回NULL值,在用户首次登录时姓名栏是空的,此时可为函数设置默认值$request->getParameter('name','ゲスト')。
6 发言为空时的重载
重载的处理是在活动的Execute()中。我们要做到当发言栏为空时,不存入文件。在Mochat/actions/IndexAction.class.php中作如下修改
function Execute (&$controller, &$request, &$user)
{
if($request->getParameter('comment')){
if(file_exists(DATA_FILE)){
//如果文件存在,用追加形式打开文件
…
//关闭文件
fclose($fp);
}
//运行getDefaultView(GET方式),转到画面处理
return $this->getDefaultView ($controller, $request, $user);
}
7 防止一次投稿被重载两次
当用户在点击发言按钮后,发现有些东西忘写了,就强行点击刷新按钮,造成一次投稿被重载两次。这个问题通常有两个解决方法
1.安装与Struts中的generateToken()/isTokenValid()/resetToken()相似的功能
2.第二次重载时,重新产生要求。
前者根据系统时间产生票,把票插入到页面里,在用户发送时票也被发送。当第一次处理完成后,票被设无效,这样当第二次重载在到来时,该页面的票已无效了。后者重新产生新的页面而不管以前的重载。前者有些复杂,我们采用后者。Mojavi中有重定向处理的功能,我们将使用它
Mochat/actions/IndexAction.class.php文件中
//getDefaultView运行getDefaultView(GET方式),转到画面处理
return $this->getDefaultView ($controller, $request, $user);
将上面的页面置换
//重定向到自身
$module = $controller->getRequestModule(); //项目名
$name = urlencode($request->getParameter('name')); //姓名
$controller->redirect("?module=$module&name=$name");
在此使用的两个$controller函数有下面的功能
string getRequestModule ()
返回被重定向的项目名、同样功能但返回活动的函数为string getRequestAction。
void redirect (string $url)
重定向的路径
使用这两个函数,使程序被重定向到第二次重载,避免了二次重载。
8 理解Renderer
下一章我们将引入模板发动机Smarty,在此之前我们要讨论一下Mojavi的Rednerer到底是什么。直率的说模板的内容是PHP的脚本,Renderer类调用并执行它们。看下面代码
class Renderer
{
var $attributes;
function setAttribute ($name, $value)
{
对$attributes赋值
}
function execute (&$controller, &$request, &$user)
{
$template =& $this->attributes;
模板内容
}
}
Require()在读文件时,一旦离开PHP模式进入HTML模式,在遇到<?之前将模板的内容输出,并且在遇到<?时返回<?模式,按php要求显示数据。例如:
<?= $template['body'] ?>
<?= ?>是<?php echo ; ?>的省略形式
所以<?php echo $template['body']; ?> 与前一句是等价的。另外,还可以用setAttribute()输出被设置在$attributes中数据。
现在的Mochat中,在Mochat/views/IndexView_input.class.php内用这样一段代码来产生发言列表
while($line = array_pop($data)){
$body = $body. $line . "<HR>";
}
这样每两行之间用<HR>分开,并被传到模板中。这样一来涉及就被分散到模板和显示文件两处进行。如果想改变<HR>的话很麻烦,所以把这段处理转移到模板当中。
Mochat/views/IndexView_input.class.php <?php
class IndexView extends View
{
function & execute (&$controller, &$request, &$user)
{
删除
//错误提示
$error = implode('<br>', $request->getErrors());
删除
//Renderer
$renderer =& new Renderer($controller, $request, $user);
$renderer->setTemplate('mochat_input.tpl');
$renderer->setAttribute('body', $request->getAttribute('data'));
$renderer->setAttribute('error', $error);
$renderer->setAttribute('name', $request->getAttribute('name'));
return $renderer;
}
}
?>
对
Mochat/templates/mochat_input.tpl
<?= $template['body'] ?>
作如下替换
<HR>
<?php
while($line = array_pop($template['body'])){
echo $line . "<HR>";
}
?>
这样一来,涉及部分都被归纳到模板中。
但是,这样一来显示文件类就没有存在意义了,所以可根据自己的习惯使用。