Going for Symfony | 第四天

抱歉,过了这么久才放出我第四天的进程:因为这一天的过程实在比较难,而结果却很有成就感。我完成了以下工作:

  1. 完成了某一本书的详细信息显示;
  2. 加入了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然后用回车的话,会出错。这个问题我将留在日后解决。先让我得意一会吧……)

Comments

2 responses to “Going for Symfony | 第四天”

  1. 猛禽 Avatar

    顶一个日后解决。哈哈

  2. […] 在第四天的心得中,我留下了一个小问题: […]

Leave a Reply to Going for Symfony | 第4.5天 – 生活在远方 Cancel reply

Your email address will not be published. Required fields are marked *