国内最全IT社区平台 联系我们 | 收藏本站
阿里云优惠2流量王
您当前位置:首页 > 数据库 > access > symfony24天教程之第4天 重构

symfony24天教程之第4天 重构

来源:程序员人生   发布时间:2014-09-28 07:49:33 阅读次数:2498次

 symfony回顾

在第3天,我们看到了一个MVC架构的所有层,并修改它们用来在页面上正确地表示question的列表。应用变得好看了些但是还是有点缺乏内容。第四天的目标是表示一个question的answer,给question的详细页面一个更友好的URL,加入一个客户类(custom class),路由政策,以及重构。你可能觉得现在就重写前几天的代码有点太早了,但是相信完成了今天的教程之后你的想法会有所改变。

要阅读这个教程,你应该熟悉以下的章节 MVC在symfony中的实现 .还有如果你懂一点 敏捷开发 也会有所帮助。

表示一个quesiton的answer

首先,让我们继续调整第2天Question CRUD生成的模板。quesiton/show 动作专注于表示一个quesiton的详细信息,并假设收到的参数是你传给它的id。我们可以试试看,调用下面的URL(你可能要把最后的 2 改成你的数据表里的某个id)

http://askeet/frontend_dev.php/question/show/id/2

你可能已经开到过这个页面了。这就是我们将要修改的页面,我们要加上一个question的answer.

动作速览

首先,让我们看看show动作,它在下面这个文件。

askeet/apps/frontend/modules/question/actions/actions.class.php

public function executeShow()
 {
   
$this->question = QuestionPeer::retrieveByPk($this->getRequestParameter('id'));
   
$this->forward404Unless($this->question);
 }

如果你熟悉Rropel,你知道这是一个简单的对Question数据表的请求。它的目的是用id参数作为唯一主键取得一条唯一的记录。在上面这个例子里,id参数的值是2,所以QuestionPeer类的->retrieveByPk()方法会返回一个Question类的对象,它的主键为2。如果你不熟悉Propel,那么请先看看 某些文档,然后再回来继续。

这个请求的结果被通过$question变量交给 showSuccess.php模板,

sfAction对象的->getRequestParameter('id‘)方法取得请求中的叫做'id'的参数,而不论它是通过GET还是POST模式传来的。例如,如果你请求

http://askeet/frontend_dev.php/question/show/id/1/myparam/myvalue
那么show动作将用 $this->getRequestParameter('myparam').来取得myvalue.

注意:forward404Unless()方法如果在数据库找不到要找到question它就会发送给浏览器一个404页面。总是处理执行中的边界值和错误是一个好习惯,symfony给出了一个简单的方法来让这件事情更容易。

修改showSuccess.php模板

生成的showSuccess.php模板不能满足我们的要求,所以我们要全面重写它。打开 frontend/modules/question/templates/showSuccess.php 用以下的内容替换掉。

 

<?php use_helper('Date'?>
 
<div class="interested_block">
  
<div class="interested_mark">
    
<?php echo count($question->getInterests()) ?>
  
</div>
</div>
 
<h2><?php echo $question->getTitle() ?></h2>
 
<div class="question_body">
  
<?php echo $question->getBody() ?>
</div>
 
<div id="answers">
<?php foreach ($question->getAnswers() as $answer): ?>
  
<div class="answer">
    posted by 
<?php echo $answer->getUser()->getFirstName().' '.$answer->getUser()->getLastName() ?> 
    on 
<?php echo format_date($answer->getCreatedAt(), 'p'?>
    
<div>
      
<?php echo $answer->getBody() ?>
    
</div>
  
</div>
<?php endforeach?>
</div>

你看到interested_block div在昨天已经加到listSuccess.php模板了。它表示对一个question感兴趣的用户数。还有,现在它非常象一个list,除了在title没有link_to.它只是对初始代码的一次改写,以表示一个question的需要表示的信息。

新的部分是answers  DIV,它表示对这个question的所有answer(用 $question->getAnswers() Propel方法),除了body还表示每个answer的Relevancy(关联),作者的名字,创建日期。

format_date()是另一个需要初始声明的模板助手的例子,你可以在 symfony宝典的 助手的初始化 章节找到更多关于助手的语法。(这些助手帮助我们能更快地完成单调的日期格式化表示的任务)

注意:Propel为关联表创建的方法的名字是在表名后加一个's',虽然->getRelevancys() 这样的方法名有点不好看,但是可以帮你节约号几行SQL代码。

加入一些新的测试数据

现在是时候在 data/fixtures/test_data.yml 给answer和relevancy数据表加些数据了。(你完全可以加些自己的数据)

 

Answer:
  a1_q1:
    question_id: q1
    user_id:     francois
    body:        |
      You can try to read her poetry. Chicks love that kind of things.

  a2_q1:
    question_id: q1
    user_id:     fabien
    body:        |
      Don't bring her to a donuts shop. Ever. Girls don't like to be
      seen eating with their fingers - although it's nice. 

  a3_q2:
    question_id: q2
    user_id:     fabien
    body:        |
      The answer is in the question: buy her a step
, so she can 
      get some exercise and be grateful for the weight she will
      lose.

  a4_q3:
    question_id: q3
    user_id:     fabien
    body:        |
      Build it with symfony - and people will love it.

用下面的命令行重新载入你的数据,

$ php batch/load_data.php

如果以前修改成功,下面的URL将将表示你的第一个question

http://askeet/frontend_dev.php/question/show/id/XX
注意:把XX改成你的第一个question的id

现在 question表示得更好看了,后面还跟着它的answer.不错吧。

修改模块 第1部分

几乎可以肯定表示一个作者的全名在这个应用的其他地方也会用到。你也可以把全名考虑成一个User对象的属性,这意味着User模块应该有一个方法可以用来取得全名,而不是在动作中每次重写。打开 askeet/lib/model/User.php 加入以下的方法

public function __toString()
{
  
return $this->getFirstName().' '.$this->getLastName();
}

为什么这个方法叫 __toString() 而不叫 getFullName()或其它类似的名字呢?因为__toString() 方法在PHP5中是一个对象描述为字符串时的缺省方法。这意味着你可以把askeet/apps/frontend/modules/question/templates/showSuccess.php 文件里的下面的代码改写成 更简单的形式

posted by <?php echo $answer->getUser()->getFirstName().' '.$answer->getUser()->getLastName() ?> 
改为
posted by <?php echo $answer->getUser() ?> 

代码简洁吧.

不要重复你自己

敏捷开发的一个好的原则就是避免重复代码,被称为 Don't Repeat Yourself (D.R.Y)。这是因为重复的代码在检查,修改,测试和更新的时候都要比一段封装后的代码多花更多的时间。这还让应用的维护变得复杂。如果你去看看昨天的教程,你会注意到昨天写的listSuccess.php模板和ShowSuccess.php有一些重复的代码。

<div class="interested_block">
  
<div class="interested_mark">
    
<?php echo count($question->getInterests()) ?>
  
</div>
</div>
 

所以我们重构的第一个任务就是去掉这两个模板里的重复代码,把他们放到一个片段(fragment),或成为可重用代码块里。在askeet/apps/frontend/modules/question/template/ 里创建一个_interested_user.php文件,代码如下。

<div class="interested_mark">
  
<?php echo count($question->getInterests()) ?>
</div>

然后,把两个模板(listSuccess.phpshowSuccess.php)里的代码置换成

<div class="interested_block">
  
<?php include_partial('interested_user', array('question' => $question)) ?>
</div>

一个片段没有任何对现在对象的本地访问,这个片段用了一个 $question变量,所以它必须在对 include_partial 的调用里定义。片段文件的前缀_可以帮助我们把它和template/目录下的真正的模板文件区分开来。如果你想学到更多关于片段的知识,可以读一读 symfony宝典的 显示 章节

修改模块 第2部分

新的片段里的$question->getInterests()调用请求数据库并返回一个Interest类的对象的数组。对于只需要感兴趣的用户的数字来说这是一个消耗太大的请求,它可能给数据库带来不必要的负载。记住,这个调用还在listSuccess.php里调用,是在一个循环里。看来最好优化一下它。

一个好的方法是给Question数据表加入一个叫做interested_users的列,然后在每次有关于这个question的interest创建的时候去更新这个列。

重要:我们将要更改一个模块,但是没有明显的方法去测试它,因为现在没有办法可以去通过askeet给Interest数据表加记录。如果你没法测试,你就不应该做任何修改。幸运的是,我们有一个办法可以测试这个修改,我们会在本章的后面发现它。

给User对象模块加入一个字段

大胆地修改the askeet/config/schema.xml 数据模块,给ask_question数据表加一个字段。

<column name="interested_users" type="integer" default="0" />

然后重建模块

$ symfony propel-build-model

对,我们重建模块而不用担心对现有模块的扩展,因为对User类的扩展是在 askeet/lib/model/User.php里的,它继承自Propel 生成的类askeet/lib/model/om/BaseUser.php。这就是你为什么不应该编辑 askeet/lib/model/om/ 目录下的代码:它们在每次调用build-model的时候都被重建。Symfony帮助我们更容易地在Web项目的早期修改模块。

你好需要更新真实的数据库。为了避免写SQL语句,你要重建你的SQL schema并重新载入测试数据

$ symfony propel-build-sql
$ mysql -u youruser -p askeet < data/sql/lib.model.schema.sql
$ php batch/load_data.php

注意:TIMTOWTDI(另一种方法,There is more then one way to do it):除了重建数据库,你可以手工给MySQL数据表加入新的列。

$ mysql -u youruser -p askeet -e "alter table ask_question add interested_users int default '0'"

修改Interest对象的保存方法

更新这个新字段的动作必须在每次一个user对一个question感兴趣的时候都做一次,也就是说,每次一条新的纪录加入到Interest数据表的时候都要做一次。你可以通过在MySQL里做一个触发器来实现它,但是这是一个数据库依存的解决方案,你就不能很容易地迁移到另一个数据库了。

最好的解决方案是重载Interest类的save()方法,这个方法每次Interest的对象被创建的时候都会被调用。所以,打开 askeet/lib/model/Interest.php ,写如下的代码


 

public function save($con = null)
{  
    
$ret = parent::save($con);
 
    
// update interested_users in question table
    $question = $this->getQuestion();
    
$interested_users = $question->getInterestedUsers();
    
$question->setInterestedUsers($interested_users + 1);
    
$question->save( 生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠
程序员人生
------分隔线----------------------------
分享到:
------分隔线----------------------------
关闭
程序员人生