我们的留言本由于需要加入更多的操作,所以在页面HTML上面需要再修改一下,加入一个留言查看的弹窗。

    新的HTML在这里:下载新的页面

    解压取出里面的showmsg.html文件,它只是页面的HTML片段,主要提供显示一个查看单条留言的对话框,并不是完整的页面。

    将showmsg.html放到protected/view目录下面。

    我们现在需要把这个页面插入到guestbook.html中。打开guestbook.html,在最下面填写留言表单的下方,我们加入一行代码:

    看起来大概是这样:

    刷新localhost后,右键查看源代码,我们会发现showmsg.html的内容已经放在include那个位置上了。

    这是sp框架模板引擎的include语法功能,具体了解可以参考手册相关内容。

    inlucde语法的好处是可以把模板内通用的HTML片段抽离出来独立成文件,方便其他模板文件引用进去,达到一次修改就可以改变所有引用的内容。

    二、查看单条留言

    我们在protected/controller目录下面,新建个php文件,名称是:ViewController.php,里面内容是:

    1. class ViewController extends BaseController {
    2. function actionShow(){
    3. // 把提交的upid参数,作为值来构造一个查询条件数组,
    4. // 条件是:把id字段中等于提交upid参数值的记录查询出来
    5. $condition = array("id" => arg("upid"));
    6. $guestbook = new Model("guestbook");
    7. // 通过find()方法,按条件数据进行查询,
    8. // find返回结果是对应记录的一维数组
    9. $result = $guestbook->find($condition);
    10. // 输出看看
    11. dump($result);
    12. }
    13. }

    当我们打开浏览器http://localhost/view/show.html?upid=1 ,可以看到:

    find结果

    内容比较多,我们仔细来研究研究:

    • 这里是ViewController类,跟MainController一样,它继承于BaseController类,而且类的名字跟文件名是一样的。
    • ViewController类里面的方法叫actionShow(),action开头的方法是可以被浏览器访问到的。actionShow()是执行这块操作的主体代码块。
    • actionShow()方法开始我们把提交上来的参数upid,作为值构造成了一个查询条件的数组,数组名称是$condition。
    • arg(“upid”)代表获取浏览器提交上来upid的参数。
    • 继续实例化guestbook表,然后调用find()方法,用$condition这个条件数组作为参数输入。
    • 取得find()方法的结果$result,并且用dump()进行调试输出观察。

    Model类的find()方法,指的是查询一条符合条件的记录,返回的是一维数组。

    对比一下前章节学习过的findAll()方法,我们把上述代码的find()直接改成findAll()。

    刷新浏览器我们可以看到跟find()的一点不一样的地方了:

    findAll结果

    是的,findAll()比find()的结果多了个0,也就是多了一个维度。

    从原理上来讲,find()方法内部其实是调用了findAll()来查询,但是会指定只返回第一条记录,然后find()会把第一条记录变成一维的数组再返回给用户。

    也就是说,find()实际上是findAll()的第一条记录,不过find()相对方便的是它是一维数组,可以直接通过类似$result[“title”]的方式来取得值,而不是findAll()结果的$result[0][“title”]。

    相同条件下,find()的$result[“title”]是等于findAll()的$result[0][“title”]的。

    这时候我们已经可以指定查询到了单条留言的内容了。那么我们会把返回变成json格式。

    json格式是目前较为通用的数据传输格式。

    我们现在把dump()调试去掉,改成json输出:

    1. <?php
    2. class ViewController extends BaseController {
    3. function actionShow(){
    4. // 把提交的upid参数,作为值来构造一个查询条件数组,
    5. // 条件是:把id字段中等于提交upid参数值的记录查询出来
    6. $condition = array("id" => arg("upid"));
    7. $guestbook = new Model("guestbook");
    8. // 通过find()方法,按条件数据进行查询,
    9. // find返回结果是对应记录的一维数组
    10. $result = $guestbook->findAll($condition);
    11. // 输出看看
    12. header('Content-type: application/json');
    13. echo json_encode($result);
    14. }
    15. }
    • header()函数的作用是输出后让浏览器识别这是一个json流。
    • json_encode()函数是php自带函数,可以将php数组转换成json格式,这里转换好了之后,就echo输出了。

    刷新浏览器我们可以看到json的输出了:

    这里看起来像是乱码的东西,就是json格式的数据。


    接下来我们修改一下HTML,对,就是插入那个showmsg.html

    1. <div class="modal fade" id="showModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
    2. <div class="modal-dialog" role="document">
    3. <div class="modal-content">
    4. <div class="modal-header">
    5. <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
    6. <h4 class="modal-title" id="show_title"></h4>
    7. </div>
    8. <div class="modal-body">
    9. <p id="show_contents"></p>
    10. <blockquote class="blockquote-reverse small">
    11. <ul class="list-inline text-muted">
    12. <li>by</li>
    13. <li id="show_createtime"></li>
    14. <button type="button" class="btn btn-default btn-xs"><span class="glyphicon glyphicon-thumbs-up" aria-hidden="true"></span> 100</button>
    15. </li>
    16. </ul>
    17. </blockquote>
    18. </div>
    19. <div class="modal-footer">
    20. <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
    21. </div>
    22. </div>
    23. </div>
    24. </div>
    25. <script>
    26. function showmsg(id){
    27. // 由于需要JSON数据,所以使用$.getJSON
    28. $.getJSON("<{url c="view" a="show"}>", { 'upid': id }, function(json){
    29. // 这里是返回的数据,json变量就是json格式的数据,可以直接使用
    30. // 给各个位置赋值上去
    31. $("#show_title").html(json.title);
    32. $("#show_contents").html(json.contents);
    33. $("#show_username").html(json.username);
    34. $("#show_createtime").html(json.createtime);
    35. // 显示对话框
    36. $('#showModal').modal('show');
    37. });
    38. }
    39. </script>

    我们修改了不少地方,逐个来看看。

    • 在每个需要显示内容的HTML元素上,我们加入了id的属性,也就是分别给它们取个名字,方便通过JS来进行赋值。
    • 加入了一个JS函数showmsg(),这个js函数的作用首先是调用jQuery库的getJSON()方法,把传入的id发到<{url c=”view” a=”show”}>这个地址上面去。
    • 然后接收返回的json数据,一一对应填入HTML里面。
    • 最后把对话框显示出来。

    我们还需要给每个留言增加个启动showmsg()函数的链接。在guestbook.html里面,我们把循环里面的标题增加一个链接。

    把原来的:

    1. <h4 class="media-heading"><{$r.title}><button type="button" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button></h4>

    改成了:

    1. <h4 class="media-heading"><a href="javascript:void(0);" onclick="showmsg(<{$r.id}>)"><{$r.title}></a><button type="button" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button></h4>
    • onclick是HTML的一个事件,意思是点击后发生点什么,这里的代码指的是“当点击这个链接时,指向地址是空的,但是会启动JS函数showmsg()”
    • showmsg()函数传入了当前留言的id值。

    保存,刷新浏览器。点击任何一条留言的标题,那么就可以看到:

    查看单条留言

    这里弹出的内容,是通过Ajax从view/show里面获取到的。

    看到每个留言下面的大拇指标志吗? 通常在网络上这是点赞的意思。我们来实现这样的功能吧。

    点赞通常是一个数字,所以我们需要在原来的数据表guestbook上面新增一个数字字段来存储它,名字就叫dig。这里是通过PHPMyAdmin增加的界面:

    对应的SQL语句是:

    1. ALTER TABLE `guestbook` ADD `dig` INT(11) NULL DEFAULT '0' AFTER `username`;

    完成后我们可以看到dig字段已经在那儿了。

    增加dig字段结果

    打开ViewController.php,增加一个action作为点赞操作的方法。

    • 首先我们是查询出来对应的id记录的dig数量是多少,然后加一。
    • 再通过update把加一后的值更新回去
    • 最后显示一下新的值

    update()方法输入两个参数,前面参数是条件数组,后面参数是需要更新的字段+值。

    从使用方式来看,update()方法很像是find() + create()的组合。

    然后在guestbook.html页面上面,我们修改一下点赞显示的方式,加入onclick事件。

    因为点赞显示的位置没有元素,所以我们可以用span把数字包裹起来,这样就可以给它赋以名称id了。

    1. <button type="button" class="btn btn-default btn-xs" onclick="digmsg(<{$r.id}>)"><span class="glyphicon glyphicon-thumbs-up" aria-hidden="true"></span> <span id="dig-<{$r.id}>"><{$r.dig}></span></button>

    首先我们刷新一下页面,会发现点赞数已经出来了——当然只有0。

    然后我们在showmsg.html的script里面,再加上digmsg()函数:

    1. function digmsg(id){
    2. // 这里不需要json返回,所以直接使用$.get即可
    3. $.get("<{url c="view" a="dig"}>", {"upid" : id}, function(dig){
    4. $("#dig-" + id).html(dig);
    5. });
    6. }

    这里我们有个小技巧,就是在页面foreach循环的时候,点赞位置的id是dig-<{$r.id}>,所以每条留言的这个id都是不一样的,所以在 $(“#dig-“ + id).html(dig);就可以把返回的数据填充到正确id的点赞位置了。

    完成后我们刷新一下浏览器,发现点赞数字可以点击了,并且点击后,数字会变大。

    最后我们还需要修改一下查看留言的弹出框,把点赞数也显示上去。

    包括把弹出的HTML点赞位置改为:

    1. <span id="show_dig"></span>

    JS函数showmsg()里面也要增加:

    1. $("#show_dig").html(json.dig);

    改好后弹窗框也OK了:

    点赞结果

    四、时间显示的补充

    从上图弹出框可以看到留言时间还是时间戳。我们来把它修改一下成好看的日期吧。

    在ViewController的actionShow()方法里面,我们加入一个转换:

    1. $result["createtime"] = date("Y年m月d日 H:i:s", $result["createtime"]);

    放在查询到$result后,还没有输出之前。大概的位置是:

    那么弹出框时间也就正常了。

    这里介绍的技巧,是当查询到数据之后,其实还是可以进一步修改的,有时候我们在php端修改,会更方便点。

    一般数据库操作,最基本的就是增删改查(CURD),分别是增加(create)、删除(delete),修改(update),查询(find/findAll)。增改查前面已经介绍过了,我们现在来介绍最后一个删除操作。

    打开ViewController.php,增加一个action作为删除操作的方法。

    1. function actionDel(){
    2. // 同样的,首先是根据提交参数确定条件
    3. $condition = array("id" => arg("upid"));
    4. $guestbook = new Model("guestbook");
    5. // 直接返回结果,实际上这个结果是被忽略的
    6. }

    删除操作相对简单,跟其他操作一样,先是确定数组条件,然后直接调用delete()方法。

    delete()方法只有一个参数,代表了删除记录的条件。

    接下来我们修改guestbook.html文件,首先我们把foreach循环留言的大块DIV,加入一个ID,以识别不同的留言。

    然后在每个留言的右上角,已经有个 x 了,这是一个删除按钮。我们加入事件给它。

    一个xx

    代码是:

    1. <button type="button" class="close" aria-label="Close" onclick="delmsg(<{$r.id}>)"><span aria-hidden="true">&times;</span></button>

    具体部分代码是:

    1. ...
    2. <div class="col-md-8">
    3. <{foreach $records as $r}>
    4. <div class="panel panel-default" id="panel-<{$r.id}>">
    5. <div class="panel-body">
    6. <div class="media">
    7. <div class="media-left">
    8. <a href="#">
    9. <img class="media-object" src="/i/img/1.gif" alt="...">
    10. </a>
    11. </div>
    12. <div class="media-body">
    13. <h4 class="media-heading"><a href="javascript:void(0);" onclick="showmsg(<{$r.id}>)"><{$r.title}></a><button type="button" class="close" aria-label="Close" onclick="delmsg(<{$r.id}>)"><span aria-hidden="true">&times;</span></button></h4> <{$r.contents}>
    14. <blockquote class="blockquote-reverse small">
    15. ...

    然后我们还是在showmsg.html的script里面,再加上delmsg()函数:

    1. function delmsg(id){
    2. // 这里不需要json返回,所以直接使用$.get即可
    3. $.get("<{url c="view" a="del"}>", {"upid" : id}, function(){
    4. $("#panel-" + id).fadeOut();
    5. });
    6. }
    • 这里实际上不需要返回值,所以也没有赋值给返回值。
    • fadeOut()是一个很好玩的动画效果,会让页面上的元素渐变消失。

    OK后我们来刷新一下页面,点击其中一个留言右上角的叉,留言就消失了。

    六、分页显示

    当我们留言内容变多的时候,页面会变得很长,这时候我们需要对留言进行分页显示。

    分页也是比较基础的功能之一,大部分网站都可以见到它的身影。

    另外在我们的页面上,其实也是有分页的HTML在的。

    我们先来介绍一下关于findAll()方法的深入使用方法。

    findAll()方法有四个参数,依次是$conditions,$sort,$fields,$limit;

    • $conditions是条件数组,默认情况下是字段对应值的查询条件。
    • $sort是排序方式,比如说接下来我们会用到“createtime DESC”就是根据createtime创建时间倒序查询。
    • $fields是查询出来的字段,一般是“*”即可。
    • $limit是限定查询的数量,也是我们分页的基础。默认情况是“第几条,要几条”,比如说“10, 20”指的是从第10条记录开始,查出20条数据。

    除了限定条数,$limit还有一种分页的方法:数组形式的$limit是自动分页的功能,指的是array(第几页, 每页多少条);如如 $limit = array(10, 20);,就是查出第10页,每页20条记录。

    “查出第10页,每页20条记录”,我们可以知道实际上findAll出来的结果最多是20条。

    打开MainController文件,我们修改一下actionIndex()方法。

    1. function actionIndex(){
    2. // 接收页码参数
    3. $page = (int)arg("p", 1);
    4. // 实例化一个guestbook的模型类
    5. $guestbook = new Model("guestbook");
    6. // 用findAll()方法查询guestbook表的全部数据
    7. $this->records = $guestbook->findAll(null, "createtime DESC", "*", array($page, 3));
    8. // 输出看看
    9. // dump($this->records);
    10. $this->pager = $guestbook->page;
    11. // dump($this->pager);
    12. $this->display("guestbook.html");
    13. }

    改后我们发现页面上只有3篇留言了。下面我们来研究研究:

    页码

    • 接收页码$page,我们用了arg(“p”, 1);后面参数1是代表如果p不存在,则返回1.
    • (int)arg(“p”, 1)的意思是强制将p参数转换成数字,保证数据库安全。

    findAll

    1. $guestbook->findAll(null, "createtime DESC", "*", array($page, 3));

    这里用了findAll的四个参数:

    • null指的是没有条件,查询全部内容。
    • “createtime DESC”指的是按createtime倒序查询,如果是正序那么应该是”createtime ASC”。
    • “*”是全部字段,默认这个就好。
    • array($page, 3),这里指的是:当前是第$page页,每页3条留言。

    综上我们可以知道,现在$this->records查询出来的结果是第$page的3条留言。

    因为我本机上面留言较少,所以分页只设置了3页,如果大家有兴趣可以设置大点的数字。

    page

    1. $this->pager = $guestbook->page;

    这句代码紧跟着上面的findAll查询,意思是从$guestbook里面取出上一次findAll给出来的页码数据。

    这句代码必须紧跟着findAll,如果$guestbook再来一次findAll,那么可能$guestbook->page就不是第一次findAll的页码数据了。

    $this->pager输出看看:

    取得页码数据后,我们就可以开始修改页面上的页面显示了。找到guestbook.html文件的约55行左右,把分页显示的nav整个修改成:

    • 首先我们判断是否有分页数据,这里<{if}>标签的作用就是如果$pager为空,则不会显示整个分页。$pager为空的意思是数据表记录没有能填满一页(根据分多少页的参数)。
    • 填充上一页和下一页,注意是通过url函数传递p参数的
    • 循环all_pages的页码数据,显示各个页码。
    • 最终分页上面会生成各个分页的链接。

    我们来看看最终效果:

    分页效果


    以上我们已经了解过简单数据操作和Ajax等相关知识,sp框架的初级内容已经介绍得差不多了。请大家继续学习我们的教程,以便了解更多相关知识。