MVC

1. MVC基本概念

为什么使用MVC

MVC是目前应用最广的一种编程方式,本质上,MVC是将代码进行分离,将显示数据的部分、处理业务的部分 和 操作数据的部分 都独立出来。

打个比方,没有使用MVC方式进行开发的项目就好像是一个管理混乱的餐馆,客户点菜(需求),餐厅的经理、服务员、清洁员一起上,然后你一嘴我一嘴的告诉厨师要做什么菜,最后菜出来了,又不管各自的职能都去端菜给客户,极其混乱:

1530972930761

而使用了MVC方式开发的项目就好比是一个管理有序的餐馆,每个人员都知道自己该做什么,调理清晰,执行业务顺畅:

1530972965070

我们以第二天开发的新闻管理系统为例:

以前的实现方式:

1531014431630

使用MVC的实现方式:

1531014444989

使用MVC的好处实在是太多,最主要的方面体现在:

1, 可以实现单入口,增强一个项目中对页面地址的控制能力;

2, 功能和数据分层明确,便于后台人员维护和管理;

3, 极其有利于多人协同开发(前端与后台,后台与后台),各成员之间互不影响;

4, 拥有比较完善的基本结构,多人协同开发也不至于产生混乱。

什么是MVC

MVC不是某种语法,而是前辈们经过无数的实践总结而得出来的一种便捷,智慧的设计思想。

MVC是三个英文单词的首字母,分别代表三个组件:

M –》Model

V –》View

C –》Controller

MVC思想大体上将我们开发的程序分成三个部分(三大组件),并且规定这三个部分:

Model表示模型层,负责数据业务规则的处理,凡是跟数据操作有关的内容都属于数据逻辑,只要与SQL指令有关,那么都属于模型(数据逻辑);

View表示视图层,负责展示数据,其实就是我们看到的页面,也就是模板template(模板),用来承载数据的容器,通常是HTML文件(显示[HTML],美化[CSS]和效果[JavaScript]);

Controller表示控制器,也叫作模块(Module),负责接受用户请求,并调用相应的模型和视图处理和展示业务,是用来与用户直接接触的脚本代码(浏览器直接提交给PHP的脚本对象),判断用户操作的逻辑性,在需要的时候,他可以调用模型操作数据,也可以调用视图展示页面。

名次释义

model : 模型。

module : 模块,组件。

2. 案例:使用MVC实现新闻管理系统

1. MVC实现过程

第一步,搭建MVC基本结构,配置专用虚拟主机域名

  1. 在code目录中创建了名为mvc的目录,

    1531016988428

    mvc目录结构如下图:

1531016948626

  1. 配置www.mvc.com域名指向code/mvc根目录

    在apache的虚拟主机配置文件httpd-vhosts.conf中增加虚拟主机配置项,

    1531017169937

    在hosts文件中增加配置项,

    1531017212138

    重启apache,

    1531017246599

    测试虚拟主机域名是否配置成功,

    在入口文件index.php中构建如下代码:

    1531017356840

    访问index.php效果如下:

    1531017323491

第二步,将HTML模板页面转移进项目目录

  1. 复制后台所需的三个模板页面,拷贝到mvc/app/admin/view目录中,

    在view目录中创建名为news的目录,

    1531017685001

    拷贝三个文件到news目录中,

    1531017770854

  2. 复制前台所需的模板页面,拷贝到mvc/app/home/view目录中,

    复制如下图所示文件,

    1531017890669

    拷贝到view目录中,

    1531017931576

第三步,构建控制器展示视图模板文件

建议:每张有实际意义的表,建立一个对应的控制器类

  1. 在mvc/app/admin/controller目录中创建名为NewsController.class.php,

    1531018163097

    在NewsController.class.php中创建相应的类,并且构建方法展示出不同的后台模板页面,

    1531018518445

  2. 测试访问不同的页面,

    访问后台新闻列表页:在NewsController.class.php类中构建测试代码,如下

    1531018618650

    访问NewsController.class.php文件,

    1531018665311

    如果要切换访问添加页,只要将调用的方法切换为访问showAd方法即可,

    1531018712369

    再次访问NewsController.class.php,页面就成功的切换到了后台新闻添加页,

    1531018751317

==问题==:当前我们访问后台页面都是通过访问NewsController.class.php文件实现的,但是MVC的流程要求访问任何页面都应该通过访问入口文件(index.php)实现,所以,我们需要做进一步的改进。

第四步,实现通过入口文件访问不同的页面

  1. 剪切NewsController.class.php中访问不同页面的测试代码,如下图所示,

    1531019891138
    拷贝到入口文件mvc/index.php中,如下图所示,

    1531019963689

    同时还需在index.php中实例化NewsController类之前引入该类的类文件,

    1531020115555

  2. 调整入口文件Index.php中的代码,如下图所示将蓝框中的代码改为红框中的代码:

    1531021013436

    调整mvc/app/admin/controller/NewsController.class.php中的代码,如下图所示将蓝框中的代码改为红框中的代码:

    1531021107795

  3. 测试访问入口文件index.php的效果,

    1531021161444

  4. 现在我们虽然能够实现从index.php单入口访问不同的页面了,但是每次我们切换访问页面,就需要跑到index.php中手动微调代码,非常的不方便也不智能。所以我们进一步作出改进,在index.php中定义三个变量参数,分别$action动作参数,$module模块参数,$plat平台参数。

    名词释义

    action : 动作

    module : 模块

    platform : 平台

动态拼接我们所需的东西,比用if判断的更实际。实际代码如下图所示蓝框中的代码改为红框中的代码:

1531033549826

测试进一步调整之后的效果:

我们访问后台新闻管理系统列表页,

1531033755367

不需要再手动在index.php中修改代码了,实现的功能OK了。

==问题==:当前我们已经能够通过入口文件index.php实现智能的切换访问不同的页面,但是,我们发现每个页面中展示的数据都是写死的,并不是从数据表中查得的数据,所以,我们需要再一次进行调整。

视图 view OK 了,接下来要实现模型Model ,即,要实现活的数据

第五步,构建模型类,实现在控制器中调用模型操作数据表中的数据

建议:每张有实际意义的表,建立一个对应的模型类

  1. 在mvc/app/model目录下创建一个名为NewsModel.class.php的文件,

    1531034615784

    模型类代码如下图所示:

    1531035545533

  2. 在入口文件中定义model目录的常量,然后引入模型类文件,

    1531035619381

  3. 在mvc/app/admin/controller/NewsController.class.php中的showList方法里调用模型查询数据,

    1531035711459

  4. 回显查询得到的数据到视图模板文件中去,mvc/app/admin/view/news/newslist.html

    1531035824378

    测试访问效果后台新闻管理系统列表页效果:

1531035852016

数据展示正常!

第六步,实现父模型(类)

更正: 父类模型 -> 父模型类

因为很多地方都需要用到数据连接查询等基本操作,所以,需要把这些放置到一个类中。

子类继承,就不用到处都需要new了

  1. 在mvc/core目录下创建名为Model.class.php的文件:

    1531037697777

    Model.class.php中的代码如下:

    1531037763156

  2. 调整父模型类文件mvc/core/Model.class.php中PDO类和PDOException类所使用的命名空间,改动部分如一下所有图中的行号内容,

    1531037844488

    1531037876410

    1531037905831

    1531037926684

  1. 在入口文件index.php中引入父模型类文件,

    1531037985285

  2. 在模型类文件mvc/app/model/NewsModel.class.php继承父类模型,如下图所示将蓝框中的代码改为红框中的代码,

    1531038115357

  3. 测试使用,访问后台新闻管理系统列表页,

    1531038164908

    效果正常,说明父类模型构建成功。

第七步,引入SMARTY模板引擎

  1. 复制SMARTY的核心类库目录libs,

    1531038491032

    拷贝到mvc/plugins目录中,

    1531038546566

    将libs改名为smarty

    1531038577438

  2. 在入口文件index.php中引入SMARTY核心类文件,

    1531039420006

  3. 在mvc/app/admin/controller/NewsController.class.php中showList方法中使用SMARTY,如下图将蓝框所示的代码改为红框所示的代码,

    1531039766358

  4. 调整模板文件(mvc/app/admin/view/news/newslist.html)回显内容:

    1531039849016

  5. 测试访问效果:

    1531039866731

    效果正常。

第八步,实现父控制器类

更正 : 父类控制器 -> 父控制器类

为了简化Smarty的代码,需要构建一个 父控制器类 (继承 Smarty)。

子控制器类 集成 父控制器类,这样就可直接使用 Smarty,而无需实例化它。

  1. 在mvc/core目录下创建Controller.class.php的文件,

    1531042301195

    在Controller.class.php中实现继承Smarty类,

    1531042388086

  2. 在入口文件中引入父类控制器类文件,

    1531042434373

  3. 调整mvc/app/admin/controller/NewsController.class.php中showList方法内使用SMARTY代码,如下图将蓝框所示的代码改变成红框所示的代码:

    1531042734977

  4. 测试使用效果:

    1531042781158

    效果正常。

第九步,实现配置文件

  1. 在mvc根目录中创建名为conf的目录,

    1531189176903

  2. 在mvc/conf目录下创建名为conf.php的文件,

    1531189444605

  3. 在入口文件index.php中引入mvc/conf/conf.php配置文件,

    1531190344187

  4. 在父类模型mvc/core/Model.class.php中使用配置值,如下图所示将蓝框中的代码改为红框中的代码:

    1531190425050

  5. 测试使用效果:访问后台新闻管理系统列表页

    1531190509292

  6. 在mvc/core目录下创建了一个Func.php的文件,封装一个名为C的读取配置项值的函数,

    1531195125586

    1531195172492

  7. 在index.php中引入公共函数,

    1531195232253

  8. 调整父类模型中读取配置的方式,

    1531195279966

测试访问后台新闻管理系统列表页,

1531195319459

继续完善配置文件的配置项,

在mvc/conf/conf.php增加如下配置项,

1531204942908

在入口文件中修改三个参数$plat、$module、$action的默认值使用方式,如下图将蓝框的代码改为红框的代码:

1531205009160

再次测试访问后台新闻管理系统列表页,

1531205094498

第十步,实现初始化文件

  1. 在mvc/conf目录下创建一个名为init.php的文件,

    1531205430421

  2. 将入口文件中的定义常量的操作和引入文件的操作全部剪切转移到mvc/conf/init.php中,

    剪切index.php中如下图所示代码:

    1531205507075

    1531205536680

    将上面剪切的代码转移粘贴到mvc/conf/init.php中,

    1531205614696

    1531205649768

  3. 将mvc/conf/init.php中定义根目录的常量进行调整,

    1531206215541

  4. 再次测试访问后台新闻管理系统列表页,

    1531206257537

再对mvc/conf/init.php中的初始化操作做进一步调整,

在mvc/conf目录下创建define.php文件,

1531206361222

将mvc/conf/init.php中所有define操作的代码进行剪切,

1531206409723

将上面剪切的代码转移粘贴到mvc/conf/define.php中,

1531206448284

在mvc/conf/init.php中引入mvc/conf/define.php文件,

1531206806237
再次测试访问新闻列表页:

1531206840140

2. 全天总结

MVC全天总结流程图在doc/MVC1.vsd

1531044680403

#

1. 实现后台新闻新增页和功能

  1. 调整mvc/app/admin/controller/NewsController.class.php中的showAd方法的程序,如下图将蓝框的代码改为红框的代码:

    1531208255075

  2. 调整mvc/app/admin/view/news/newsad.html中form表单的action,

    1531208390925

  3. 在mvc/app/admin/controller/NewsController.class.php中创建adh方法,

    1531208496983

  4. 测试使用效果:

    访问添加页:

    1531208605725

    点击提交:

    1531208626016

2. 实现前台展示新闻首页

  1. 在mvc/conf/define.php中定义几个路径常量,

    1531210329600

  2. 再在mvc/conf/init.php中引入mvc/app/home/controller/IndexController.class.php,

    1531210385141

  3. 调整mvc/app/home/controller/IndexController.class.php中的代码,如下图将蓝框的代码改为红框的代码:

    1531210444985

  4. 调整mvc/core/Controller.class.php中设置存放模板文件目录的代码,如下图将蓝框的代码改为红框的代码:

    1531210531679

  5. 再次测试访问后台新闻列表页和前台新闻首页

    后台新闻列表页:

    1531210603165

    前台新闻首页:

    1531210629327

虽然现在能够访问到前台新闻首页,但是前台新闻首页没有展示出从数据表中读取的数据

  1. 在mvc/app/home/controller/IndexController.class.php中showIndex方法里渲染模板之前先将数据查询出来,

    1531211263979

    调整模板mvc/app/home/view/newsIndex.html中回显数据的部分,

    1531211323875

  2. 再次测试访问前台新闻首页:

    1531211360389

    说明功能制作OK。

3. 创建核心框架类实现自动加载

文件引入定义中,有些文件,其文件名等,可能是长期不变的,相反,有些可能会变动频繁或不止当前的数量,

未来可能有更多的文件需要引入,每次都手动引入很繁琐。

所以,可以根据命名的规律,来定义一个自动加载的方法。

  1. 在mvc/core目录下创建App.class.php文件,

    1531212246554

  2. 在mvc/core/App.class.php中构建autoload方法,内容如下:

    1531214521156

  3. 在mvc/conf/init.php中注册自动加载方法,

    1531214608626

  4. 再次测试:

    测试访问后台新闻管理系统列表页:

    1531214698370

    测试访问前台新闻首页:

    1531214731773

4. 实现程序启动方法

继续优化入口文件(index.php) ,将接收参数,输出等 归纳到一个(静态)方法中,然后入口文件调用即可。

  1. 在mvc/core/App.class.php中构建一个名为run的静态方法,

    1531214904885

  2. 将入口文件mvc/index.php中如下图所示的代码剪切转移到mvc/core/App.class.php中的run方法里,

    剪切下图所示代码:

    1531214994723

    转移到mvc/core/App.class.php中的run方法里:

    1531215053641

  3. 将mvc/core/App.class.php中的run方法$action、$module、$plat三个局部变量提升为全局变量,如下图将蓝框的代码改为红框的代码:

    1531215368334

  4. 在入口文件index.php中调用启动方法,

    1531215450815

  5. 测试访问效果:

    访问后台新闻列表页:

    1531215508203

5. 实现单例工厂模式

  1. 在mvc/core/App.class.php中构建single方法和$_obj静态属性,如下图所示,

    1531217621021

  2. 调整mvc/core/App.class.php中run方法中实例化控制器类的对象,如下图将蓝框的代码改为红框的代码:

    1531217723272

  3. 再次测试访问后台新闻管理系统列表页,

    1531217809504