在这一章里,咱们将看到Yii2怎样帮助咱们建立web应用。示例虽然很简单,但整个过程都符合软件工程思想。咱们将完成应用开发的每个步骤,而且每一步都会根据权威书籍中的最佳实践来进行:
php
建立领域模型:这本书解释了领域驱动,Tackling Complexity in the Heart of Software, Eric Evans, Addison-Wesley Professionalhtml
设置测试装置:咱们遵守验收测试驱动实践,Growing Object-oriented Software, Guided by Tests, Steve Freeman and Nat Pryce, Addison-Wesley Professionalgit
设置开发流水线github
持续交付:Reliable Software Releases through Build, Test, and Deployment Automation, Jez Humble and David Farley, Addison-Wesley Professionalweb
持续集成:Improving Software Quality and Reducing Risk, Paul M. Duvall, Steve Matyas, and Andrew Glover, Addison-Wesley Professionalshell
红-绿-重构 开发循环:深度解释,请参考下列图书:数据库
Clean Code:A Handbook of Agile Software Craftsmanship, Robert Martin, Prentice Hallapache
Test-Driven Development by Example, Kent Beck, Addison-Wesley Professionaljson
部署和手工测试:这符合持续交付,而且这些步骤也是必不可少的bootstrap
保持专一。
贯穿整本书,在使用示例应用的时候,咱们须要注意实际需求。在这个小节,咱们将定义整个示例的场景。
假设咱们有一个小的商业应用,要对外提供一些服务。咱们有一些客户,他们有一些数目巨大的帐目记录在纸面上,用商业卡片管理很是不方便。所以,咱们须要用自动化的方式把这些档案管理起来。
首先,咱们须要一些增删查改(CRUD)界面进行记录管理,为客户展现最核心的属性。
很明显,咱们的业务和客户可能会随着时间的推移而增加、变化,所以咱们的应用也应该随之变化。在一开始,咱们就应该为可能的变化作好准备。
本着吃本身狗食的原则(译注:微软在1998年提出),既然本身开发的系统本身要使用,那这个系统最好保证是高质量的。
很明显,咱们将在应用中处理客户模型。在“customer”和“client”两个词中,咱们以为“customer”更为贴切。
一个客户(customer)是一我的,他至少具备姓名、地址、电子邮件,以及电话号码等属性。咱们为客户提供一个服务,按小时统计,并根据合约支付这段时间产生的费用。这就是咱们在第一次迭代设计中打算解决的问题。
咱们假设每位客户都是单个的人,所以咱们不用处理公司,有多个联系人的状况。姓名是一个复杂的结构,若是咱们深刻细节,有敬语、职务、昵称、中间名、姓等属性须要考虑。但在这个应用中,咱们真正感兴趣的并非客户的姓名,咱们只是须要用姓名来识别一个客户。所以,咱们将简单的使用一个文本行来表示,容许咱们按任何格式来写入姓名。地址一样能够是一个复杂的结构,此次咱们打算用一个结构来描述,而再也不是一个简单的文本行。这是由于咱们须要经过地址来实现如下两件事情。
进行一些统计,例如在一个特定的城市,有多少个客户
遵守不一样的文化背景,正确地生成邮寄地址
所以,咱们决定采用下面的结构:
用途(例如:帐单地址、购物地址、家庭地址、工做地址)
国家
州,国家下级的区域,好比美国
城市
街道
建筑物
部门/办公室
收件人姓名
邮政编码
咱们应该注意到,一个地址能表明一个部门、邮箱、办公室、组织中的雇员,甚至是整个建筑。一样,一个客户也能够多个地址。
电话这个实体具备如下属性:
用途(我的或工做)
号码
一个客户可能有几个电话号码,经过用途字段区分。
除了姓名、地址、电话这个实体以外,咱们的职员须要经过一种途径对客户进行自由的描述。这里咱们简化了姓名属性。而后,咱们加入生日、电子邮件属性,固然,一个客户能够有多个电子邮件。
咱们先停一下。
咱们如今能画出客户模型的完整聚合图了,以下图所示:
根据Eric Evans的《领域驱动设计》,客户是一个实体(Entity),也就是说,是一个状态能够改变的对象,所以咱们要关心它在整个系统中的一致性。而其它部分是值对象(Value Object),意味着初始建立后,状态不会改变的对象,所以他们是能够彻底互换的。
为了简化处理,咱们再也不详细描述商业上是如何处理客户的,咱们不打算在这本书中涵盖它。不管如何,让咱们把注意力回到咱们为客户提供的一系列服务中来,维护这些记录将会颇有用。咱们将在后面的章节用到这个模型。
让咱们来执行一个特定的任务。考虑到有的人给咱们打电话(假如咱们已经识别出号码),咱们想获取呼入者的全部详细信息。在应用中,若是这个号码没有找到关联的人,那咱们就知道他并非咱们的客户。若是找到关联人,咱们至少能够直接用他的姓名跟他打招呼,这是很好的客户服务。
咱们应该理解数据库查询,咱们须要一个途径能够插入数据,有可能还须要编辑和删除它。所以,咱们第一次开发迭代的特性包含如下内容:
将客户信息插入数据库
在数据库中编辑客户信息
从数据库中删除客户信息
根据电话号码从数据库中查询客户信息
构造通用的数据库查询并非咱们的目标,咱们仅仅须要根据一个电话号码进行查询。
让咱们开始吧。
一直到本书结束,咱们将在接下来的章节中讨论同一个应用,所以准备工做只须要作这一次。
下载示例代码:Packt的网站上能够下载你购买过图书的示例代码,地址是http://www.packtpub.com,若是你已经购买了这本书,请访问http://www.packtpub.com/support,注册后会直接将文件经过电子邮件发送给你。
咱们要开发的应用,从本质上说,是一个客户关系管理系统(CRM)。所以,咱们首先建立一个名为 crmapp 的目录。
请注意,正本书中,经过命令行调用的示例,都假定你当前目录为crmapp目录。
Yii2推荐使用包管理器Composer进行安装,所以咱们就来使用这个工具。你能够去阅读Composer的完整文档了解详细的细节,咱们在这里准备了一个简短的说明:
全部经过Composer安装的包,都会存放在项目路径下,一个名为vendor的子目录中。
与Composer相关的数据,全部依赖和其它信息,都保存在名为composer.json的清单文件中,位于项目根目录下。只要你在这个文件中申明了依赖,你就能在任什么时候候安全的删除vendor目录,并在调用php composer.phar install 或 php composer.phar update时彻底重建。
Composer的文档描述了一个获取composer.phar的方法:
curl -sS https://getcomposer.org/installer | php
固然,若是你的PATH路径中若是没有CURL,你也能够直接去Composer的官方网站 https://getcomposer.org/ 直接下载PHAR文件。(译注:若是是windows环境,推荐直接下载一个Composer的安装包,安装完成后,添加到PATH目录,之后就能够直接使用composer <command>进行调用,而不使用php composer.phar <command>,更为方便)。
咱们准备好后,就能够按下面的格式执行命令了:
php composer.phar <command>
假定你会使用版本控制系统来管理代码,本书的代码使用Git(http://git-scm.com/)进行管理。
准备阶段快速指引:
mkdir crmapp cd crmapp curl -sS | php git init
正如咱们在本章的开始部分所说,咱们将听从测试优先的开发实践来进行验收测试。这样作的缘由以下:
咱们想检查应用是否能正常工做,又不想使用乏味的人工测试
咱们尚未深度的单元测试需求,由于咱们目前主要的工做只是把已经存在的组件装配在一块儿,所以,经过对UI进行端对端的验收测试最为简单可行
若是咱们关心用户特性请求的实现状况,咱们须要一些形式的验收测试。
Yii2内置支持Codeception测试框架,官方站点是 http://codeception.com/。咱们不会在自己中直接使用它,可是 yii2-codeception(https://github.com/yiisoft/yii2-codeception)提供了一些辅助类,来集成到Yii框架中。
让咱们来申明,咱们须要在项目中使用 Codeception,执行下面的命令:
php composer.phar require "codeception/codeception:*"
稍等片刻,直到Composer执行完成。
目前,composer.json文件的内容是:
{ "require": { "codeception/codeception": "*", } }
命令 php composer.phar require <包名:版本> 只是一个辅助方法,将require块插入到清单文件中,并调用update命令。
固然,我也须要将Yii2一样做为依赖加入,但在这以前,让咱们先干一件事情。
正如咱们看到的,Codeception已经存在于 ./vendor/bin/codecept 目录中了。这个路径敲起来比较长,在POSIX兼容的shell中,好比bash,容许咱们使用下面的方式来进行简化:
alias cept="./vender/bin/codecept"
这样作更好。在本章余下的部分,咱们都假定你已经执行了上面这条命令。
Codeception是一个复杂的系统,因此咱们须要依赖它本身内建的命令。不必深刻Codeception的内部细节,咱们只是简单实用就能够了。执行下面的命令:
cept bootstrap
这将为Codeception生成一个tests目录,并进行配置。
如今,让咱们建立一个傻瓜型的验收测试,用以检查咱们的测试工具。
cept generate:cept acceptance SmokeTest
这个命令将生成 SmokeTestCept.php,位于 tests/acceptance 目录下。当咱们打开这个文件,咱们将看到以下代码(根据Codeception版本,代码可能略有不一样):
$I = new AcceptanceTester($scenario); $I->wantTo('perform actions and see result');
AcceptanceTester是一个能够模仿浏览器后的真实用户,对应用进行测试的类。Codeception同时也提供 CodeGuy 用以进行单元测试,提供 TestGuy 进行功能测试,在后面咱们才会用到。
当咱们调用 AcceptanceTester.wantTo("do something")时,咱们只是为下面的测试行为建立了一个标题(用双引号括起来的)。
让咱们修改一下代码,加入冒烟测试的功能,对咱们的首页进行测试:
$I = new AcceptanceTester($scenario); $I->wantTo('See that landing page is up'); $I->amOnPage('/'); $I->see('Our CRM');
当咱们访问应用首页的时候,咱们但愿看到有 Our CRM 这一行。假设咱们应用中已经有那么一个标题存在了。
如今运行测试:
cept run
咱们会看到失败的信息,由于咱们尚未设置web服务器,处理 / 请求。这样,到了该咱们写一些生产代码来经过这个测试的时候了。然而,到目前而言,咱们须要的还不是生产代码,咱们的基础服务都尚未创建起来。咱们须要先创建发布机制。
咱们已经说过,编写的web验收测试会模拟真实用户,在浏览器中打开应用,经过可视的UI进行交互。所以,咱们如今须要作的是将应用整个部署到咱们能运行验收测试的机器。
提示:最多见的状况是,你决定开发和测试使用同一台机器,这是错误的!别这么干。
极可能你的工做平台和应用最终要运行的机器不是同样的。参照已有数十年历史的工业生产,这已是一个持续不断的问题了。当应用的生命周期预计是数年时,在不一样环境的生产服务器上测试你的应用,你会面临类似的集成问题。固然,这不是将打包好的软件卖给用户,这仍是要简便一些。在咱们的案例中,咱们假定一个单一的发布点只有一个固定web应用,所以,简便性不是问题,可重复性测试才是问题。
最终,你的验收测试将会包含如下几个步骤:
将应用发布到测试服务器
在你的机器上运行验收测试
固然,你能够在测试服务器上运行验收测试。要这样作,你只须要用localhost这个本地回路网络接口来配置测试就能够了。然而,仍是须要你安装一些与测试服务器不相干的软件。例如,若是你想全栈运行,使用Selenium进行浏览器内测试,你可能须要安装一个web浏览器,Java运行时,虚拟帧缓冲软件,而且这又须要安装它们各自相关的软件,这样作很是费劲。最有效的作法是使用你本身的桌面环境,来运行web验收测试。
提示:这固然不是在谈论单元测试和功能测试。单元测试因为只与自己有关,应在有原始代码的地方执行,彻底不须要部署。功能测试,应在部署后的应用上测试,由于须要测试通过配置后的最终应用以及交互的正确性。
在任何案例中,理想化的方案是,你只需一个就像deploy这样的简单命令,就能完成如下功能:
访问并启动目标机器(特别是,当它是一个虚拟机实例的时候)
确保除应用外,环境是有效的
将当前代码拷贝到目标机器
在目标机器上,配置刚拷贝过去的代码的环境
启动应用
你应该能在命令行中输入deploy,并敲击Enter键,完成上述的全部步骤。正如Martin Fowler在他的《持续集成》(http://martinfowler.com/articles/continuousIntegration.html)中所说,这对你来讲小菜一碟。理想状况下,部署行为应该在你启动验收测试工具时自动完成。
在本书中,咱们只关心最后两步。由于咱们开发的是PHP应用,只要目标机器上的web服务器在运行,”启动应用“这一步就算已经完成了。
本书仅关注web应用开发,并不涉及系统管理,那是系统管理员的事。然而,在附录A,”使用Vagrant进行部署设置“,咱们准备介绍一个基于虚拟机的部署,一样也很容易在任何桌面工做站上实现。你能够没必要须要另一台物理机,就仍然能模拟现实世界的部署过程。若是你别无选择,强烈推荐你读读。实际上,采用那里的描述,自己全部的代码都准备好了。让咱们假设你有一个准备好的环境和deploy命令,为了简化,咱们同时假定每次运行验收测试前,部署都会自动运行。部署的结果能够从你的机器经过一个简单的URL进行访问,验收测试工具会将它做为应用的入口点。
如今,让咱们到Codeception的跟验收测试相关的配置部分,打开文件 tests/acceptance.suite.yml,把入口URL加入到 modules.config.PhpBrowser.url 词条中。假定你没修改过这个文件,文件内容看起来应该像这样:
class_name: AcceptanceTester modules: enabled: - PhpBrowser - WebHelper config: PhpBrowser: url: 'http://YOUR.APPLICATION.URL'
(译注:本机acceptance.suite.yml文件格式跟上面不一样,不存在config项,url直接放在enabled的PhpBrowser下)
举例来讲,若是你用Apache的基于IP的虚拟主机技术来配置目标机器(https://httpd.apache.org/docs/2.2/vhosts/ip-based.html),modules.config.PhpBrowser.url 的值就应该像这样:http://127.0.0.01:8000。
当咱们改变配置,咱们须要重建Codeception,执行下面的命令:
cept build
不要忘记 cept 这个别名是咱们本身建立的。实际运行的是 ./vendor/bin/codecept 文件。
若是你如今运行测试:
cept run
你应该能够看到以下输出:
你会看到Codeception在 / 路由上显示了一些东西,但还不是咱们指望的。根据使用的Apache版本不一样,能够是404错误或者403错误。若是你使用的其它Web服务器,可能显示的另外的错误信息。不管如何,问题的根源很简单,这就是咱们须要在外界可访问的web目录中,加入index.php文件。
咱们先作一个约定,能够被外部访问到的目录只有一个,命名为web,放在项目的根目录下。例如,若是你的web服务器是Apache,应该把DocumentRoot指向web目录。
所以,将下面的内容放在web目录的index.php文件中:
Our CRM
是的,就是7个字符的文本文件。毕竟,这就是咱们的验收测试指望的全部内容了,正确吗?
让咱们来运行测试:
cept run
咱们获得了以下输出:
如今,咱们须要在项目中使用Yii2了,描述咱们功能需求的最佳方法,就是写一个完整的端到端测试。