`
isiqi
  • 浏览: 16354578 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

第七天 模型与视图操作

阅读更多
Symfony回顾

现在我们的学习已经过去六天了,也许我们其中的一些人会认为到现在为止程序并不是十分的有用。这是因为一些人是通过可用的页面数量来评价一个程序是否有用的,而他们认为askeet只是显示一个问题列表,显示相关的答案以及处理用户会话。

我们并没有添加大量页面的原因是因为使用Symfony添加页面实在是太容易了。我们需要证明?好的。今天我们将会显示一个最后提问的问题列表,一个最后发表的答案列表,一个对某一个问题感兴趣的用户列表,用户的配置,并且我们会在每一个页面上添加一个浏览栏来访问这些特性。因为这些工作并不够一小时,我们同时会进行视图配置,并且会最终查看一下我们这周所完成的工作。准备好了?让我们开始吧。

重构

现在我们要使用一个与question/templates/_list.php相类似的页码控件添加一个页码列表。我们并不希望重复自己,所以我们要将这些页码代码放在一个自定义的帮助器中。帮助器是模板可以访问的一个PHP函数(就如同link_to()与format_date()帮助器)。

在askeet/apps/frontend/lib/helper中个GlobalHelper.php文件,并且添加下面的代码:

<?php

function pager_navigation($pager, $uri)
{
$navigation = '';

if ($pager->haveToPaginate())
{
$uri .= (preg_match('/\?/', $uri) ? '&' : '?').'page=';

// First and previous page
if ($pager->getPage() != 1)
{
$navigation .= link_to(image_tag('first.gif', 'align=absmiddle'), $uri.'1');
$navigation .= link_to(image_tag('previous.gif', 'align=absmiddle'), $uri.$pager->getPreviousPage()).'&nbsp;';
}

// Pages one by one
$links = array();
foreach ($pager->getLinks() as $page)
{
$links[] = link_to_unless($page == $pager->getPage(), $page, $uri.$page);
}
$navigation .= join('&nbsp;&nbsp;', $links);

// Next and last page
if ($pager->getPage() != $pager->getCurrentMaxLink())
{
$navigation .= '&nbsp;'.link_to(image_tag('next.gif', 'align=absmiddle'), $uri.$pager->getNextPage());
$navigation .= link_to(image_tag('last.gif', 'align=absmiddle'), $uri.$pager->getLastPage());
}

}

return $navigation;
}

这个页面浏览帮助器改进了我们前面所编写的代码:他可以使用任意的路由规则,不会为第一个页面显示'previous'链接,同样也不会为最后一个页面显示'next'链接。我们同时添加了四个图片(first.gif,previous.gif,next.gif,last.gif)来使链接显示更漂亮。我们可以在以后的工程中重用这个帮助器。

要在question/templates/_list.php片段中使用这个帮助器,调用方法如下:

<?php use_helper('Text', 'Global') ?>

<?php foreach($question_pager->getResults() as $question): ?>
<div class="question">
<div class="interested_block">
<?php include_partial('interested_user', array('question' => $question)) ?>
</div>

<h2><?php echo link_to($question->getTitle(), 'question/show?stripped_title='.$question->getStrippedTitle()) ?></h2>

<div class="question_body">
<?php echo truncate_text($question->getBody(), 200) ?>
</div>
</div>
<?php endforeach; ?>

<div id="question_pager">
<?php echo pager_navigation($question_pager, 'question/list') ?>
</div>

名字Global指向我们刚刚创建的GlobalHelper.php文件。

测试我们的工作是否正常:
http://askeet/frontend_dev.php/

最近问题列表

在question模块中,创建下面的动作:

public function executeRecent()
{
$this->question_pager = QuestionPeer::getRecentPager($this->getRequestParameter('page', 1));
}

这就如同以前一样的简单。我们认为获取最近问题的能力应是QuestionPeer类的一个方法。Peer类专注于返回指定类的对象列表,这在Symfony一书的模型一节进行详细的解释。但是getRecent()类方法还需要我们来创建。打开askeet/lib/model/QuestionPeer.php 类添加下面的代码:

public static function getRecentPager($page)
{
$pager = new sfPropelPager('Question', sfConfig::get('app_pager_homepage_max'));
$c = new Criteria();
$c->addDescendingOrderByColumn(self::CREATED_AT);
$pager->setCriteria($c);
$pager->setPage($page);
$pager->setPeerMethod('doSelectJoinUser');
$pager->init();

return $pager;
}

依据问题创建日期降序排列将会选择最近的问题。这个方法使用self而不是parent,是因为这是一个类方法,而不是一个对象方法。在这里我们使用doSelectJoinUser()方法,而不是简单的doSelect()方法,这是因为我们知道模板需要知道问题作者的详细信息。这就意味着首先请求问题列表,然后对于每一个问题请求得到相关的用户。当我们调用:
$question->getUser();
doSelectJoinUser()方法会在一个请求中完成所有的工作。

这里并没有请求发往数据库。joinUser允许我们将请求数量由1+问题数量减少到1。数据库会庆幸这样简单的优化。

Propel文档会为这个特性提供详细的解释。

最近问题列表模板看起来与在主页显示的问题列表十分相似。用下面的代码创建askeet/apps/frontend/module/question/templates/recentSuccess.php文件:

<h1>recent questions</h1>

<?php include_partial('list', array('question_pager' => $question_pager)) ?>

现在我们就可以理解为什么在第五天的学习中将问题列表重构到一个代码片段中。最后,我们需要在frontend/config/routing.yml文件中添加recent_question规则:

recent_questions:
url: /question/recent/:page
param: { module: question, action: recent, page: 1 }

但是需要求等一下:question/_list代码片段使用question/list路由规则来创建链接,所以他并不适用于最近问题列表。我们需要将路由规则作为一个参数传递到代码片段中,从而他可以为多个页面所重用。所以将recentSuccess.php的最后一行代码改为:

<?php include_partial('list', array('question_pager' => $question_pager, 'rule' => 'question/recent')) ?>

并且将_list.php代码片段的最后一行改为:

<div id="question_pager">
<?php echo pager_navigation($question_pager, $rule) ?>
</div>

不要忘记也要在modules/question/templates/listSuccess.php中添加路由参数:

<h1>popular questions</h1>

<?php echo include_partial('list', array('question_pager' => $question_pager, 'rule' => 'question/list')) ?>

要显示最近的问题列表,我们可以在我们的浏览器地址栏中输入:
http://askeet/question/recent

最近答案列表

这与上面的相类似,所以我们就可以直接操作:

创建answer模块

$ symfony init-module frontend answer

创建recent动作:

public function executeRecent()
{
$this->answer_pager = AnswerPeer::getRecentPager($this->getRequestParameter('page', 1));
}

扩展AnswerPeer类:

public static function getRecentPager($page)
{
$pager = new sfPropelPager('Answer', sfConfig::get('app_pager_homepage_max'));
$c = new Criteria();
$c->addDescendingOrderByColumn(self::CREATED_AT);
$pager->setCriteria($c);
$pager->setPage($page);
$pager->setPeerMethod('doSelectJoinUser');
$pager->init();

return $pager;
}

创建新的recentSuccess.php模板:

<?php use_helper('Date', 'Global') ?>

<h1>recent answers</h1>

<div id="answers">
<?php foreach ($answer_pager->getResults() as $answer): ?>
<div class="answer">
<h2><?php echo link_to($answer->getQuestion()->getTitle(), 'question/show?stripped_title='.$answer->getQuestion()->getStrippedTitle()) ?></h2>
<?php echo count($answer->getRelevancys()) ?> points
posted by <?php echo link_to($answer->getUser(), 'user/show?id='.$answer->getUser()->getId()) ?>
on <?php echo format_date($answer->getCreatedAt(), 'p') ?>
<div>
<?php echo $answer->getBody() ?>
</div>
</div>
<?php endforeach ?>
</div>

<div id="question_pager">
<?php echo pager_navigation($answer_pager, 'answer/recent') ?>
</div>

在浏览器中测试:
http://askeet/answer/recent

现在我们已经习惯其用法了,不是吗?

用户配置

答案中的用户名字将会链接到我们所编写的user/show动作。这将是用户配置,而且他会显示最新的问题与答案,以及用户的详细信息。

所要做的第一件事就是要创建动作:

public function executeShow()
{
$this->subscriber = UserPeer::retrieveByPk($this->getRequestParameter('id', $this->getUser()->getSubscriberId()));
$this->forward404Unless($this->subscriber);

$this->interests = $this->subscriber->getInterestsJoinQuestion();
$this->answers = $this->subscriber->getAnswersJoinQuestion();
$this->questions = $this->subscriber->getQuestions();
}

->getInterestsJoinQuestion()与->getAnswersJoinQuestion()是User类的本地方法。我们可以查看askeet/lib/model/om/BaseUser.php类来查看其工作原理。

askeet/apps/frontend/modules/user/templates/showSuccess.php模块对我们来说应不是问题:

<h1><?php echo $subscriber ?>'s profile</h1>

<h2>Interests</h2>

<ul>
<?php foreach ($interests as $interest): $question = $interest->getQuestion() ?>
<li><?php echo link_to($question->getTitle(), 'question/show?stripped_title='.$question->getStrippedTitle()) ?></li>
<?php endforeach; ?>
</ul>

<h2>Contributions</h2>

<ul>
<?php foreach ($answers as $answer): $question = $answer->getQuestion() ?>
<li>
<?php echo link_to($question->getTitle(), 'question/show?stripped_title='.$question->getStrippedTitle()) ?><br />
<?php echo $answer->getBody() ?>
</li>
<?php endforeach; ?>
</ul>

<h2>Questions</h2>

<ul>
<?php foreach ($questions as $question): ?>
<li><?php echo link_to($question->getTitle(), 'question/show?stripped_title='.$question->getStrippedTitle()) ?></li>
<?php endforeach; ?>
</ul>

当然,我们希望可以限制由User对象的->getInterestsJoinQuestion(),->getAnswersJoinQuestion(),getQuestion()方法所返回的结果数量,以及排序方式。这可以通过覆写askeet/lib/model/User.php类文件中的相应方法来简单的做到,而我们在这里并讨论如何来做,但是今天的发布版本会包含相关的内容。

现在我们可以进行最终的测试了。让我们来看一下第一个用户所做的事情:

http://askeet/user/show/id/1

现在我们也可以由一个问题链接到一个用户配置。在question/templates/showSuccess.php以及question_body div开始处的question/templates/_list.php中添加下面的代码行:

<div>asked by <?php echo link_to($question->getUser(), 'user/show?id='.$question->getUser()->getId()) ?> on <?php echo format_date($question->getCreatedAt(), 'f') ?></div>

不要忘记在_list.php中声明Date帮助器。

添加浏览工具栏

我们将会改变全局的布局来添加一个侧边栏。这个侧边栏将会包含动态的内容,但是我们希望布局中可以设置其位置,他并不是每一个模板的一部分。另外,将侧边栏的代码放在模板中就意味着大量的重复,而正如我们所知的,我们并不会那样来做。

这就是为什么这个侧边栏是一个元素的原因。一个元素是一个动作的结果(例如,由模板执行所产生的HTML代码)。Symfony一书的视图一章解释了元素是什么,以及一个元素与一个片段的区别。

在布局中添加元素

打开全局布局(askeet/apps/frontend/templates/layout.php)。我们还记得这段代码吗?

<div id="content_bar">
<!-- Nothing for the moment -->
<div class="verticalalign"></div>
</div>

将其替换为下面的元素:

<?php include_component_slot('sidebar') ?>

正是如此。

定义进入元素的动作

我们决定使用比一个简单的元素更为强大的东西:元素槽。他是一个元素,但是其动作可以由通过调用者的动作进行修改,允许上下文内容。其视图配置(view.yml)定义了与一个元素槽相对应的动作:

default:
components:
sidebar: [sidebar, default]

在这个例子中,名为sidebar的元素槽声明为sidebar模块的默认动作的结果。

视图配置可以为整个程序进行定义(在askeet/apps/frontend/config目录下)或者是为特定的模块进行定义(在askeet/frontend/modules/mymodule/config/目录下)。对于我们的情况来说,我们会为整个程序进行定义,并且在必须的时候进行覆写来在侧边栏中提供内容相关的链接。

所以打开askeet/apps/frontend/config/view.yml并且添加我们在上面所显示的元素槽。我们会在Symfony一书的相关章节中了解到更多的关于视图配置的内容。

编写sidebar/default动作与模板

首先,我们会让symfony来初始化新的sidebar模块:

$ symfony init-module frontend sidebar

接下来,我们需要编写一个默认的元素。在askeet/apps/frontend/modules/sidebar/actions/目录下,将actions.class.php改为component.class.php,将其内容改为:

<?php

class sidebarComponents extends sfComponents
{
public function executeDefault()
{
}
}

一个元素视图对应一个模板,就如一个动作对应一个模板。所不同的只是名字的区别:一个元素视图的名字与片段相类似(以_开始),而不同于常规的模板(以Success结尾)。所以用下面的内容来创建一个askeet/apps/frontend/modules/sidebar/templates/_default.php片段(删除不会用到的indexSuccess.php):

<?php echo link_to('ask a new question', 'question/add') ?>

<ul>
<li><?php echo link_to('popular questions', 'question/list') ?></li>
<li><?php echo link_to('latest questions', 'question/recent') ?></li>
<li><?php echo link_to('latest answers', 'answer/recent') ?></li>
</ul>

如果现在我们试着在我们的askeet网站的每一个页面中进行浏览,那么我们就会得到一个错误。那是因为我们是在生产环境下浏览网站,而配置会进行缓存,并且在每次请求时并不会进行分析。我们修改了view.yml配置文件,但是生产环境下的动作并不会了解这些。他们会使用缓存的版本-不包含元素槽的配置。如果我们希望看到改变,我们要清除缓存,或者是在生产环境下浏览:

$ symfony clear-cache

或者:

http://askeet/frontend_dev.php/

浏览工具栏就会显示在每一个页面上。

更多的视图配置

现在让我们看一下apps/config/目录下的程序view.yml配置:

default:
http_metas:
content-type: text/html; charset=utf-8

metas:
title: symfony project
robots: index, follow
description: symfony project
keywords: symfony, project
language: en

stylesheets: [main, layout]

javascripts: []

has_layout: on
layout: layout

components:
sidebar: [sidebar, default]

metas部分包含整个网站的meta标记配置。title关键字定义了在标题栏或是浏览器窗口所显示的标题。title是非常重要的,因为如果网站是通过搜索索引查看的,那么标题将是用户看到的第一个内容。所以有必要将其改为更适合askeet网站的标题:

metas:
title: askeet! ask questions, find answers
robots: index, follow
description: askeet!, a symfony project built in 24 hours
keywords: symfony, project, askeet, php5, question, answer
language: en

刷新当前页面。如果我们没有看到任何改变,那是因为我们是在开发环境下,那么我们应清除缓存来得到正确的窗口标题。

查看我们的工作

一个通常的传统是当我们到达第七天时应停下来查看一下我们的工作。这是一个记录当一些事情,包括当前的数据模型与可用的动作的好机会。

到目前为止,我们的工程中可用的动作列表如下:

answer/
recent
question/
list
show
recent
sidebar/
default (component)
user/
show
login
logout
handleErrorLogin

模块同时包含下面的方法列表:

Anwser()
getRelevancyUpPercent()
getRelevancyDownPercent()
AnswerPeer::
getRecentPager()
Interest->
save()
Question->
setTitle()
QuestionPeer::
getQuestionFromTitle()
getHomepagePager()
getRecentPager()
Relevancy
save()
User->
__toString()
setPassword()

myUser->
signIn()
signOut()
getSubscriberId()
getSubscriber()
getNickName()

另外还有一个自定义的工具与一个自定义的验证器,位于askeet/apps/frontend/lib/目录下。

这七个小时显得并不坏,不是吗?

明天见
分享到:
评论

相关推荐

    多视图例子.多视图例子.多视图例子.

    7. 多视图决策支持系统:说明如何构建一个系统,允许用户根据自己的需求选择最适合的视图,以支持决策过程。 以上内容仅是多视图应用的一部分,实际的"多视图例子"可能会包含更多具体的技术实现和案例研究。通过...

    计算机视觉中的多视图几何(英文版-第2版)

    在多视图几何中,涉及到众多基础理论和实际应用技术,包括摄像机模型、图像重建、几何特征提取、场景三维建模以及图像配准等。 在该领域中,最为经典的理论基础就是射影几何学。射影几何学是研究在某种变换下(称为...

    立体模型的旋转变换 vc源代码 可画三视图

    编程思路:运用点、线、面三表结构,点表x[],y[],z[]存储各顶点坐标, s[]、e[]存储各线段起始及终止点在点表中的位置,m[8][8]中前几位存储线段码,第7位存储该面的线段数,第8位表示可见性。另外加字符响应函数:A...

    福建专版2020中考数学复习方案第七单元视图与变换课时训练35投影与三视图展开图

    投影与三视图是几何学中的重要概念,特别是在解决立体几何问题时不可或缺的工具。三视图是指一个立体图形从三个不同方向(正面、侧面、顶部)观察所得到的平面投影,分别是主视图、左视图和俯视图。这三种视图能够...

    2019秋九年级数学上册第五章投影与视图2视图同步练习新版新人教版20191202524

    - 在日常生活中,例如设计、工程和艺术等领域,三视图被广泛用于描绘和理解三维形状,帮助人们构建实物模型。 - 教育中,通过三视图的练习,可以训练学生的空间想象力和几何推理能力。 4. **几何体识别**: - ...

    九年级数学下册第29章投影与视图单元综合测试1新人教版

    【九年级数学下册第29章:投影与视图】 投影与视图是三维几何的重要组成部分,主要涉及如何通过二维图像理解三维物体的形状和结构。本单元测试旨在检验学生对这一主题的理解和应用能力。 1. **投影与视图的概念**...

    计算机视觉中的多视图几何

    该模型通常包括内部参数(如焦距、光心坐标)和外部参数(如摄像机的位置与姿态)。 3. **多视图重建**:利用多个不同视角下的图像来重建三维场景的过程称为多视图重建。这涉及到从不同的图像中提取特征点,并通过...

    具有PHP数据访问对象(PHPPDO-DAO)和模型视图控制器(MVC)的PHP Web项目.zip

    在本项目中,我们探讨的是一个基于PHP的Web应用程序,它使用了数据访问对象(PDO-DAO)和模型视图控制器(MVC)的设计模式。这些技术是现代Web开发中的核心概念,对于构建可扩展、可维护的Web应用至关重要。 **PHP...

    软件分析与建模 实验3流程视图

    5. **建桥实验**:可能是指特定学校的课程项目,通过实际操作让学生深入理解软件分析与建模的概念。这类实验通常包括具体的任务,例如设计一个特定系统的流程视图。 6. **PowerDesigner的使用**:使用PowerDesigner...

    2019_2020学年九年级数学下册第25章投影与视图达标检测卷新版沪科版20200312321

    第12题则要求根据主视图和左视图还原立体图形,这要求学生能从二维视图重建三维模型。 5. **三视图的绘制**:第15题要求根据规定尺寸画出三视图,这考察了学生的空间想象能力和绘图技巧。三视图必须满足“长对正、...

    计算机视觉中的多视图几何(中文版)

    7. **运动分析**:多视图几何也可以用于分析相机的运动,例如通过光流法估计连续帧间的相机运动,或者通过RANSAC等算法去除噪声,提高运动估计的准确性。 8. **SLAM(Simultaneous Localization and Mapping)**:...

    计算机视觉中的多视图几何(中文完整版)

    7. **运动分析**:利用多视图几何,我们可以分析物体和相机的运动,例如通过光流法估计连续帧间的物体运动,或者通过RANSAC(随机样本一致性)算法剔除匹配错误,提高估计精度。 8. **结构化光照**:在一些高级应用...

    3D模型浏览器.rar

    2. **视图操作**:提供旋转、缩放和平移等基本操作,帮助用户从不同角度观察模型。 3. **光照与阴影**:模拟真实世界的光照效果,包括环境光、聚光灯和点光源等,以及软阴影和硬阴影。 4. **材质与纹理**:支持应用...

    九年级数学下册第29章投影与视图单元综合检测卷新人教版

    7. 投影长度与实际长度的关系:当光线倾斜时,投影长度可能不同于实际长度,且两者之间的夹角会影响投影长度的比例。 8. 体积与表面积的计算:根据几何体的三视图来求解物体的体积和表面积,需要理解每个视图代表的...

    [翻译]Eclipse图形化编辑框架(GEF)/更新第7章:层和视图

    **第7章:层和视图的更新内容** 在更新的第7章中,博主可能详细介绍了以下几点: 1. **层的创建与管理**:如何创建新的层,以及如何在代码中动态添加和移除层,以便根据需要调整元素的层次结构。 2. **层的绘制...

    QT高级编程

    第7章 用qtconcurrent实现线程处理 7.1 在线程中执行函数 7.2 线程中的过滤和映射 第8章 用qthread实现线程处理 8.1 独立项的处理 8.2 共享项的处理 第9章 创建富文本编辑器 9.1 qtextdocument简介 9.2 创建自定义的...

    山东省武城县四女寺镇九年级数学下册第29章投影与视图复习导学案无答案新版新人教版

    本知识点主要涉及初中数学中的投影与视图,是三维空间向二维平面转换的重要概念,尤其在工程设计和艺术创作中有广泛应用。以下是详细解释: 1. 投影: - 平行投影:当光线平行于投影面时产生的投影,例如太阳光下...

    AutoCAD 2016室内装潢设计基础教学视频教程下载第9章 视图工具.zip

    在AutoCAD中,视图工具允许用户从不同的角度查看和操作模型,这对于室内设计来说尤其重要,因为设计师需要从多个维度来审视设计方案。这一章将详细讲解以下知识点: 1. **基本视图**:包括前视图、后视图、左视图、...

    投影与三视图习题集知识.pdf

    综上所述,投影与三视图的知识点包括:几何体的投影特性、视图之间的关系、几何体识别、视图与实际形状的对应、几何体的侧面积计算以及通过视图推断几何体的形状和数量等。掌握这些知识点对于理解和解决实际工程问题...

Global site tag (gtag.js) - Google Analytics