抱歉,过了这么久才放出我第四天的进程:因为这一天的过程实在比较难,而结果却很有成就感。我完成了以下工作:
- 完成了某一本书的详细信息显示;
- 加入了AJAX的支持,可以由浏览者增加一本书的tag并更新到数据库中;
=============
首先来看第一步的实现。在我的实现中,数据库中记录书籍信息的表既是“一”端,也是“多”端:多本书可以对应一个出版社和购买地点,而一本书可以对应多个tag。
在书籍详细信息表中,我会列出出版社和购买地点的名称,而不是简单的列出出版社和购买地点的ID。在Symfony中,无论是从N到1还是从1到N的数据获得是很直接的。以下的代码来自detailSuccess.php,它是对应于action.class.php文件中executeDetail函数的模板:
//action.class.php
public function executeDetail(sfWebRequest $request)
{
$this->book=$this->getRoute()->getObject();
}
编者、译者:
getCopyrighter();
译本否:
getTranslated()?'是':'否';
出版社:
getBookPublisher();
购买/整理日期:
getPurchdate(sfConfig::get('app_date_format'));
购买地点:
getBookPlace();
注意,这里我用getBookPublisher直接获得了对应本书的出版社的名称。对于tags的显示,稍微有些不同:
TAG:
<?php $book->getBookTagLists())) ?>
<form id="frmUpdateTags" action="<?php echo url_for('@update_tags_ajax')?>" method="post">
<input id="txtNewTag" name="txtNewTag" size="60" type="text" />
<a id="btnaddtags" class="smalltext" href="#"><img src="/images/tag-button.gif" alt="Add new tags" /></a>(以空格分割)
<input id="txtBookID" name="txtBookID" type="hidden" value="<?php echo $book->getID() ?>" />
</form>
从代码可以看到,要获得多端的数据,可以用getBookTagLists实现。注意,我在数据库和模型中只是定义了BookTaglist和对应的PK/FK。Symfony是很聪明的,它解析到了这个PK/FK关联,从而在我的BookBook类中创建了一个获得多端数据的方法,它的命名就是get+多端的类名+s(s表示复数,是多端)。同时,我用include_partial来专门显示tags,这是考虑到tags可能被用户修改(一个update的操作)从而需要重新显示,因此update后的显示代码和单纯的显示代码(及模板)是可以复用的。
_tags.php的代码如下:
<div id="book_tags">
<?php>
foreach ($tags as $tag)
{
echo link_to($tag->getTag(), 'books_with_tag', array('p'=>1, 'tag'=>$tag->getTag())).' ';
}
?>
</div>
有了上述的代码,一本书的详细信息的显示就算完成了。接下来我要解决的是用AJAX让用户可以增加他为这本书定义的tag。
=============================
首先,这个动作需要一个路径,我是这样定义的:
update_tags_ajax:
url: /books/updatetags
param:
module: books
action: updatetags
然后在actions.class.php中创建这个动作的处理:
public function executeUpdatetags(sfWebRequest $request)
{
$id=$request->getPostParameter('id');
$tags=$request->getPostParameter('tags');
return $this->renderPartial('books/tags',array('tags'=>BookBookPeer::doUpdateTags($id, $tags)));
}
将doUpdateTags函数定义在BookBookPeer中不是最好的方法,因为按照Symfony的设计理念,Peer类中应该只定义返回记录集的函数。不过,鉴于我这个函数要返回更新后的tags,这么做也不算太离谱。renderPartial是Symfony的函数,专门用来渲染整个页面的一部分,用它来和AJAX配合是最好不过的了。
我们再看看doUpdateTags函数:
static public function doUpdateTags($id, $tags)
{
$book=BookBookPeer::retrieveByPk($id);
$tagslist=explode(" ", $tags);
foreach ($tagslist as $tag)
{
$tl=new BookTaglist();
$tl->setId($id);
$tl->setTag($tag);
$book->addBookTaglist($tl);
}
$book->save();
return $book->getBookTaglists();
}
这里的关键是,我要将输入的tags字符串用空格分裂开,然后对于每个tag创建一个BookTagList对象,并将它加入到book对象中。最后我调用save和getBookTaglists返回更新后的tags。这个返回的tags将在executeUpdatetags中继续传递个tags.php模板进行显示。于是我达到了代码、模板复用的目的。
注意,这里我们没有对增加的tag是否和已有的tag重复进行判断。由于BookTaglist是以bookid+tag作为主键的,因此出现重复的tag会造成出错。不过这不是我的疏忽或偷懒,而是Symfony在addBookTaglist的实现中已经考虑到了这点,它会自动过滤重复的tag。
最后,是进行AJAX的编写。Symfony建议用jQuery来实现。我将最新的jQuery保存在/web/js目录下,同时在这个目录中再创建一个专门用来增加tags的js文件add_tags.js:
$(document).ready(function()
{
$("#btnaddtags").click(
function(event)
{
event.preventDefault();
var tags=$("#txtNewTag").val();
var id=$("#txtBookID").val();
//alert(tags+"|"+id);
$("#book_tags").load(
$(this).parents('form').attr('action'),
{id: id, tags: tags}
)
$("#txtNewTag").val("");
});
}
);
这里最关键的函数就是load()。其具体作用我就不详述了,需要了解的可以看jQuery的API文档。
最后,我将修改detailSuccess.php文件,加入对jQuery和add_tags两个js文件的引用:
<?php use_javascript('jquery.js') ?>
<?php use_javascript('add_tags.js') ?>
测试一下吧……很好!成功了!
(一个小bug:现在的情况是,只能用点击那个按钮的方式来提交新的tags,如果输入了一些tags然后用回车的话,会出错。这个问题我将留在日后解决。先让我得意一会吧……)
Leave a Reply to 猛禽 Cancel reply