
4.2 显示一篇新闻文章
接下来,我们要运用MVC的知识,来将刚刚插入的新闻从数据库中读取出来,呈现给视图页面。在开始之前,可以花几分钟时间再回顾一下前面MVC的内容,弄清楚我们要做的事将有以下几个步骤:
① 建立一个新闻页面模型,在模型中创建一个获取数据的getPage方法,将查询条件作为该方法的参数。
② 创建一个新闻页面控制器,在控制器中实例化文章模型后,使用该实例对象的getPage方法获取文章,然后将该文章渲染到视图。
③ 在视图中输出文章。
实现的次序依次是M→C→V,思路很清晰对不对?好,让我们赶快开始吧!
4.2.1 创建文章模型和方法
在终端中进入网站项目的目录(在网站项目的任意位置都可以,但不能在网站项目之外的地方,否则会出错,以后请记住这一点,我不再提醒了),然后运行以下命令:
zf create model page
执行该命令后,结果提示如图4-2所示。

图4-2 使用ZF tool创建page模型
ZF tool将在application/models目录下创建一个名为Page.php的文件(在命令行中,如果你输入模型名称首字母是小写,tool工具会自动将其转为大写)。该文件定义了一个名为Kh_Model_Page的类,其继承自Zend_Db_Table_Abstract。先不管这个Zend_Db_Table_Abstract是什么吧,要弄清楚它可不容易,如果你愿意可以去看官方手册,不过我不建议你现在就这样做,因为你会晕的…………
你是醒着吗?把鼠标扔开,找到键盘,修改该文件代码如下:
application/models/Page.php <?php class Kh_Model_Page extends Zend_Db_Table_Abstract { // 定义模型数据表 protected $_name = 'core_pages'; // 获取页面 public function getPage() { $select = $this->select(); $select->where('star = ?', 4); $row = $this->fetchRow($select); if($row){ return $row; }e lse{ return null; } } }
对刚刚开始接触面向对象编程模式的读者来说,以上这段代码不好理解,让我们多花点笔墨来予以解释。
首先,class是声明类的关键字,我们用class声明了一个名为Kh_Model_Page的类,这个类继承(extends)了Zend_Db_Table_Abstract类,大括号里就是这个类的内容。Zend_Db_Table_Abstract是一个数据表抽象类,当我们在其中定义了名为$_name的受保护的变量后,这个类会自动与默认的数据库(就是我们在配置文件中定义的)建立连接,并对应$_name值的同名数据表,自动为我们去做有关数据表操作的所有事情,通常这被称为对象-关系映射(ORM)。
protected $_name = 'core_pages'声明了这个类对应core_pages这个表,也就是说,core_pages表此刻拥有了一个阿凡达——Kh_Model_Page这个类就是core_pages数据表的化身,杰克的化身在潘朵拉星球具备了许多上天入地的特异功能,我们的core_pages表的化身Kh_Model_Page在Zend Framework框架内,也具备了core_pages表本身不具备的能力,它将敏捷如飞,独当大任。
紧接着我们创建了一个名为getPage的方法(函数),在该方法里,$this指针就是这位无处不在的阿凡达(真觉得$avatar也是个不错的指针名)。它掏出一个搜索器并将其命名为$select($select = $this->select()),然后给$select输入一个条件指令(where('star = ?', 4)),把该搜索器放到自己的一只手里($this->fetchRow($select)),让它引导着手去自己的大肚子里找东西。如果找着了就把这个东西放在$row里扔出去(return $row),如果没找着就不管了,连同自己也消失(return null)。这位阿凡达是不是有点像机器猫哆啦A梦?做事风格有点像。
$select->where('star = ?', 4)使用where()方法构建查询条件语句,在where()方法中,可以使用普通字符串和占位符方式作为查询语句。
在实际编码中,把$select->where('star = ?', 4)这样的条件语句硬编码在模型中大大制约了getPage这个方法的使用范围,假如我在另一个地方要查询star为3的页面,那么怎么办?另外再创建一个getPage2方法?那还不如用原生代码来写。更好的办法是在方法中将查询条件作为变量参数,在控制器中传入。
1.改进后的模型
让我们来对先前的模型进行一点小小的改进,给getPage()方法添加一个可传入的参数,并且在下面构建查询时,先判断这个参数有没有值,如果有的话,将该参数放到查询语句的条件中去。打开application/models/Page.php,将该文件中的代码改成以下的内容:
application/models/Page.php <?php class Kh_Model_Page extends Zend_Db_Table_Abstract { // 定义模型数据表 protected $_name = 'core_pages'; // 获取页面 public function getPage($where = null) { $select = $this->select(); if($where != null){ $select->where('star = ?', $where); } $row = $this->fetchRow($select); if($row){ return $row; } else{ return null; } } }
在创建getPage方法时我们为其预定义了一个参数$where,在控制器中使用这个方法时,就可以根据情况灵活地加入参数,得到想要的结果。在方法中,赋给参数$where的默认值为
null,在使用这个方法时,该参数就可以默认,否则会有出错信息。
4.2.2 创建新闻文章控制器
模型方法创建好之后就可以工作了——记住,这意味着你现在有了一个哆啦A梦,你可以以主人的姿态,随意地控制它使它为你服务啦!通常需要做的就是获得(术语叫实例化)一个模型对象,然后让这个对象发出使用模型方法的指令,然后就等着看结果,如果结果满意,就把它传给一个视图变量,让视图去实现结果的呈现和展示。
先创建新闻文章控制器NewsController。在终端中进入网站项目的目录,运行以下命令:
zf create controller news 1
执行结果如图4-3所示。

图4-3 使用ZF tool创建News Controller控制器
以上代码将创建一个名为NewsController(ZF tool会自动把控制器名称首字母转为大写,再加上Controller作为文件名)的控制器,同时在其中创建了一个名为index的方法和该方法对应的index.phtml视图文件(通过News后面跟着的参数1),该视图文件默认是一句表明自己身份的英文。事实上在创建控制器和方法时,zf命令中参数1是可省略的,即不附加1也会默认创建相应的视图。
建议,我们最好现在再去看一下刚刚创建的控制器文件和视图文件的位置,加深对框架MVC结构的理解和记忆。
进入控制器发号施令吧。打开application/controller/NewsController.php文件,在其中添加如下代码:
application/controller/NewsController.php <?php class NewsController extends Zend_Controller_Action { public function init() { /* Initialize action controller here */ } public function indexAction() { // action body $modelPage = new Kh_Model_Page(); // 实例化模型对象 $star = 4; // 定义查询条件 $newsStar = $modelPage->getPage($star);// 使用模型的getPage方法 // 获取文章 $this->view->newsStar = $newsStar; // 输出到view视图 } }
以上短短的4行代码,彰显了“老板级”话语的特点:言简意赅。就是召唤出哆啦A梦,然后只需要一个手势:4!哆啦A梦看在眼里,心领神会,马上施展招数,从肚子里找到老板想要的东西,放在$nawsStar里。老板随手拿起$nawsStar,交给了一个神秘的戴着面具的对象$this->view->newsStar。
4.2.3 创建新闻频道首页视图
在Web开发中,很多程序员一提到视图,就会说那是美工的事,所以把书翻到这里,他们也许会松一口气,好像会有人来接班似的。但说实话,在Web开发过程中,我从来没等到过美工接班,他们好像老在忙别的事。我认为一个优秀的Web程序员必须敬重美工,但却不能依赖美工,也就是说,美工不到位的时候,他们可以自己完成基本的任务,使网站看上去挺像那么回事。
话说控制器把那篇文章交给了views视图,相当于把颜料给了画家,具体要它变成什么样子的工作,就由视图来完成了。NewsController控制器默认指定的视图文件是/application/views/scripts/news/index.phtml,让我们打开它,把这篇千呼万唤的文章输出来吧!
application/views/scripts/news/index.phtml <?php echo "<h3>".$this->newsStar->title."</h3>"; echo $this->newsStar->body; ?>
这里只是直接输出了文章的标题和内容,化腐朽为神奇的手笔还没有做,以后留给美工们去发挥吧!没美工,前后台就你一个?向达·芬奇学习吧!
这里输出的是$this->newsStar,而不是this->view->newsStar,因为此地是views的地盘,大家见了它,直接喊名字即可,就比如赵云在外见了陌生人要报姓名“吾乃常山赵子龙”,回到城里见了兄弟们,大家都喊他“子龙”就可以了。
图4-4展示了此时的http://kehuan.edu/news页面。

图4-4 News新闻页面
4.2.4 改进模型,让它适应更复杂的查询条件
如果我们想多加一个查询条件,让News的索引页面显示的文章star为4,同时top为1,则原有的getPage方法就不够用了,得加一个参数top进去作为查询条件,让它变成以下的样子:
application/models/page.php public function getPage($where = null, $top = null) { $select = $this->select(); if($star != null){ $select->where('star = ?', $where); } if ($top != null){ $select->where('top = ?', $top); } $row = $this->fetchRow($select); if($row){ return $row; } else{ return null; } }
没错,这么写非常自然,并没有什么不妥之处。去看一下数据库中core_pages的字段,你还能想到几个别的可能也会成为查询条件的字段,尽可以把它们都预定义在这里,作为参数以备使用。但如果有可能成为查询条件的字段有很多,这么做就显得有点笨了,我们需要寻找更优雅的解决方案。看下面的更好的方法:
application/models/page.php public function getPage($where = array()) { $select = $this->select(); if (count($where) > 0){ foreach ($where as $key=>$value){ $select->where($key. ' = ?', $value); } } $row = $this->fetchRow($select); if($row){ return $row; } else{ return null; } }
我们预定义了一个数组$where作为参数来构建多重查询条件,getPage变得更聪明了,会自行根据$where数组来构筑查询语句,这样不管你有多少个查询条件,这个模型方法都可以应付自如,我们在不同的控制器里都可以灵活地使用这个方法来得到想要的某篇文章。
4.2.5 改进NewsController控制器
现在,要想让News的index.phtml页面显示的文章star为4,同时top为1,在控制器里也应把查询条件写成一个数组:
application/controller/NewsController.php public function indexAction() { // action body $modelPage = new Kh_Model_Page(); // 实例化模型对象 $where = array('top'=>1, 'star'=>4); // 定义查询条件 $starNews = $modelPage->getPage($where); // 使用模型的getPage // 方法获取文章 $this->view->starNews = $starNews; // 输出到view视图 }
刷新页面,可看到star为4、top为1的新闻被调用显示出来,如图4-5所示。

图4-5 按条件查询后得到的新News页面