jobeet第六天:更多的数据模型

*这一系列文章来源于Fabien Potencier,基于Symfony1.4编写的Jobeet Tutirual

Doctrine查询对象

在第二天的内容中我们定义了这样一个需求(requirements):“在Job首页显示最近发布的和在有效期内的Job信息列表”。我们现在在首页中显示的是数据库中全部的Job数据,而没有考虑到不需要在首页中显示已过期的Job信息。

一个在有效期限内的Job数据就代表着这个Job信息发布的日期在30天之内。$entities = $em->getRepository('IbwJobeetBundle')->findAll()这行代码是从数据库中取出所有的Job数据,因为我们并没有给查询指定任何的条件。

现在我们来做些修改,我们希望取出的是在有效期内的Job数据:

 

调试Doctrine生成的SQL

有时候查看Doctrine生成出来的SQL对我们调试Bug是很有帮助的。比如,我们可能在使用Doctrine进行数据库查询时得不到我们所期望的结果时,那么我们就很需要去查看Doctrine生成的SQL,并以此来对我们的代码进行排错。在开发环境(development)下,还好有Symfony的调试工具栏(浏览器页面的最下面的那条栏),我们能在调试栏中找到大量我们在调试过程中需要用到的信息,包括刚才所说的SQL调试(http://jobeet.local/app_dev.php)。

06-01

序列化对象

尽管上面的代码可以运行了,但这离完美还差很远呢,因为我们还没有考虑到另一个需求:“一个用户能够重新激活或者延续Job信息的期限多30天”。

在上面的代码中我们只依赖了created_at的值来取出不过期的Job数据,由于created_at代表的是Job的创建时间,如果我们还需要表示Job数据的过期时间,我们还要另外一些列(columns)。

如果你还记得我们在第三天的内容中描述的数据表结构的话,你肯定会记起我们还定义了一个expires_at列。由于我们还没在Fixture文件中设置expires_at的值,所以它们的值都是空的。我们需要的是,当一个Job被创建的时候,expires_at的值就会被设置成created_at值的30天之后。

每当我们需要在Doctrine对象被序列化到数据库中之前能够自动执行一些操作的时候,我们可以添加一个新的行为(action)到ORM映射文件的lifecycle callback区块中,就像我们之前对created_at列进行的操作:

现在我们需要重新生成实体类(entity class),这样Doctrine就会在Job实体类中添加一个setExpiresAtValue()函数:

打开src/Ibw/JobeetBundle/Entity/Job.php文件,我们来编辑setExpiresAtValue()函数:

好,现在让我们来用expires_at列来替换created_at列来取出未过期的Job数据:

 

加入更多的Fixtures

现在我们刷新浏览器中的Job首页,我们不会看到页面有任何的改变,因为我们之前加入到数据库中的Job数据都才仅仅发布了几天,所以现在数据库中的Job数据都是在有效期内的。让我们在Fixture文件中加入一些已过期的(expired)的Job数据吧:

现在我们来重新加载Fixtures,然后刷新浏览器,确保过期的Job信息不会被显示出来:

 

重构代码

尽管上面的代码已经能运行了,但我们所做得还是不够好,你能发现其中有什么问题吗?

Doctrine的查询代码不应该属于actionController层),它应该属于Model层。在MVC模式中,Model层定义的是业务逻辑,而Controller层则通过是Model层来从数据库中取出数据。现在我们来把之前从数据库中获取Job数据集合的代码从Controller层移到Model层吧。现在我们为Job实体类创建一个Repository类。

打开/src/Ibw/JobeetBundle/Resources/config/doctrine/Job.orm.yml,添加下面的代码:

运行下面的命令,Doctrine能够帮我们生成Repository类:

下一步我们来给JobRepository类添加一个方法:getActiveJobs()。这个方法可以取出有效期内的Job数据,并且按照expires_at的值进行排序(它还可以接受一个$category_id参数,它可以按分类来取出Job数据)。

现在action里面就可以使用刚才添加的getActiveJobs()方法了。

上面重构后的代码比起之前的未重构代码有如下优点:

  • 获取有效期内的Job数据的代码现在位于Model层中
  • JobController::indexAction()中的代码更少了,可读性也提高了
  • getActiveJobs()方法可以被重用
  • 更加容易地对model的代码进行测试

首页中的Categories

根据我们第二天内容中的需求,Job信息能够按照分类进行显示。直到现在我们都还没考虑到对Job信息进行分类显示。按照第二天内容中的需求,我们需要在首页中按照不同的分类来显示Job信息。首先,我们需要获得包含有未过期的Job数据的所有分类。

Category实体创建一个Repository类:

生成Repository类:

打开src/Ibw/JobeetBundle/Repository/CategoryRepository.php文件,添加getWithJobs()方法:

同时我们需要修改indexAction()方法:

我们在上面的代码中可以看到Category有个setActiveJobs()方法,那么现在我们来修改这个方法:

在模板中,我们需要通过迭代变量categories的值来显示所有的Job数据:

 

限制结果行数

我们现在需要限制Job信息列表中显示的行数为10行。实现这个功能非常简单,我们来给JobRepository::getActiveJobs()方法添加一个$max参数:

修改indexAction()方法中的代码,我们需要使用$max参数来调用getActiveJobs()方法:

 

自定义配置

JobController::indexAction()方法中,我们对返回Job数据的行数($max = 10)进行了硬编码(hardcode),我们需要让返回行数的值是可配置的。在Symfony中,我们可以在app/config/config.yml文件中的parameters区块中自定义一些配置(如果parameters区块不存在的话,那么我们也可以自行创建它)。

定义好后我们就可以在Controller中访问它们的值了:

 

动态Fixtures

对于上面所做的修改,我们还不能在页面中看到有什么变化,因为现在我们的数据库中只有很少量的Job数据。现在我们需要在Fixture中批量添加Job数据。我们可选择手动复制之前已存在的代码来重复进行生成Job数据,但我们有更好的办法。我们需要引起注意的是,重复的代码给人的感觉就是很差,甚至在Fixture文件中出现重复的代码:

现在用doctrine:fixtures:load命令来重新加载Fixture,观察Programming分类下的Job信息行数是否为10行:

06-02

过期的Job页面

如果一个Job不在有效期限内了,那么它将是不再可能被用户访问到的,即使用户知道它所在的URL也不行。我们可以尝试着访问过期的Job信息的页面(获得过期的Job信息的id的方法:select id, token from job where expires_at < NOW(),然后把获得的id替换下面URLID的值):

每当用户访问过期的Job信息页面时,我们应该让用户重定向到404页面。现在我们来给JobRepository添加一个方法:

 如果没有结果被返回,那么getSingleResult()方法会抛出Doctrine\ORM\NoResultException异常;如果返回的结果不止一个,那么getSingleResult()方法会抛出Doctrine\ORM\NonUniqueResultException异常。如果你使用getSingleResult()方法,那么请用try…catch语句包含它,以至于确保所返回的结果集只有一行数据。

现在修改JobController::showAction()方法:

现在我们去访问一个已过期的Job信息页面,我们会被重定向到404页面

06-03

好了,我们今天就到这吧。我们明天会开始实现Category页面。

 

原文链接:http://www.intelligentbee.com/blog/2013/08/12/symfony2-jobeet-day-6-more-with-the-model/

 

发表评论