在第二天的学习中,我们了解了如何基于一个关系数据模型构建一个对象模型,并且为这些对象中的一个对象生成了一个程序框架。顺便说一下,在前一天所生成的程序代码可以在askeet的SVN仓库得到:
http://svn.askeet.com/
我们在第三天的目标是要为这个网站定义一个漂亮的结构布局,将问题列表作为默认主页,显示对一个问题感兴趣的用户数量,以及为了进行数据测试由样本文本文件移居数据库。所需要做的内容并不是很多,但是有许多内容需要阅读和理解。
为了阅读这一节教程,我们需要熟悉Symfony一书中所解释的工程,程序,模块以及动作等概念。
MVC模型今天是我们第一次进入MVC体系结构的世界。这意味着什么呢?简单来说就是生成一个页面的代码依据其特性位于不同的文件中。
如果代码独立于页面专注于数据的操作,他应位于Model中(大多数情况下在askeet/lib/model/目录中)。如果他专注于最终的显示,他应位于View中。在Symfony中,视图层依据于模型(例如askeet/apps/frontend/modules/question/templates/中)以及配置文件。最后,将所有这些联系在一起,并将网站逻辑转换为PHP代码的程序代码位于Controller,而在Symfony中,一个指定页面的控制器被称之为动作。我们可以在Symfony一书的MVC implementation in symfony一章中了解更多关于这个模型的内容。
然而今天我们的程序只是做一些小的修改,我们将会操作许多不同的文件。不要感到麻烦,因为文件的组织与不同层的代码的分离很快就会变得明显并且有用。
改变布局在设计模式中,一个动作所调用的模板内容集成到一个全局的模板或是布局中。换句话说,布局包含所有接口的不变部分,他修饰动作的结果。打开默认的布局(askeet/apps/frontend/templates/layout.php),将其改为下面的内容:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<?php echo include_http_metas() ?>
<?php echo include_metas() ?>
<?php echo include_title() ?>
<link rel="shortcut icon" href="/favicon.ico" />
</head>
<body>
<div id="header">
<ul>
<li><?php echo link_to('about', '@homepage') ?></li>
</ul>
<h1><?php echo link_to(image_tag('askeet_logo.gif', 'alt=askeet'), '@homepage') ?></h1>
</div>
<div id="content">
<div id="content_main">
<?php echo $sf_data->getRaw('sf_content') ?>
<div class="verticalalign"></div>
</div>
<div id="content_bar">
<!-- Nothing for the moment -->
<div class="verticalalign"></div>
</div>
</div>
</body>
</html>
我们试着尽可能保持标记的可理解,并且将所有的样式移到CSS样式表中。我们并不会在这里描述样式文件,因为CSS语法并不是这个教程的目的。我们可以在SVN仓库中下载这些样式文件。
我们创建了两个样式文件(main.css,layout.css)。将他们拷贝到我们的askeet/web/css/目录下,并且编辑我们的frontend/config/view.yml文件来改变自动载入的样式文件:
stylesheets: [main, layout]
此时的布局仍是轻量级的,我们将会在后来重新构建。模板中重要的一点就是<head>部分,这是自动生在的,而sf_content变量包含动作的结果。
我们可以通过请求主页来检测修改是否可以正确显示--这一次是在开发环境中:
http://askeet/frontend_dev.php/
关于环境的说明如果我们想要了解http://askeet/frontend_dev.php/与http://askeet/之间的区别,那么我们需要看一下Symfony一书的配置一节。现在我们只需要知道他们指向同一个程序,但是却在不同的环境中。一个环境是一个唯一的配置,此时的框架的特性可以依据需要来决是否激活。
在这种情况下,/frontend_dev.php/ URL指向开发环境,此时在每一次请求时都会解析整个配置,HTML缓存未激活,而调试工具都可用(包括窗口右上角的半透明的工具条)。而 / URL--等同于/index.php/--指向生产环境,此时配置是"编译"的,而为了加速页面的传输禁用了调试工具条。
这两个PHP脚本--frontend_dev.php与index.php--称之为前端控制器,所有到程序的请求都是由他们来处理的。我们可以在askeet/web/目录下找到他们。事实上,index.php应命名为frontend_dev.php,但是因为frontend是我们创建的第一个程序,Symfony推测我们也许希望其作为默认程序,所以将其重命名为index.php,从而我们可以在仅请求/来在生产环境中查看我们的程序。如果我们希望了解更多的关于通常的MVC模型中前端控制器与控制器层,我们可以查看Symfony一书的控制器一章。
一个良好的规则就是我们在开发环境中进行浏览,直到对所有的特性感到满意,然后切换到生产环境来检测其速度与良好的URL。
记住,当我们添加新类或是改变配置文件后,要在生产环境中查看结果,应要先清除缓存。
重新定义主页现在,当我们请求新网站的主页时,他显示一个'恭喜'页面。一个更好的主意是显示问题列表。要这样做,打开frontend程序的routing配置文件(askeet/apps/frontend/config/routing.yml),定位到homepage:部分,将其改变:
homepage:
url: /
param: { module: question, action: list }
在开发环境中刷新页面(http://askeet/frontend_dev.php),现在就会显示问题列表了。
如果我们是一个很好奇的人,我们也许会查找包含’恭喜'信息的页面。而我们会很奇怪并不会在askeet目录下找到这个文件。事实上,默认的default/index动作的模板是在Symfony的数据目录中定义的,而且是独立于工程的。如是我们希望覆盖他,我们可以在我们自己的目录中创建一个默认的模块。
路由系统所提供的功能我们将会在后面详细讨论,但是如果我们感兴趣,我们可以阅读Symfony一书的rounting一章。
定义测试数据主页的列表显示仍然显得很空,除非我们添加我们自己的问题。当我们开发一个程序时,有一些测试数据是一个好主意。手动输入测试数据是相当痛苦的,这也就是为什么Symfony可以使用文件来移居数据库的原因。
我们将在askeet/data/fixtures/目录下创建一个测试数据文件(这个目录需要创建)。使用下面的内容来创建一个名为test_data.yml的文件:
User:
anonymous:
nickname: anonymous
first_name: Anonymous
last_name: Coward
fabien:
nickname: fabpot
first_name: Fabien
last_name: Potencier
francois:
nickname: francoisz
first_name: François
last_name: Zaninotto
Question:
q1:
title: What shall I do tonight with my girlfriend?
user_id: fabien
body: |
We shall meet in front of the Dunkin'Donuts before dinner,
and I haven't the slightest idea of what I can do with her.
She's not interested in programming, space opera movies nor insects.
She's kinda cute, so I really need to find something
that will keep her to my side for another evening.
q2:
title: What can I offer to my step mother?
user_id: anonymous
body: |
My stepmother has everything a stepmother is usually offered
(watch, vacuum cleaner, earrings, del.icio.us account).
Her birthday comes next week, I am broke, and I know that
if I don't offer her something sweet, my girlfriend
won't look at me in the eyes for another month.
q3:
title: How can I generate traffic to my blog?
user_id: francois
body: |
I have a very swell blog that talks
about my class and mates and pets and favorite movies.
Interest:
i1: { user_id: fabien, question_id: q1 }
i2: { user_id: francois, question_id: q1 }
i3: { user_id: francois, question_id: q2 }
i4: { user_id: fabien, question_id: q2 }
首先,也许我们会认出这里的YAML语法。如果我们还不熟悉Symfony,那么也许我们还不知道在框架中YAML格式是配置文件最喜欢的格式。这并不是唯一的--如果我们还可以使用XML或是.ini文件,可以很容易的添加一个配置处理器允许Symfony读取他们。如果我们有时间和耐心,我们可以读一下Symfony一书中的实际配置一章中的关于YAML和Symfony配置的更多内容。而现在,如果我们并不熟悉YAML语法,那么我们需要立即开始,因为这个教程大量用了YAML语法格式。
好了,现在回到测试数据文件。他定义了对象实体,并以内部名称来标识。这个标签对于链接相关对象而不必定义id是十分有用的。例如,创建的第一个对象为User类,并且标识为fabien。第一个问题标识为q1。这就很容易通过指定一个相关对象标签来创建一个类的对象:
Interest:
i1:
user_id: fabien
question_id: q1
前面所给定的数据文件使用了短YAML语法来描述这些内容。我们可以在Symfony一书的数据文件一章了解到更多的关于数据迁移的内容。
我们并不需要为created_at与updated_at列定义值,因为在默认情况下Symfony知道如何来填充这些域。
创建一个批文件来迁移数据
下一步就是实际的迁移数据库,而我们希望用一个可以在命令进行调用的PHP脚本来完成这些工作--一个批处理。
批处理框架用下面的内容在askeet/batch/目录下创建一个名为load_data.php的文件:
<?php
define('SF_ROOT_DIR', realpath(dirname(__FILE__).'/..'));
define('SF_APP', 'frontend');
define('SF_ENVIRONMENT', 'dev');
define('SF_DEBUG', true);
require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');
// initialize database manager
$databaseManager = new sfDatabaseManager();
$databaseManager->initialize();
?>
这个脚本并没有做任何事情,或者说是几乎没有做任何事情:他定义了一个要进行配置的路径,程序以及环境,装入这个配置,并且初始化数据管理器。但是这已经很多了:这就意味着下面所编写的代码将会利用自动装入的类,自动连接到Propel对象以及Symfony根类。
如果我们已经测试了Symfony的前端控制器(例如askeet/web/index.php),我们就会发现这些代码十分的熟悉。这是因为正如批请求一样,每一个web请求需要访问同样的对象与配置。
数据导入
现在已经准备好了批处理的框架,要在使其来完成一些事情了。批处理需要完成:
1 读取YAML文件
2 创建Propel对象实例
3 在所链接的数据库的数据表中创建相关的记录
这听起来很复杂,但是在Symfony中,由于sfPropelData对象,我们只需要两行代码就可以完成这些工作。在askeet/batch/load_data.php脚本最后的?>前添加下面的代码:
data = new sfPropelData();
$data->loadData(sfConfig::get('sf_data_dir').DIRECTORY_SEPARATOR.'fixtures');
这就完成了。创建了一个sfPropelData对象,并且通知其将指定目录下的所有数据--我们的fixtures目录--装入到databases.yml配置文件中所定义的数据库中。
这里的DIRECTORY_SEPARATOR常量用来兼容Windows与*nix平台。
启动批处理
最后,我们可以检测这些代码是否值得我们这样的论述,在命令行输入下面的命令:
$ cd /home/sfprojects/askeet/batch
$ php load_data.php
通过刷新开发中的主页我们可以检测数据库中的这些修改:
http://askeet/frontend_dev.php
数据已成功装入。
默认情况下,sfPropelData对象会在装入新的数据之前删除我们所有的数据。我们也可以在当前数据之后添加:
$data = new sfPropelData();
$data->setDeleteCurrentData(false);
$data->loadData(sfConfig::get('sf_data_dir').DIRECTORY_SEPARATOR.'fixtures');
在模块中访问数据
当请求question模块的list动作时所显示的页面是executeList()(可以在askeet/frontend/modules/question/actions/action.class.php动作文件中找到)方法传递到askeet/apps/frontend/modules/question/templates/listSuccess.php模板中的结果。这是基于Symfony一书中控制器一章中所解释的名字转换。让我们看一下所执行的代码:
actions.class.php:
public function executeList ()
{
$this->questions = QuestionPeer::doSelect(new Criteria());
}
listSuccess.php:
...
<?php foreach ($questions as $question): ?>
<tr>
<td><?php echo link_to($question->getId(), 'question/show?id='.$question->getId()) ?></td>
<td><?php echo $question->getTitle() ?></td>
<td><?php echo $question->getBody() ?></td>
<td><?php echo $question->getCreatedAt() ?></td>
<td><?php echo $question->getUpdatedAt() ?></td>
</tr>
<?php endforeach; ?>
一步一步的,其所做的工作如下:
1 动作请求满足一个空标准(criteria)的Question表的记录
2 记录列表放在一个数组中($questions)传递到模板
3 模板在动作所传递过来的问题中循环
4 模板显示记录中每一列的值
->getId(),->getTitle(),->getBody()等方法是在Symfony的propel-build-model命令调用中生成的,用来获取id,title,body等数据域的值。这些是标准的获取方法,由在域名字前添加get前缀进行格式化,而Propel也提供了标准的设置方法,以set为前缀。Propel文档描述了为每一个类所创建的访问方法。
而我们也许会感到迷惑的是QuestionPeer::doSelect(new Criteria())调用,他也是一个标准的Propel请求。Propel文档会详细的对其进行解释。
如果我们并不能完全理解上面所编写的代码也不担心,几天后就会变得清晰了。
修改question/list模板现在数据已经包含了对问题感兴趣的记录,这样就应该很容易的得到对一个问题感兴趣的用户。如是我们看一下askeet/lib/model/om/目录下由Propel所生成的BaseQuestion.php类,我们就会注意到有一个->getInterests()方法。Propel会检测到在Interest数据表定义中的question_id外键,从而推测以一个问题会有一些感兴趣的用户。这样就使其很容易通过修改listSuccess.php(askeet/frontend/modules/question/templates/)模板来显示我们所希望的内容。在这个处理中,我们会移除那些丑陋的表格,并且替换为我们漂亮的div格式:
<?php use_helper('Text') ?>
<h1>popular questions</h1>
<?php foreach($questions as $question): ?>
<div class="question">
<div class="interested_block">
<div class="interested_mark" id="interested_in_<?php echo $question->getId() ?>">
<?php echo count($question->getInterests()) ?>
</div>
</div>
<h2><?php echo link_to($question->getTitle(), 'question/show?id='.$question->getId()) ?></h2>
<div class="question_body">
<?php echo truncate_text($question->getBody(), 200) ?>
</div>
</div>
<?php endforeach; ?>
我们在这里可以看到与原始的listSuccess.php相同的foreach循环。link_to()与truncate_text()函数是由Symfony所提供的模板帮助器。第一创建了一个指向相同模块的另一个动作的超链接,而第二个将问题内容截短为200个字符。link_to()帮助器是自动装入的,但是我们要使用truncate_text()必须声明使用帮助器的Text组。
现在我们可以刷新主而来测试我们的新模板:
http://askeet/frontend_dev.php/
感兴趣的用户数在每一个问题的附近显示。要得到上面的显示,下载main.css样式文件,并将其放置在askeet/web/css/目录中。
清理propel-generate-crud命令会创建一些我们并不会需要的动作与模板。现在是清除他们的时候了。
在askeet/apps/frontend/modules/question/actions/actions.class.php中要移除的动作:
* executeIndex
* executeEdit
* executeUpdate
* executeCreate
* executeDelete
在askeet/apps/frontend/modules/question/templates/目录中要移除的模板:
editSuccess.php
明天见今天是我们深入Model-View-Controller世界的伟大的一步:通过操作Propel对象模型的布局,模板,动作与对象,我们访问了一个MVC结构程序中所有层次。如果我们并不能完全理解这些层之间的桥梁也不必担心:这会一点点清晰起来的。
今天打开许多文件,如果我们想要了解在一个工程中文件是如何组织的,可以查看Symfony一书的文件结构一章。
明天将会是另一个伟大的一天:我们将会修改视图,设置一个更为复杂的路由规则,修改模块,深入数据操作以及在表之间进行链接。
分享到:
相关推荐
《精通Eclipse Web开发——Java体系结构工具框架及整合应用》是一本深入讲解如何使用Eclipse进行Web开发的专业书籍。本书旨在帮助读者掌握基于Java的Web应用开发技术,特别是利用Eclipse这一强大的集成开发环境(IDE...
在软件开发领域,设计模式和体系结构是至...通过这些练习,学习者能够深入理解设计模式和体系结构的概念,提升软件开发的专业技能。同时,这些练习也鼓励学习者思考如何在实际项目中灵活运用这些知识,以解决复杂问题。
选择题第三题中的正确答案“真”强调了这一原则的重要性。 - **依赖倒置原则**:避免循环依赖是另一个重要的设计原则,选择题第四题指出“消除两个包之间出现的循环依赖在技术上是可行的”,并给出了“假”的答案,...
**1.1 MVC体系结构** MVC(Model-View-Controller)是一种软件架构设计模式,被广泛应用于构建用户界面丰富的应用程序中。它将应用程序逻辑分为三个核心组成部分: - **模型(Model)**:负责管理应用程序的核心数据...
### 第7章 软件体系结构设计 #### 软件体系结构的基本概念与设计文档 软件体系结构是软件工程中的一个重要组成部分,它描述了软件系统的高层次结构、组件及其相互间的关系。软件体系结构的设计文档(SDD,Software...
综上所述,ASP.NET MVC Server Explorer的第三部分深入探讨了如何在ASP.NET MVC环境中设计和实现插件体系结构,利用C#、XML和.NET MVC框架的特性来创建可扩展的Web应用。这包括了依赖注入、面向接口编程、插件配置、...
通过深入理解软件体系结构的概念、原则和最佳实践,软件工程师可以更有效地设计和构建高质量的软件系统,同时也能更好地应对软件开发过程中的各种挑战,最终实现软件项目的目标并满足用户的需求。
Java学习体系结构是一个全面的指南,旨在帮助初学者掌握Java编程技能,并逐步深入到JavaEE应用开发。以下是对各个知识点的详细说明: 1. **JavaSE基础**:这是学习Java的第一步,包括JDK的安装,编写HelloWorld程序...
#### 第三章 软件体系结构设计方法 **面向对象分析与设计** 面向对象方法是一种流行的软件体系结构设计方法,它强调从问题领域中提取对象,并设计对象之间的交互关系。 - **面向对象分析**:关注于识别对象及对象...
《精通Eclipse Web开发:Java体系结构、工具、框架及整合应用 源代码(ch3-ch18)》是一部深入探讨Eclipse平台下Web应用程序开发的资源集合,涵盖了从基础到高级的诸多技术要点。本资源包含从第三章至第十八章的源代码...
面试时,理解Spring MVC的体系结构至关重要。其中包括`HandlerMapping`、`HandlerAdapter`、`ViewResolver`、`HandlerExceptionResolver`以及`HandlerInterceptor`这些关键组件,它们协同工作,完成请求处理的全过程...
4. **第三章 面向对象编程**:讲解面向对象编程的基础概念和最佳实践。 5. **第四章 泛型与集合**:探讨C#中的泛型编程以及标准集合类的使用。 6. **第五章 LINQ和Lambda表达式**:介绍LINQ查询表达式和Lambda表达式...
本资源包含从第3章到第18章的源代码,为深入学习和实践提供了丰富的素材。 1. **Eclipse IDE**:Eclipse是一款流行的开源集成开发环境,支持多种编程语言,特别是对Java的支持极其强大。它提供了代码编辑、调试、...
这个标题暗示了学习者将深入理解如何在.NET环境中使用MVC4模式来开发Web应用,并结合JSP进行视图层的构建。 【描述】提到的“有关雨模块一NIIT考试的内容,应该能考到70分”可能是指某个具体的教学单元或模块,可能...
3. **C#语法基础**:掌握变量、数据类型、控制结构(如if-else、switch、for、while)、函数、类和对象等基本概念。 4. **面向对象编程**:理解封装、继承、多态等OOP概念,以及如何在C#中实现这些特性。 5. **...
本教程的第13章深入探讨了MVC设计模式及其在Java JSP web开发中的应用。 首先,我们来回顾一下过滤器(Filter)的概念。过滤器是Web应用程序的重要组成部分,它允许开发者在请求到达目标资源之前或响应离开之前对其...
《面向模式的软件体系结构》卷2是一本深入探讨软件工程领域的专著,主要关注模式在构建复杂系统中的应用和体系结构设计。该书英文版为专业人士提供了丰富的理论与实践指导,是理解并掌握模式驱动的软件开发不可或缺...
【标题】"NIIT MVC2 MT2 ...【描述】描述简洁,但可以推断出这是一组专门针对NIIT教育机构的某个特定课程单元——可能是关于.NET框架和ASP.NET MVC技术的进阶内容,其中MVC2是.NET Framework 4.0中的一个版本...
这些功能的实现离不开Web服务和API的调用,例如与第三方支付平台(如支付宝、微信支付)的接口集成,以实现安全便捷的在线支付。 对于运营策略,Duwamish可能会采用多种手段吸引和保留用户,如优惠券、积分系统、...
这个压缩包提供了学习者深入理解Model-View-Controller(MVC)设计模式在实际开发中的应用实例。 【描述】"北大青鸟 Y2 使用mvc开发投票系统源码"表明该资源是北大青鸟教学体系中第二阶段(Y2)的教学内容,旨在让...