Category: Symfony

  • 任氏有无轩最新改版

    任氏有无轩改版已经很多次了,从最早的Symfony 1.x开始,在Symfony框架进入到2.x的时候重新改版了一次,最近的这次刚刚结束,也算是很大的一次。

    根据我在Bitbucket的提交记录,3月12日开始第一次重新提交,4月22日完成最后一次的提交,历时40余天。但是,众所周知,我的开发是很断续的,平均下来每天投入的时间不会超过1个小时,所以这次改版的总耗时不超过40小时,也就是常规工作时间下一周的工作量——加班的话也就3天左右。

    这次的改版牵涉面非常广。

    (more…)

  • 在Symfony 2中获取WordPress信息

    在我正在进行的任氏有无轩改版中,我希望在首页获得我的博客的一些信息,比如最新发布的五个帖子。

    我使用的博客后台是Wordpress,它提供了很好的编程接口,可以轻松地完成这个任务。而且Wordpress也提供了这些编程接口的说明。

    (more…)

  • Symfony 2中Twig模板的两个小技巧

    今天抓紧时间又更新了一下任氏有无轩的首页的改造。在我已经获得了我的博客的若干最新帖子的前提下,我需要在首页上显示其中的几个。 我要解决两个问题:

    1. 对帖子的内容进行过滤。
    2. 对过于长的帖子内容进行截断。

    (more…)

  • Symfony 2中的批量数据生成

    (原文发布于2014年2月24日,原文链接:Data Fixtures in Symfony 2

    本文收录于[go4pro.org]

    =======

    本文介绍了在Symfony 2中使用Data Fixtures工具进行批量数据生成的方法。特别介绍了牵涉到1-M等主从表之间PK关联的数据的生成方法。

  • SF2中使用createNativeQuery和ResultSetMapping

    任氏有无轩”的后台中有一个统计是得出每天对书籍详情页面的访问数量,结果集很简单:一个日期,一个该天的访问次数。

    在原生SQL下,这个并不复杂,如下的语句就可以完成: select count(v.bid) vc, date(from_unixtime(v.visitwhen+15*60*60)) vd from book_visit v group by vd order by vd

    但是这个语句稍作变动(将book_visit改为FQN的引用)在SF2中用常规的createQuery将不能执行,提示date函数不被支持。

    解决这个问题有两种方法,一种是quick-and-dirty的纯原生SQL:

    $q = $em->getConnection()->prepare('select count(v.bid) vc, date(from_unixtime(v.visitwhen+8*60*60)) vd from book_visit v group by vd order by vd');
    $q->execute();
    return $q->fetchAll();

    这个方法简单明了,适合初学者使用。

    作为SF2和Doctrine的推荐方法,我们要使用所谓的ResultSetMapping和createNativeQuery。

    我们可以这样简单的理解,ResultSetpMapping提供了一种映射机制,将原生SQL返回的字段映射到结果集的域,从而使得Doctrine可以以此构造返回的结果集,然后执行SQL语句,并将结果返回。

    应该说,即使你不使用createNativeQuery,而只是用createQuery,Doctrine也还是会隐含的做一个ResultSetMapping的,只是不用显式调用罢了。

    代码如下:

    public function getVisitCountByDay()
    {
    $em = $this->getEntityManager();
    $rsm=new DoctrineORMQueryResultSetMapping;
    $rsm->addScalarResult('vc', 'vc');
    $rsm->addScalarResult('vd', 'vd');
    $q=$em->createNativeQuery('select count(v.bid) vc, date(from_unixtime(v.visitwhen+15*60*60)) vd from book_visit v group by vd order by vd', $rsm);
    $res=$q->getResult();
    return $res;
    }

    这个调用中,最关键的其实是两个addScalarResultvc是标量结果很自然,但是vd为什么是标量结果?Doctrine的文档对此语焉不详。

    我的理解是,一旦结果集中包含了一个标量结果(vc),那么所有其它结果也自然要变为标量结果。STOP。

    本文收录于[go4pro.org]

  • 在Symfony 2中使用原生SQL

    在任氏有无轩站点中,有一个页面是用来给我自己看书籍详情页面的访问记录的,这个页面中需要做一些统计,比如:最近访问的是哪些书籍?访问量最大的是哪些?每天的访问量有多少?

    前两个功能都很容易实现,我也以为第三个功能也很容易实现。

    (more…)

  • 任氏有无轩一些小的改动

    这几天一直在“小打小闹”的改进我的任氏有无轩。

    今天完成了一个小功能:

    在书籍详细页面显示该书被访问几次以及上次访问的时间。

    由于我对处理文本文件(必然会牵涉到的正则)不是很熟,所以还是用数据库来完成。

    遇到的一个意想不到的问题是如何写日期/时间类型。

    最早我将数据类型定义为datetime,但是在写入时如果用string变量,出现:

    Fatal error: Call to a member function format() on a non-object in G:wwwrsywx2SymfonyvendordoctrinedballibDoctrineDBALTypesDateTimeType.php on line 44

    的错误。

    如果用Datetime变量,出现:

    Can't convert object Datetime to string

    错误。 最后我是用一个bigint来直接存储时间戳的方式解决问题……这个世界清静了……

    效果如下:

  • 任氏有无轩改版彻底完成

    折腾了很久,终于将我的任氏有无轩改版到4.0。

    这次的改版经历了很长时间。

    首先是用Symfony 2.X,这对我是个挑战,因为我之前都是基于Symfony 1.4.X来编写站点的。Symfony的一个最大的特点就是每次大的版本升级都会引入全新的东西,需要开发者去适应,这是闹哪套!

    Symfony 2首先是基于namespace,因此必须有PHP 5.3+的版本支持。我的主机在BlueHost上,应该说BH是很保守的虚拟主机服务商,在外界已经普及使用PHP 5.3的时候,它还死撑着用PHP 5.2。我还为此专门去信询问,回复是他们还要测试。没有这个最基本的平台,我的开发也就没有了动力。

    幸好,BH主机从善如流,一口气开始支持PHP 5.3/5.4,所以我才又重拾开发。

    其次是版面的设计。这要感谢前令狐,在7月份的第一届G4PCC上,他介绍了Bootstrap这个框架。这个框架的存在让我精神一振,因为它可以让一个程序员(而不是美工师)设计出比较优雅、简介的页面。通过简单的摸索,我就已经上手了。

    (more…)

  • 重构“任氏有无轩”——第四天

    好吧,我忏悔:今天的内容很少,根本不是一天的内容。但是,考虑到今天是周末啊…… 今天我只是开始做一些分页构件的准备工作。

    ========

    分页当然很重要。但是在Symfony 1.4之前,其提供的Paginator构件其实并不很恰当:它融合了相关数据集返回、构造实际的导航条这两个本来一个属于M,一个属于V的工作。按照Quick-and-Dirty的原则来说,这样做也无可厚非,只是会破坏Synfony一直推崇的MVC分离的原则。

    所以,回头一想,如果开发者要自己开发一个Paginator就会很简单:获得相关的数据集由M去完成。这个步骤中的一个关键是获得符合筛选条件的总记录数,我们当然不能傻乎乎的将所有记录返回然后count一遍,应该在数据库服务器端就完成这个统计。由于今天我们不是做M端,也不是做V端——啊?那我们今天讲虾米?!——所以我会另开一文。

    今天只是准备工作:测试。

    按照TDD的思路,在实现一个功能之前,就先描述好这个功能应该获得什么输入,得到什么输出是非常好的习惯。

    在分页模块中,有一个功能是必须实现的,即:已知总记录数(n),每页显示记录数(rpp),需要知道总共要多少页(p)。 一般的算法就是p=ceil(n/rpp)。但是这样的算法对不对?比如,如果总记录数正好是每页记录数的整数倍,那会不会出问题? 对于这种验证,用单元测试就会很方便。 首先编写Paginator类,其中有一个函数是getTotalPages

    // srctrrsywxBundleUtilityPaginator.php
    namespace trrsywxBundleUtility;
    class Paginator
    {
        private $page;
        private $total;
        private $style;
        public function __construct()
        {
            $this->page = 1;
        }
        /*
         * var recCount: the total count of records
         * var $rpp: the record per page
         */
        public function getTotalPages($recCount, $rpp)
        {
            if ($rpp == 0)
            {
                $rpp = 20; //This is forced in this. Need to get parameter from configuration
            }
            $r = ceil($recCount / $rpp);
            return $r;
        }
    }

    然后创建对应的测试单元软件:

    // srctrrsywxBundleTestsUtilityPaginatorTest.php
    namespace trrsywxBundleTestsUtility;
    use trrsywxBundleUtilityPaginator;
    class PaginatorTest extends PHPUnit_Framework_TestCase
    {
        public function testGetTotalPage()
        {
            $paginator = new Paginator;
            $r = $paginator->getTotalPages(1, 0);
            $this->assertEquals(1, $r);
            $r = $paginator->getTotalPages(10, 20);
            $this->assertEquals(1, $r);
            $r = $paginator->getTotalPages(20, 20);
            $this->assertEquals(1, $r);
            $r = $paginator->getTotalPages(21, 20);
            $this->assertEquals(2, $r);
            $r = $paginator->getTotalPages(100, 20);
            $this->assertEquals(5, $r);
            $r = $paginator->getTotalPages(110, 20);
            $this->assertEquals(6, $r);
            $r = $paginator->getTotalPages(110, 10);
            $this->assertEquals(11, $r);
        }
    }

    在命令行中,运行如下命令:

    phpunit -c app srctrrsywxBundleTestsUtility

    如果一切顺利,那么就会得到:OK (1 test, 7 assertions)这样的提示。否则,就一定是某个用例下出了问题,需要针对这样的问题进行处理。

    【本文收录于[go4pro.org]