Month: March 2011

  • 重构狗屎皮:第四天

    今天进入狗屎皮重构的第四天。有件事要纪念一下:从今天晚间开始,我的虚拟主机上站点:BSpMq.com以及rsywx.net无法正常访问,必须翻墙。我实在不能明白的是,这样一个纯粹是打P扯谈的站点,一个以藏书、温和时事评论的站点怎么会被封?GFW真的是不可理喻了!

    进入妓术专题。

    今天进入狗屎皮重构的第四天。我主要解决了分页的问题。

    Symfony进行分页也非常简单,因为它内置了sfPagination这个类,我们只要传递一些参数,并在模板中加入一些代码就可以了。

    在我的开发中,第一个要解决的分页界面是“类别”中的文章。先放最终效果:

    category_with_pagination

    具体实现步骤如下:

    首先,修改routing.yml文件中关于显示属于某个类别的文章的路径,加入分页参数:

    show_category:
        url: /category/:slug/:page
        class: sfDoctrineRoute
        options:
            model: G4pCategory
            type: object
        param:
            module: category
            action: show
            page: 1

    为了简便,我给它定义了缺省的页数为1。

    然后,修改category/actions/actions.class.php文件中的executeShow函数:

        public function executeShow(sfWebRequest $request)
        {
            $this->category = $this->getRoute()->getObject();
            $this->c_slug=$this->category->getName();
            $this->pager=new sfDoctrinePager('G4pArticle', sfConfig::get('app_max_articles_on_category'));
            $this->pager->setQuery($this->category->getArticlesQuery($this->category->getId()));
            $this->pager->setPage($request->getParameter('page', 1));
            $this->pager->init();
        }

    这里的关键是最后四行。我创建了一个sfDoctrinePager对象,传递了它的查询SQL,并设置其要显示的页面(从sfWebRequest,也就是从$_GET中传递进来),然后进行初始化。注意,这里的SQL——严格说是一个查询的对象不是直接来自于之前我常用的G4pCategoryTable.class.php文件,而是来自G4pCategory.class.php文件。这两个文件的区别说实话我也不是很清楚。按照我的认为,前者主要是包含一些直接返回记录集的函数,我们可以把这个文件看成是一个虚拟的表;后者是一些类方法,一般不会包含直接返回记录集的方法,并可以对类的操作如save,get某个字段的方法进行改写。

    getArticlesQuery函数具体定义如下:

        public function getArticlesQuery($id)
        {
            $q=Doctrine::getTable('G4pArticle')->createQuery('a')
            ->leftJoin('a.G4pCategory')
            ->where('a.G4pCategory.id=?', array($id))
            ->orderBy('a.published_at desc, id');
            return $q;
        }

    它和我之前定义的一些方法还是比较类似的,唯一的区别是我直接返回一个Query对象,而不是返回记录集(通过$q->execute()得到)。这是因为具体要得到哪些记录必须由pager来确定,而pager是根据当前页面和每页显示的帖子数量来构建对应的SQL语句的。

    另外,请注意这个SQL的构成方法。Article表和Category表显然是一对多的关系(“一”在category表这边),所以我用left join来连接category表,我不需要指定on字段,因为这个是由Symfony根据表关系自动生成的。而在where子句中,我用了带参数的SQL构造,这样也可以有效的防止一些恶意的SQL注入攻击。

    最后,我们修改category模块的showSuccess.php函数(它其实就是一个模板文件),引入对应的记录集并加以操作,显示必要的导航等等。全部代码如下。为了方便阅读,我直接加了一些注释,如果你要直接使用代码,请将我的注释去除或者以正确的方式插入:

    <?php $articles=$pager-?>getResults() //获得当前的记录集 ?>
    <div class="pick">
        <div class="titlepic"><span class="sectionhead">文章分类:</span><span class="slug"><?php echo $c_slug; ??></span>
            <span class="more"><?php echo link_to('...所有文章', 'list_article') ??>
            </span>
        </div>
        <br></br>
        <div class="article">
            <ul>
                <?php foreach ($articles as $a):
                    $acount=Doctrine_Core::getTable('g4pcomment')-?>getCommentCount($a->id)->c;
                ?>
                <li><?php echo link_to($a-?>title, 'show_article', $a) ?>
                    <div class="writer">作者:<?php echo link_to($a-?>author, 'show_author', $a) ?> | <?php echo $a-?>published_at ?> | <a echo="" href="<?php" url_for=""><?php echo $acount ??>评</a>
                    </div>
                </li>
                <?php endforeach; ??>
            </ul>
            <hr></hr>
            <?php if($pager-?>haveToPaginate()): //判断是否需要分页,如果需要,那么显示导航条 ?>
            <a echo="" href="<?php" url_for=""><img alt="第一页" src="/images/first.png" title="第一页"></img></a>
            <a echo="" href="<?php" url_for=""><img alt="上一页" src="/images/previous.png" title="上一页"></img></a>
            <?php foreach ($pager-?>getLinks() as $page): ?>
            <?php if($page==$pager-?>getPage()): ?>
                <span class="currentpage"><?php echo $page ??></span>
            <?php else: ??>
            <a echo="" href="<?php" url_for=""><?php echo $page ??></a>
            <?php endif ??>
            <?php endforeach ??>
            <a echo="" href="<?php" url_for=""><img alt="下一页" src="/images/next.png" title="下一页"></img></a>
            <a echo="" href="<?php" url_for=""><img alt="末页" src="/images/last.png" title="末页"></img></a>
            <?php endif ??>
            <br></br><strong>本类别中共有<?php echo count($pager) ??>篇文章。</strong>
            <?php if($pager-?>haveToPaginate()): ?>
            当前为第<?php echo $pager-?>getPage()?>页,共<?php echo $pager-?>getLastPage() ?>页。
            <?php endif ??>
        </div>
    </div>

    就这么简单直接。 ===== 作为第4天,今天的工作其实还可以延续,因为我可以用相同的、很少的工作量完成其它几个页面的显示。不过这个就让我稍微偷偷懒,用个实际中的几天来完成吧。 本文收录于[go4pro.org]

    220322注:此时的Symfony还没有推出后来很流行的Twig模板引擎。

  • 重构狗屎皮:第三天

    今天进入了狗屎皮重构的第三天。今天要解决一个很关键的问题:如何让用户贴出评论?

    先看界面:

    ui-master-detail

    这时一个标准的主从表界面:帖子内容、已经有了的评论内容再加上一个表单方便用户提交评论。

    我本来认为要完成用户提交评论,在Symfony下是很简单的——简单到你可以不用写任何额外的代码,但实际情况并不如此。

    主要的一些修改如下。

    首先,要修改article的action.class.php文件中的executeShow函数:

    class articleActions extends sfActions
    {
        public function executeShow(sfWebRequest $request)
        {
            $article=$this->getRoute()->getObject();
            $this->article = $article;
            $this->comments=Doctrine_Core::getTable('g4pcomment')->getComments($article->getId());
            $this->form=new G4pCommentForm();
        }
        ......
    }

    上述代码中必须人肉创建了一个comment的表单,并传递到模板中进行进一步处理。

    在showSuccess.php模板中,通过include_partial来显示comment表单:

    include_partial('comment/form', array('form'=>$form, 'article_id'=>$article->getId()));

    注意,我还额外传递了一个article_id参数进去,这是为了后续的处理。

    修改comment表单文件如下,让它带一个article_id的参数:

    <?php echo form_tag_for($form, '@comment') ?>

    最后,修改comment的create以及processForm函数:

    public function executeCreate(sfWebRequest $request)
        {
            $this->forward404Unless($request->isMethod(sfRequest::POST));
            $aid=$request->getParameter('aid');
            $nc=new G4pComment();
            $nc->set('article_id', $aid);
            $nc->set('created_at', date('Y-m-d H:i:s'));
            $this->form = new G4pCommentForm($nc);
            $this->processForm($request, $this->form);
            //$this->setTemplate('article/show');
        }
    
    protected function processForm(sfWebRequest $request, sfForm $form)
        {
            $form->bind($request->getParameter($form->getName()), $request->getFiles($form->getName()));
            if ($form->isValid())
            {
                $comment = $form->save();
                $article=$comment->getG4pArticle();
                //var_dump($article);
                $this->redirect($this->generateUrl('show_article', array('id'=>$article->getId(), 'title_slug'=>$article->getTitleSlug())));
            }
        }

    在executeCreate函数中,设置了一个comment的外键article_id(从前一个页面传递过来),设置了comment创建的时间。

    在processForm函数中,需要修改一下处理完毕后的重定向,回到原来的帖子的页面,显示帖子、评论等。

    最后,我不得不说两句。我认为这个要求是几乎所有WEB程序都会用到的,但是我提问到Symfony的官方论坛后三天没有人回答!最后还是靠自己解决了问题……

    (本文收录于[go4pro.org])

  • 从地震、核泄露,到盐慌子孙

    地震我们中国人已经听过、甚至亲身经历过了。这次的日本地震虽说震级相当的大,但是如果没有加上“核泄露”,我相信不会在中国引发盐慌。

    我很少看新闻,我现在对外部世界的了解来自微博。我稍微回想了一下,这整个这个过程大概是这样的:

    1. 核电站爆炸——核泄漏
    2. 核泄漏防护——“碘”第一次进入视野
    3. 中国的食盐都是加碘盐——抢购盐

    这三步中,只有第一步是逻辑的推理。到了第二步,就有点待考的意思了;而到了第三步,则完全是失去了理智的举动。

    微博的风向是瞬息万变的,今天的新浪微博上,凡是我fo的,都不再谈地震、核泄露,而只谈盐慌子孙。

    微博的人才也是济济的,很快各类调侃的段子、对联、笑话就开始蔓延。我也转了好几个。

    ====

    我转的目的,却不完全是为了嘲笑那些在我们看来失去了理智从而变得异常不可理喻的同胞们——即使也许这些段子的本意就只是这个。

    我在思考两个问题:

    1. 他们为什么会如此行为?
    2. 为什么大家不愿意思考第一个问题,而更愿意提前进入全民欢乐模式?

    我先试图根据我的想法来回答第一个问题。

    今天我在微博上说过,“私”人要维持自己的生存权利是天经地义的。

    如果——只是如果——碘盐真的能防止核泄漏造成的核辐射,那么作为一个“私”人,他多买几包盐就是正常不过的举动——甚至可以说是正常的反应。

    而只有在确定自己的生存已经不再有威胁后,“私”人才会去考虑他所生活的群体中其它“私”人的生存问题。这时他要将他人分成两类:此人的生存将更有利于他自己的生存或者反之。他的优先考虑范围将是前者而不是后者。

    虽然在大部分日常生活情况下,我们都能很清楚的判断并分类,但是在这样一个突发、紧急情况下,我们却不再能、也不再愿、也没有时间去进行这样的判断,所以,在这样的情形下,唯一合乎逻辑和人性生存——及竞争——本性的行为,自然就是占据任何可以占据的资源,从而让他人无资源可占据。在当前情形下,这个资源很离奇的成为了“盐”。

    我们现在很淡定的讨论着这个问题,若有若无的在心里BS着他们,嘲笑着他们……却没有想到他们也许真的是没有别的选择。

    他们不像我们有较高的收入,没有那么畅通的获得信息的渠道,而生活的折磨让他们对ZF的公信、砖家的辟谣早已失去了信心并产生了习惯性的逆向思维,他们还能有什么选择?

    我们——我们这些在这里写着微博,带着笑意——也许还有一些善意,嘲笑着他们的人,我们怎么能这样对一个被逼如此去做的人呢?我们难道可以因为我们有别的选择就选择去嘲笑没有选择的人吗?

    撒谎的最高境界是:自己知道是在撒谎,而其它所有人都信以为真。可是,我们现在却只能听到撒谎的最杯具境界:自己知道自己是在撒谎,所有人也都知道他在撒谎,而只有他自己认为别人不知道他在撒谎!

    这个状态其实也是很玄妙的。平时无事,或者事不关己的时候,大家都会很理性、很温顺、很默契;而一旦有事,甚至出现人命关天的大事的时候,大家就一致抛弃谎言,很暴力、很夸张、很盐慌!

    这个状态其实是非常脆弱的。平时和谐,因无核胁;一有核胁,全无和谐。诱因多种多样,这次是盐,下次是醋,下次可能是柴米油酱茶。

    有人恐慌,就有人利用这个恐慌牟利,就有人善于制造恐慌。这个产业链是非常完善而又高效的……而不能阻止这样的产业链的产生,ZF难辞其咎!

    ===========

    我们其实都很清楚:碘盐其实无效,核泄漏要传到中国还为时尚早。所以,我们无法理解他们的行为。(我在这里用“我们”、“他们”并没有任何歧视的意思,只是为了更方便的区分一下两种类型的人群)。

    我相信所有这些网上的搞笑段子都很搞笑,也大都是善意的。只要是善意的,就还是带有劝醒性质的。所以,我也很是善意的加入了这个转发的行列。

    我只是希望,这样的一种恐慌能及时停止,ZF能切实拿出一些措施,不但要补充盐的供应,还要追究那些无良超市、无良供应商乘机哄抬物价的可耻行径。

    1.5元的盐涨到4元、6元,也许对于收入颇丰的家庭来说根本就无所谓,但是对于那些收入本来就捉襟见肘的家庭来说意味着神马,你们这些恶意哄抬物价、人为加剧紧张的兔崽子们想过吗!有木有!!!!!

  • 重构狗屎皮:第二天

    今天进入狗屎皮重构的第二天。实践证明,这第二天,确实是非常2的一天。

    首先,Symfony推出了一个新框架:Symfony 2。具体介绍见:http://www.symfony.com/。我刚搭建了Symfony 1.4.9的环境,实在不想在一个全新的框架上进行开发——虽然如此做对熟悉一个东西是很有好处的。

    其次,在进行代码版本控制——我用的是Mercurial——时,输入hg push死活没有反应……最后发现,由于在Windows下需要使用plink来连接远程的repo,而在第一次连接时,需要确认接受来自远程主机的一个证书,而直接hg push时,hg会间接调用plink,而此时Windows的CMD窗口无法正确的显示这个提示而导致出现“假死”。

    解决方法也很简单,先显式运行一次plink,接收远程主机证书后再运行hg push即可。

    第三,由于上次用Symfony还是进行我的任氏有无轩改版,距离今天已经一年多了,所以很多基本的东西都忘记了。比如:

    • 如果对route进行了一些修改,最好是进行一次symfony cc;
    • 如何进行主模板、子模板间的嵌套,以及数据的传递;
    • ……

    不过,总体而言还是非常顺利的。今天已经可以看到一些效果:

    go4pro.index go4pro.article.list

    我用Symfony开发WEB的速度确实是比我用Python开发WEB要快的多。

  • 重构狗屎皮:第一天

    今天开始重构狗屎皮。

    基本的框架是:后台机器人采用pMq编写的机器人进行采集,这个机器人是用Python编写的;由于我对Python以及基于Python的框架(我们之前用的是TG2)不是很熟悉,所以决定重新用Symofny改写。

    今天是第一天。主要是进行数据库的构建。

    解决了一个“小”问题,就是:Symfony在缺省时数据库所有文本字段采用的是拉丁编码,必须设置一下强迫其采用UTF8编码,代码如下:

    # File: config/database.yml
    all:
        doctrine:
            class: sfDoctrineDatabase
            param:
                dsn: mysql:host=localhost;dbname=go4pro
                username: root
                password:
                attributes:
                    default_table_charset: utf8
                    default_table_collate: utf8_general_ci

    最后三行紧要。

    今天还对样本数据进行了一些编写。用Symfony内置的机制进行数据填充还是很快很快的。

    比如我们可以这样编写一个comment.yml文件:

    # File data/fixtures/comment.yml
    G4pComment:
        first_comment:
            G4pArticle: first
            username: Dummy
            content: 好文章!
    <?php for($i=1;$i<=15;$i++): ??>;
        comment_<?php echo $i ??>:
            G4pArticle: first
            username: 访客<?php echo $i.n; ??>
            content: 我是路人<?php echo $i.n; ??>
    <?php endfor; ??>

    这样就可以一下子产生1+15个数据。YML文件是非常讲究格式的,必须注意书写时的缩进。

  • The Grand Design – 大设计

    一不小心就看完了霍金的新作《The Grand Design》。这是我在Kobo电子书上看完的第四本书。前三本分别是《The Confession》、《Decision Points》、《My Spiritual Journey》。

    这里顺大便说明一下,我登录这样的电子书的习惯和平时登录买的实体书不一样:我要等我看完这本书后才将这本书登录进我的任氏有无轩藏书管理程序。

    霍金在这本书里要回答的问题非常深奥:

    Why is there something rather than nothing? Why do we exist? Why this particular set of laws and not some other?

    简单翻译过来就是:

    为什么是“有”,而非“无”? 为什么我们存在? 为什么是这套特定的法则而不是其它?

    不过你千万不要把这本书作为某个理论的讲解。从某个角度而言,虽然霍金提出所谓的M-theory可能是我们(也是爱因斯坦)孜孜以求的大统一理论,他根本就不想讲述任何理论!

    正如他在本书结尾时讲到的:

    But perhaps the true miracle is that abstract considerations of logic lead to a unique theory that predicts and describes a vast universe full of the amazing variety that we see. If the theory is confirmed by observation, it will be the successful conclusion of a search going back more than 3,000 years. We will have found the grand design.

    简单翻译一下就是:

    但也许真正的奇迹是,抽象的逻辑思考将我们引入一个单一的理论,而它可以预测并描述我们所看到的充满着令人叹为观止的种种呈现的广漠宇宙。如果这个理论被观察所证实,那它就为3000多年来我们所进行的探索划上了一个完美的句号。我们将会找到那大设计

    因此,我更愿意将这本书作为一本哲学书籍。

    巧的是,我所钟爱的另外一本书《What is the name of this book?》也纯粹从逻辑的角度提出了一个相同的问题:

    我在某本哲学教科书或者别的什么地方读到:“真正的哲学家是这位九岁的女孩。她正看着窗外,突然转头问她母亲:‘妈妈,我感到奇怪的是,怎么就有了这些东西呢?’” 这个问题困扰了众多哲学家。有些哲学家认为这才是哲学最基本的问题。他们用这样的形式来表述:“何以有而非无?”

    如何?这两个问题是一样的吧? 探讨这样的问题,很容易将我们引入到玄妙的境界,我们沉醉其中,并有意的将“人”和自然界分离出来——“人”作为自由意志的一种存在,直觉上很难用物理定律来加以描述:什么样的定律决定了我在一个时刻买这本书?什么时候看完?什么时候写这篇根本不像读书笔记的笔记?

    而所谓的大设计、大统一理论,就要回答这样的问题。也许在我的有生之年,看不到这样的理论的出现。对此,我表示蛋定。

    因为,即使没有这样的一个理论,我所日常生活的世界也已经足够复杂、足够精细、足够我去体验了……

  • 一些佛教和相关词汇的英语翻译

    有些书看完后不能评论的:一个是因为太敏感;一个是因为个人能力有限,无法评论。

    摘录一些词汇,作为知识普及。

    • ahimsa zone: ahimsa是一个佛教词汇,意为“不杀生”,引申为“非暴力”。圣雄甘地在其“非暴力不合作”运动中将这一概念发扬光大。ahimsa zone可以理解为无武装区。
    • Avalokiteshvara: 大慈大悲观世音菩萨
    • Bodhi Tree: 菩提树
    • Buddha: 佛
    • Buddhism: 佛教
    • bodhisattva: 菩萨
    • compassion: 同情。个人认为与“慈悲”等同。
    • Dalai Lama: 达赖喇嘛,西藏喇嘛教格鲁派(黄教)的两大活佛之一,现为第十四世,全名为:Jamphel Ngawang Lobsang Yeshe Tenzin Gyatso,分别的意义是:觉醒的智慧,言语的王子,完美的智慧,崇高的智慧,传道的拥有者,智慧的海洋。
    • Dharamsala: 达兰萨拉,印度城市
    • emptiness: 空
    • impermanence: 无常
    • International Commission of Jurists: 国际法学家委员会
    • Kalachakra Tantra: 时轮大法,个人理解又可以被理解为“灌顶”仪式(Abhisheka)
    • karma: 因果
    • meditation: 冥想
    • monasticism: 隐修,隐修的体制、方式
    • Nagarjuna: 龙树菩萨
    • Panchen Lama: 班禅喇嘛,Gendhum Chokyi Nyima,第十一世
    • prayer: 祷文
    • reincarnation: 再次投胎,特指活佛转世
    • Sangha: 佛的出家弟子的团体——僧伽(sangha)是僧宝。称之为宝,是因为它能够令大众止恶行善、离苦得乐,是极可尊贵的意思。
    • Shantideva: 寂天
    • tulku: 活佛