外包的活干多了,总有一些小项目,这些小项目甚至不需要考虑安全。这时候用框架显得浪费。自己手写原生代码又有些不太习惯(框架用多了,有时候连基本的都忘了,不能忘本啊。。。)
许久不更的博客。反正也没多少人看,哈~就当给自己一个马克,也希望能给一些刚踏入程序猿大军的新手们做到一个了解MVC抛砖引玉的小作用吧。
唠唠叨叨一些废话,开始说真的。
———-我是分割线—————–
1.我对MVC的理解
再唠叨一遍MVC三个字母的含义吧
M:model->模型。一般是操作数据的地方
V:view->视图。用户肉眼直接看到的界面
C:controller->控制器。控制整个应用的作用(好难全面解释,比如之所以点保存能够保存信息而不是删掉信息,这就是控制器的作用)
所以,MVC结构在项目开发中的绝大部分作用就是从控制器(C)作为切入点[和个人习惯有关系,但是前提要架构好整体项目],调用模型接口(M)进行数据操作,并且展示在视图(V)上,以视觉的方式直观的反映给用户。
2.预设框架的基本结构
因为比较熟悉CI框架,也用过TP框架所以基本结构如下:
1).一个框架要有一个入口文件
2).框架和URL基本调用方式为
http://基本网址/入口文件/控制器的文件夹/控制器/方法?参数名=参数值&参数名=参数值
3.文件夹结构
这里列出基本的文件及文件夹的结构
-info..........................框架文件夹
-config....................配置文件夹
database.php...........数据库配置文件
-controller................用户控制器文件夹
-test..................用户控制器文件夹
welcome.php........用户控制器文件
-core......................本框架的一些核心文件
Info_Controller.php....框架的核心控制器
Info_Model.php.........框架核心模型
route.php..............框架的路由规则
-models....................用户模型文件夹
user_model.php.........用户模型文件
-views.....................用户视图文件夹
welcome.html...........用户视图文件
index.php..................单入口文件
4.开始~
0).但入口文件设置基本信息
<?php
#info/index.php:
#声明项目根本目录
define("SERVER_ROOT",dirname(__FILE__));
#声明项目网址
define("SITE_ROOT","localhost/info");
#声明视图文件夹
define("VIEWS_ROOT",dirname(__FILE__).'views');
#声明控制器文件夹
define("CONTROLLER_ROOT",'/controllers');
#引入框架核心控制器
require_once(SERVER_ROOT . "/core/Info_Controller.php");
#引入框架核心模型
require_once(SERVER_ROOT . "/core/Info_Model.php");
#引入路由
require_once(SERVER_ROOT . "/core/route.php");
1).路由 从URL获取所有的目标信息
URL:
localhost/info/index.php/test/welcome/hello.php?getKey=getValue
从上述URL可以提取到,
我们要调用welcome控制器下的hello方法,并且以GET方式传入了参数名:getKey,参数值:getValue这样一对k/v;
熟悉 $_SERVER 的应该知道这样一个变量 :$_SERVER[‘PATH_INFO’]。
(可以通过 var_dump($_SERVER)查看,如果没有,则说明服务器没有打开path_info,本文默认打开)
该变量可以简单地理解为记录了从脚本文件(即单入口文件)到传参开始的完整路径
即’/test/welcome/hello.php’部分,所以我们要利用该变量分割出文件夹、控制器以及方法。直接上代码比较明显
<?php
#info/core/index.php
#获取controller和function
$pathInfo = $_SERVER['PATH_INFO'];
$pathArr = explode('/', $pathInfo);#分割文件夹、控制器和方法
array_shift($pathArr);#删除第一个空元素,因为PATH_INFO以'/'开始
$length = sizeof($pathArr);#获取长度用来分离出控制器和方法
#获取路径
$root = SERVER_ROOT . CONTROLLER_ROOT;
for ($i=0; $i < $length-2; $i++) {
$root .= '/'.$pathArr[$i];
}
#判断类文件及类是否存在
#命名规则为:
#控制器文件为小写字母+'.php':welcome.php
#控制器类名为首字母大写+'_Controller':Welcome_Controller
$class = $pathArr[$length-2];#$pathArr的最后元素为方法名
$className = ucfirst($class) . '_Controller';
$classFile = $root . '/' . $class . '.php';
unset($class);#用完释放,防止变量污染
if (!file_exists($classFile)) {
exit('404');
}else{
include $classFile;#文件存在,引入
if (class_exists($className)) {
$class = new $className();#类存在,实例化
}else{
exit('class not exist');#类不存在,报错
}
}
#获取并调用function
$functionStr = $pathArr[$length-1];
#为了做到伪静态,有时候方法后面我们加上'.html'或者'.php'
$functionArr = explode('.', $functionStr);
if (sizeof($functionArr)==2) {
$function = $functionArr[0];
$scriptType = $functionArr[1];
}else{
$function = $functionArr[0];
$scriptType = 'undefined';
}
#获得类和方法之后调用
$class->$function();
因为我是在完成这个框架之后写的该文,所以一定可用。
还是给出测试吧。
在controller下新建一个文件 hello.php
<?php
#测试用完即删掉该文件
class Hello_Controller{
public function world(){
echo 'hello world';
}
}
?>
测试结果如图:
2)控制器加载视图
前面已经做了URL到控制器和方法的路由,下面来做控制器加载视图。
因为PHP是一种可嵌入到HTML的脚本,所以我们可以直接在方法中include一个HTML,(好直白。。。)。同理,方法中所有在include之前声明的变量都可以直接用,但这也是一个弊端,即所有的变量都可以在HTML中使用。所以我们将include封装成一个方法,任何将要显示在HTML页面的变量都以参数的方式传给该方法。
考虑到该方法可能会被多次调用,所以封装为核心类的方法,这样每次新建一个方法只要继承核心类就可以调用。即在info_Controller.php中声明一个loadView的方法,之后controller文件夹下所有的类都extends Info_Controller即可:
<?php
#核心类 info/core/Info_Controller.php
class Info_Controller{
public function loadView($view,$data=NULL){
#拿到参数传入的数据
$data = $data;
#所有视图文件都在VIEWS_ROOT中
require_once(VIEWS_ROOT . "/$view");
}
}
<?php
#用户自定义类 info/controller/test/welcome.php
class Welcome_Controller extends Info_Controller{
public function hello(){
$data['key'] = 'value';
$this->loadView('welcome.html',$data);
}
}
}
<!-- 视图文件 /info/views/welcome.html -->
<html>
<head>
</head>
<body>
<?php var_dump($data);?>
<h1>helloworld</h1>
</body>
</html>
测试结果:
3)控制器调用模型
模型层我们主要是用来进行数据库操作。首先我们要连接数据库。
连接数据库的信息,一般我们都放在单独的配置文件,连接的时候直接引入该文件即可。
<?php
#/info/config/database.php
$_DATABASE['DBdriver'] = 'mysql';
$_DATABASE['host'] = 'localhost';
$_DATABASE['port'] = '3306';
$_DATABASE['username'] = 'root';
$_DATABASE['password'] = 'root';
$_DATABASE['database'] = 'bbs';
同2),我们肯能要多次进行数据库操作,所以每次都去connect数据库太繁琐,这里我们把连接数据库的操作写在核心model文件:info_model.php的构造函数中,这样之后我们每次新建一个模型的时候直接继承核心model类即可。
<?php
#info/core/Info_Model.php
#这里我们使用PDO进行连接
require_once(SERVER_ROOT . "/config/database.php");
class Info_Model{
function __construct(){
#链接数据库
global $_DATABASE;#方法外的变量通过global引入
$driver = $_DATABASE['DBdriver'];
$host = $_DATABASE['host'];
$port = $_DATABASE['port'];
$username = $_DATABASE['username'];
$password = $_DATABASE['password'];
$database = $_DATABASE['database'];
$this->db = new PDO("$driver:host=$host;dbname=$database;charset=utf8mb4",$username,$password);
$this->db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC);#默认返回关联数组
$this->db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); #禁用prepared statements的仿真效果
}
}
每个model文件中的类需要实例化之后才能使用。所以我们可以像2)中加载视图一样,在核心控制器中封装一个加载模型的函数
<?php
#/info/core/Info_Controller.php
class Info_Controller{
#加载控制器
public function loadView($view,$data=NULL){
$data = $data;
require_once(SERVER_ROOT . "/views/$view");
}
#加载模型
public function loadModel($model,$alias=NULL){
$modelFile = SERVER_ROOT . "/models/$model" . '.php';
if (!file_exists($modelFile)) {
exit('404');
}else{
require_once($modelFile);
#参考CI,可以给调用的模型一个别名
if ($alias) {
$this->$model=$this->$alias = new $model();
}else{
$this->$model = new $model();
}
}
}
}
新建一个模型。
<?php
#/info/model/user_model.php
class user_model extends Info_Model{
#声明一个读取数据库中所有用户数据的方法
#在此之前要在数据库中新建一个用户表‘user_info’并加入一些数据
public function getAllUserInfo(){
$sql = 'select * from user_info';
$data = $this->db->query($sql)->fetchAll();
return $data;
}
}
在自定义的控制器中加载该模型并调用上面声明的方法
<?php
class Welcome_Controller extends Info_Controller{
public function hello(){
#加载模型并且给一个别名。
$this->loadModel('user_model','user');
#调用该模型的方法(获取用户数据)
$userinfo = $this->user->getAllUserInfo();
var_dump($userinfo);
}
}
测试结果:
到这里基本上就结束了。
这只是一个简单的MVC。没有考虑的太多。欢迎指正。
附上代码链接:
http://download.youkuaiyun.com/detail/u013332865/9705768