`
guanhuaing
  • 浏览: 1200148 次
文章分类
社区版块
存档分类
最新评论

第七天 模型与视图操作

 
阅读更多
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/目录下。

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

明天见
分享到:
评论

相关推荐

    毕业设计基于STC12C5A、SIM800C、GPS的汽车防盗报警系统源码.zip

    STC12C5A通过GPS模块获取当前定位信息,如果车辆发生异常震动或车主打来电话(主动请求定位),将通过GSM发送一条定位短信到车主手机,车主点击链接默认打开网页版定位,如果有安装高德地图APP将在APP中打开并展示汽车当前位置 GPS模块可以使用多家的GPS模块,需要注意的是,当前程序对应的是GPS北斗双模芯片,故只解析 GNRMC数据,如果你使用GPS芯片则应改为GPRMC数据即可。 系统在初始化的时候会持续短鸣,每初始化成功一部分后将长鸣一声,如果持续短鸣很久(超过20分钟),建议通过串口助手查看系统输出的调试信息,系统串口默认输出从初始化开始的所有运行状态信息。 不过更建议你使用SIM868模块,集成GPS.GSM.GPRS,使用更加方便

    基于tensorflow2.x卷积神经网络字符型验证码识别.zip

    基于tensorflow2.x卷积神经网络字符型验证码识别 卷积神经网络(Convolutional Neural Networks, CNNs 或 ConvNets)是一类深度神经网络,特别擅长处理图像相关的机器学习和深度学习任务。它们的名称来源于网络中使用了一种叫做卷积的数学运算。以下是卷积神经网络的一些关键组件和特性: 卷积层(Convolutional Layer): 卷积层是CNN的核心组件。它们通过一组可学习的滤波器(或称为卷积核、卷积器)在输入图像(或上一层的输出特征图)上滑动来工作。 滤波器和图像之间的卷积操作生成输出特征图,该特征图反映了滤波器所捕捉的局部图像特性(如边缘、角点等)。 通过使用多个滤波器,卷积层可以提取输入图像中的多种特征。 激活函数(Activation Function): 在卷积操作之后,通常会应用一个激活函数(如ReLU、Sigmoid或tanh)来增加网络的非线性。 池化层(Pooling Layer): 池化层通常位于卷积层之后,用于降低特征图的维度(空间尺寸),减少计算量和参数数量,同时保持特征的空间层次结构。 常见的池化操作包括最大池化(Max Pooling)和平均池化(Average Pooling)。 全连接层(Fully Connected Layer): 在CNN的末端,通常会有几层全连接层(也称为密集层或线性层)。这些层中的每个神经元都与前一层的所有神经元连接。 全连接层通常用于对提取的特征进行分类或回归。 训练过程: CNN的训练过程与其他深度学习模型类似,通过反向传播算法和梯度下降(或其变种)来优化网络参数(如滤波器权重和偏置)。 训练数据通常被分为多个批次(mini-batches),并在每个批次上迭代更新网络参数。 应用: CNN在计算机视觉领域有着广泛的应用,包括图像分类、目标检测、图像分割、人脸识别等。 它们也已被扩展到处理其他类型的数据,如文本(通过卷积一维序列)和音频(通过卷积时间序列)。 随着深度学习技术的发展,卷积神经网络的结构和设计也在不断演变,出现了许多新的变体和改进,如残差网络(ResNet)、深度卷积生成对抗网络(DCGAN)等。

    【三维装箱】遗传和模拟退火算法求解三维装箱优化问题【含Matlab源码 031期】.zip

    【三维装箱】遗传和模拟退火算法求解三维装箱优化问题【含Matlab源码 031期】.zip

    自己编写的python 程序计算cpk/ppk

    cpk&ppk python 小程序,品友点评

    基于Springboot开发的分布式抽奖系统.zip

    基于springboot的java毕业&课程设计

    课设毕设基于SpringBoot+Vue的影城管理系统 LW+PPT+源码可运行.zip

    课设毕设基于SpringBoot+Vue的影城管理系统 LW+PPT+源码可运行.zip

    MC教育版(免登录版)

    MC教育版(免登录版)

    农作物叶片病害分类和分割数据集【数据集+标签】

    包含13993张数据和对应的13993张mask分割模版,数据集用不同目录保存,也可以用作分类数据集 类别包含:桃子、辣椒、覆盆子、大豆、南瓜、草莓

    基于vue+springboot二手交易网站.zip

    基于springboot的java毕业&课程设计

    【三维装箱】遗传算法求解三维装箱优化问题【含Matlab源码 3408期】.zip

    【三维装箱】遗传算法求解三维装箱优化问题【含Matlab源码 3408期】.zip

    基于javaspring 开发框架的培训教程 TP1.zip

    基于javaspring 开发框架的培训教程 TP1.zip

    信号和系统 MATLAB 代码:探索信号生成、系统建模、傅立叶分析、滤波器设计、采样和控制系统仿真.zip

    1.版本:matlab2014/2019a/2021a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    医院网上预约系统设计与开发/毕业设计

    医院网上预约系统设计与开发/毕业设计 JSP基于SSM网上医院预约挂号系统 科室信息: 科室id,科室名称,科室介绍,成立日期,负责人 医生信息: 医生工号,登录密码,所在科室,医生姓名,性别,医生照片,出生日期,医生职位,工作经验,联系方式,擅长,医生介绍 病人信息: 病人id,医生,病人姓名,性别,身份证号,联系电话,病人病例,登记时间 预约信息: 预约id,预约用户,预约医生,预约日期,时段,联系电话,下单时间,处理状态,医生回复 新闻信息: 新闻id,新闻标题,新闻图片,新闻内容,新闻日期,新闻来源 留言: 留言id,留言标题,留言内容,留言人,留言时间,管理回复,回复时间 用户: 用户名,登录密码,姓名,性别,出生日期,用户照片,联系电话,邮箱,家庭地址,注册时间

    企业数据治理之数据质量治理方案.pptx

    企业数据治理之数据质量治理方案

    MySQL8.4.0 LTS(mysql-8.4.0-10.fc38.x86-64.rpm-bundle.tar)

    MySQL8.4.0 LTS(mysql-8.4.0-10.fc38.x86_64.rpm-bundle.tar)适用于Linux Fedora 38 (x86, 64-bit)

    1659 jsp游乐园管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目源码

    一、源码特点 java 医疗数据管理系统 是一套完善的web设计系统,对理解JSP java编程开发语言有帮助采用了数据模型进行区块链设计,系统具有完整的源代码和数据库,系统采用web模式,系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发,数据库为Mysql,使用java语言开发。 二、功能介绍 如博客中介绍 三、注意事项 1、管理员账号:admin 密码:admin 数据库配置文件DBO.java ,权限包括管理员,用户 2、开发环境为TOMCAT7.0,Myeclipse8.5开发,数据库为Mysql,使用java语言开发。 3、数据库文件名是jspyly 系统名称yly 4、地址:http://127.0.0.1:8080/yly/qt/index.jsp

    基于SpringBoot 搭建的番茄短视频-后台管理系统.zip

    基于springboot的java毕业&课程设计

    54.医院分诊管理系统的设计与实现-基于SSM框架+ Mysql+Java设计与实现(可运行源码+数据库+lw)毕业设计管理系统

    可运行源码(含数据库脚本)+开发文档+lw(高分毕设项目) java期末大作业毕业设计项目管理系统计算机软件工程大数据专业 内容概要:通过陆丰市医院门诊分诊系统设计的研究背景、研究的意义和目的,通过运用java语言和ssm框架来建立一款分诊管理系统,能够帮助医院提高工作效率,减少工作中出现的错误率。设计出挂号管理、排队候诊管理以及叫号管理等多个子模块,绘制出实体关系图,利用MySQL技术建立数据库达成了软件与数据库的互通。最后对工作进行了总结和展望。 关键词:分诊管理系统;功能建模;java 全套项目源码+详尽文档,一站式解决您的学习与项目需求。 适用人群: 计算机、通信、人工智能、自动化等专业的学生、老师及从业者。 使用场景及目标: 无论是毕设、期末大作业还是课程设计,一键下载,轻松部署,助您轻松完成项目。 项目代码经过调试测试,确保直接运行,节省您的时间和精力。 其他说明: 项目整体具有较高的学习借鉴价值,基础能力强的可以在此基础上修改调整,以实现不同的功能。

    基于Springboot和SpringCloud的博客.zip

    基于springboot的java毕业&课程设计

Global site tag (gtag.js) - Google Analytics