#1.此框架架构模式遵循的是mvc思想
- M:model模型
- V:view视图
- C:controller控制器
#2.mvc目录结构创建
- 创建项目目录php_mvc
```cpp
|-core 框架核心目录
| |-App.class.php URL重写类
| |-Controller.class.php 所有控制器的基类
| |-MyException.class.php 用户自定义的错误异常类
| |-Model.class.php 数据库操作类 业务逻辑层
|-config 配置
| |-constants.php 项目常量文件
|-app 前台
| |-public 前台公共文件(js、css)
| |-controllers 存放所有的控制器目录
| |-Home.class.php
| |-Test.class.php
| |-models 存放所有的model类
| |-views 存放所有的页面
| | |-index index目录
| | | |-index.php 前台首页页面
| | |-error 错误目录
| | | |-error.php 错误页面
|-web 后台
| |-public 后台公共文件(js、css)
| |-controllers 存放所有的控制器目录
| |-Home.class.php 后台首页控制器
| |-models 存放所有的model类
| |-views 存放所有的页面
| |-index.php 项目后台入口文件,单一入口
| |-.htaccess 后台分布式配置文件
|-index.php 项目前台入口文件,单一入口
|-.htaccess 分布式配置文件
```
#3.单一入口
一般来说,在大部分项目中都是采用单一入口的方式,符合php的规范。
所谓单一入口就是在整个项目运行的过程中,需要加载的类和方法或者模板都是通过index.php来加载的。
这样做的主要原因是为了安全。此外单一入口不代表只有一个入口,最外层的index.php是我们前台的入口文件。web目录下的index.php是我们后台的入口文件。
#4.URL路由之pathinfo路由
我们常见pathinfo地址如下:
```cpp
localhost/php_mvc/index.php?controller=home&method=index
```
这个路由地址,是运行前台的index.php入口,并且传递了两个参数controller和method
controller表示执行哪个控制器,method表示执行哪个方法。上面这条url就是要执行home控制器的index方法。
我们现在就要实现在前台index.php接受这两个参数,当没有这两个参数时,默认执行home控制器中的index方法。然后引入相应的类,加载指定控制器中的指定方法,实例化控制器类,调用控制器中的方法。
```cpp
|-index.php 项目前台入口文件,单一入口
<?php
/**
* Created by PhpStorm.
* User: find35.com
* Date: 15/12/24
* Time: 下午5:15
*/
echo "<meta charset='utf-8'>";
require_once 'app/controllers/Home.class.php';
require_once 'app/controllers/Test.class.php';
//接受两个参数 controller控制器名称 method控制器中的方法
$controller = isset($_GET['controller']) ? $_GET['controller'] : 'home';
$method = isset($_GET['method']) ? $_GET['method'] : 'index';
//加载指定控制器中的制定方法
//实例化控制器类
$c = new $controller;
//调用控制器中的方法
$c->$method();
```
```cpp
|-app 前台
| |-controllers 存放所有的控制器目录
| |-Home.class.php
<?php
/**
* Created by PhpStorm.
* User: find35.com
* Date: 15/12/24
* Time: 下午5:44
*/
class Home
{
public function index(){
echo "这里是home控制器里的index方";
}
}
```
```cpp
|-app 前台
| |-controllers 存放所有的控制器目录
| |-Test.class.php
<?php
/**
* Created by PhpStorm.
* User: find35.com
* Date: 15/12/24
* Time: 下午5:44
*/
class Test
{
public function index(){
echo "这里是test控制器的index方法";
}
}
```
下面的前两个url在浏览器中应该是一样的结果
```cpp
localhost/php_mvc/index.php
localhost/php_mvc/index.php?controller=home&method=index
localhost/php_mvc/index.php?controller=test&method=index
```
#5.隐藏index.php
我们开始对url进行重写,首先为了先把index.php给隐藏,创建分布式配置文件
```cpp
|-.htaccess 分布式配置文件
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FIELNAME} !-d
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
```
然后在你的httpd.conf文件中把下列配置项开启,也就是前面的#号删掉
```cpp
#LoadModule rewrite_module modules/mod_rewrite.so
```
这样我们就把url进行了重写,我们可以进行测试,修改文件
```cpp
|-index.php 项目前台入口文件,单一入口
<?php
/**
* Created by PhpStorm.
* User: find35.com
* Date: 15/12/24
* Time: 下午5:15
*/
echo $_GET['url'];
```
然后在浏览器中输入
```cpp
http://localhost/php_mvc/test/index/id/13
```
如果这时在浏览器中显示如下,则表示成功
```cpp
test/index/id/13
```
也就是把index.php后面的内容全部都输出出来了。
#6.URL路由之重写URL路由
刚才已经成功获取到了url,然后我们要对url进行解析,我们建立一个新的类文件来处理,这个类文件要实现的功能是:得到控制器的名称、得到方法名、其他参数并保存。组装得到控制器的路径,判断控制器文件是否存在,存在则实例化,然后判断方式是否存在,存在则执行方法,如果控制器文件或者方法不存在则执行相应地异常报错。
```cpp
|-core
| |-App.class.php URL重写类
<?php
/**
* Created by PhpStorm.
* User: find35.com
* Date: 15/12/25
* Time: 上午10:53
*/
class App
{
protected static $controller = 'Home';//控制器名
protected static $method = 'index';//方法名
protected static $pams = array();//其他参数
/**
* url重写路由的url地址解析方法
*/
protected static function paseUrl(){
if(isset($_GET['url'])){
$url = explode('/',$_GET['url']);
//得到控制器
if(isset($url[0])){
self::$controller = $url[0];
unset($url[0]);
}
//得到方法名
if(isset($url[1])){
self::$method = $url[1];
unset($url[1]);
}
//判断是否有其他参数
if(isset($url)){
self::$pams = array_values($url);
}
}
}
/**
* 项目的入口方法
* @throws Exception
*/
public static function run(){
self::paseUrl();
//得到控制器的路径
$url = 'app/controllers/'.self::$controller.'.class.php';
//判断控制器文件是否存在
if(file_exists($url)){
$c = new self::$controller;
}else{
throw new Exception('控制器不存在');
}
//执行方法
if(method_exists($c,self::$method)){
$m = self::$method;
$c->$m();
}else{
throw new Exception('方法不存在');
}
}
}
```
在入口文件这里,我们要在执行run方法,并捕获异常情况,如有异常则输出
```cpp
|-index.php 项目前台入口文件,单一入口
<?php
/**
* Created by PhpStorm.
* User: find35.com
* Date: 15/12/24
* Time: 下午5:15
*/
echo "<meta charset='utf-8'>";
require_once 'core/App.class.php';
require_once 'app/controllers/Test.class.php';
require_once 'app/controllers/Home.class.php';
try{
App::run();
throw new Exception();
}catch(Exception $e){
echo $e->getMessage();
}
```
此时我们在浏览器中输入
```cpp
http://localhost/php_mvc/home/index/id/13
```
那么在浏览器中展示如下,表示成功
```cpp
这里是home控制器里的index方法
```
如果我们在浏览器中输入一个不存在的控制器,比如
```cpp
http://localhost/php_mvc/article/index/id/13
```
那么在浏览器将中展示出异常信息
```cpp
控制器不存在
```
#7.自动加载类文件
在一个正常的项目中,会用到很多的类文件,我们就要写很多航require的语句,这样会让代码的冗余度加大,所以为了处理这个情况,php中有一个方法是自动加载类函数,自动加载方法。所以我们需要一个方法去实现加载指定的类文件。具体目标有
- 明确指明改项目中所有类存放目录地址,这里就是控制器controllers,模型model,核心core这三个地方
- 判断类文件在哪个目录中,在找到后require过来,如果都不存在那么异常报错
- 为什么不用__autoload方法来加载类文件,如果项目中前后台都有autoload方法,可能会产生重定义错误。所以我们直接自己定义自动加载类的方法,然后在入口文件处通过spl_autoload_register将我们的类文件进行加载。
```cpp
|-core 框架核心目录
| |-App.class.php URL重写类
<?php
/**
* Created by PhpStorm.
* User: find35.com
* Date: 15/12/25
* Time: 上午10:53
*/
class App
{
/**
* 自动加载类方法
* @param $className
* @throws Exception
*/
public static function myAutoloader($className){
//控制器类文件目录
$controller = 'app/controllers/'.$className.'.class.php';
//模型类文件目录
$model = 'app/models/'.$className.'.class.php';
//核心类文件目录
$core = 'core/'.$className.'.class.php';
if(file_exists($controller)){
require_once $controller;
}else if(file_exists($model)){
require_once $model;
}else if(file_exists($core)){
require_once $core;
}else{
throw new Exception('类文件不存在');
}
}
}
```
```cpp
|-index.php 项目前台入口文件,单一入口
<?php
/**
* Created by PhpStorm.
* User: find35.com
* Date: 15/12/24
* Time: 下午5:15
*/
echo "<meta charset='utf-8'>";
require_once 'core/App.class.php';
//注册一个
spl_autoload_register(array('App','myAutoloader'));
try{
App::run();
throw new Exception();
}catch(Exception $e){
echo $e->getMessage();
}
```
此时我们在浏览器中输入
```cpp
http://localhost/php_mvc/home/index
```
如果展示如下,则表示自动加载类已经成功
```cpp
这里是home控制器里的index方法
```
特别注意,创建类的时候,类的名称和文件的名称要是一致的。
#8.解析URL参数
在刚才的app.class.php类parseurl方法中,我们已经获取到了url的参数,我们继续在run方法中对这个参数进行判断,是否存在多余的参数,如果有参数,那么我们要传递参数,否则就不传递参数。
对于要传递的参数,我们要判断有几个参数,键值是否合法。
是否大于0,是否是偶数,for循环,不满足的都报错“非法参数”
```cpp
|-core 框架核心目录
| |-App.class.php URL重写类
/**
* 项目的入口方法
* @throws Exception
*/
public static function run(){
self::paseUrl();
//得到控制器的路径
$url = 'app/controllers/'.self::$controller.'.class.php';
//判断控制器文件是否存在
if(file_exists($url)){
$c = new self::$controller;
}else{
throw new Exception('控制器不存在');
}
//执行方法
if(method_exists($c,self::$method)){
$m = self::$method;
$new_pams = array();
$num = count(self::$pams);
//传递参数,判断是否有参数
if($num > 0){
//判断传递的参数的数量是否是2的倍数
if($num % 2 == 0){
//将参数进行处理
for($i=0;$i<$num;$i+=2){
$new_pams[self::$pams[$i]] = self::$pams[$i+1];
}
}else{
throw new Exception('非法参数!');
}
}
$c->$m($new_pams);
}else{
throw new Exception('方法不存在');
}
}
```
```cpp
|-app 前台
| |-controllers 存放所有的控制器目录
| |-Home.class.php
<?php
/**
* Created by PhpStorm.
* User: find35.com
* Date: 15/12/24
* Time: 下午5:44
*/
class Home
{
public function index($data = array()){
echo "这里是home控制器里的index方法";
echo "<pre>";
print_r($data);
}
}
```
此时我们就已经将多余的参数都传递到指定的控制器的方法中了。在浏览器中输入
```cpp
http://localhost/php_mvc/home/index/cityname/shanghai/id/7
```
如果浏览器中显示如下,则表示成功
```cpp
这里是home控制器里的index方法
Array
(
[cityname] => shanghai
[id] => 7
)
```
#9.创建控制器基类
控制器基类要实现的功能就是,实现加载相应的页面的方法并且传递数据到页面中。
为什么要创建控制器基类呢,在mvc中,工作原理是,网站首页->Home控制器->index方法->查询需要的所有数据->将数据发送到(view层)->展示给用户
我们新建一个controller基类,实现加载相应页面的方法,首先判断页面是否存在,如果存在那么则引入。并且传递数据到相应的页面中。
```cpp
|-core 框架核心目录
| |-Controller.class.php 所有控制器的基类
<?php
/**
* 所有控制器的基类
* User: find35.com
* Date: 15/12/26
* Time: 上午9:53
*/
class Controller
{
/**
* 加载指定的模板页面
* @param $page
* @param array $data
*/
public function show($page,$data=array()){
$url = "app/views/".$page.".php";
//判断页面是否存在
if(file_exists($url)){
require_once $url;
}
}
}
```
之后让所有其他的类,继承此基类。调用show方法来加载view层。
```cpp
|-app 前台
| |-controllers 存放所有的控制器目录
| |-Home.class.php
<?php
/**
* 前台首页控制器
* User: find35.com
* Date: 15/12/24
* Time: 下午5:44
*/
class Home extends Controller
{
public function index($data = array()){
//加载首页页面
$this->show('index/index',$data);
}
}
```
我们新建一个view层的模板文件,在随便里面写点东西
```cpp
|-app 前台
| |-views 存放所有的页面
| | | |-index.php 前台首页页面
<?php
/**
* 前台首页页面
* User: find35.com
* Date: 15/12/26
* Time: 下午1:09
*/
echo "前台首页";
echo '<pre>';
print_r($data);
echo '</pre>';
```
此时在浏览器中输入
```cpp
http://localhost/php_mvc/home/index/cityname/shanghai/id/7
```
显示如下,则表示成功
```cpp
前台首页Array
(
[cityname] => shanghai
[id] => 7
)
```
#10.mvc工作流
mvc中是通过控制器,调用里面具体的方法,来实现我们要加载哪些的模板。这些都是在控制器里进行的,并且我们可以对数据库进行操作。
#11.错误处理
在项目中,当报错时,会加载相应的错误页面。当我们throw一个异常的时候,将自定义的错误页面进行加载。
首页创建一个自定义的错误异常的模板
```cpp
|-app 前台
| |-views 存放所有的页面
| | |-error 错误目录
| | | |-error.php 错误页面
<?php
/**
* 错误页面
* User: find35.com
* Date: 15/12/26
* Time: 下午7:46
*/
echo "<h2>这里是自定义的异常报错模板:".$msg."<h2>";
```
然后我们开始创建一个自定义的错误类
```cpp
|-core 框架核心目录
| |-MyException.class.php 用户自定义的错误异常类
<?php
/**
* 用户自定义的错误异常类
* User: find35.com
* Date: 15/12/26
* Time: 下午7:48
*/
class MyException extends Exception
{
/**
* 错误页面加载
* @param $msg
*/
public function showError($msg){
$err_dir = 'app/views/error/error.php';
//判断错误页面是否存在
if(file_exists($err_dir)){
require $err_dir;
}
}
}
```
然后我们开始在项目中使用它,首先在app类中使用
```cpp
|-core 框架核心目录
| |-App.class.php URL重写类
<?php
/**
* Created by PhpStorm.
* User: find35.com
* Date: 15/12/25
* Time: 上午10:53
*/
class App
{
protected static $controller = 'Home';//控制器名
protected static $method = 'index';//方法名
protected static $pams = array();//其他参数
/**
* url重写路由的url地址解析方法
*/
protected static function paseUrl(){
if(!empty($_GET['url'])){
$url = explode('/',$_GET['url']);
//得到控制器
if(isset($url[0])){
self::$controller = $url[0];
unset($url[0]);
}
//得到方法名
if(isset($url[1])){
self::$method = $url[1];
unset($url[1]);
}
//判断是否有其他参数
if(isset($url)){
self::$pams = array_values($url);
}
}
}
/**
* 项目的入口方法
* @throws Exception
*/
public static function run(){
self::paseUrl();
//得到控制器的路径
$url = 'app/controllers/'.self::$controller.'.class.php';
//判断控制器文件是否存在
if(file_exists($url)){
$c = new self::$controller;
}else{
throw new MyException('控制器不存在');
}
//执行方法
if(method_exists($c,self::$method)){
$m = self::$method;
$new_pams = array();
$num = count(self::$pams);
//传递参数,判断是否有参数
if($num > 0){
//判断传递的参数的数量是否是2的倍数
if($num % 2 == 0){
//将参数进行处理
for($i=0;$i<$num;$i+=2){
$new_pams[self::$pams[$i]] = self::$pams[$i+1];
}
}else{
throw new MyException('非法参数!');
}
}
$c->$m($new_pams);
}else{
throw new MyException('方法不存在');
}
}
/**
* 自动加载类方法
* @param $className
* @throws Exception
*/
public static function myAutoloader($className){
//控制器类文件目录
$controller = 'app/controllers/'.$className.'.class.php';
//模型类文件目录
$model = 'app/models/'.$className.'.class.php';
//核心类文件目录
$core = 'core/'.$className.'.class.php';
if(file_exists($controller)){
require_once $controller;
}else if(file_exists($model)){
require_once $model;
}else if(file_exists($core)){
require_once $core;
}else{
throw new MyException('类文件不存在');
}
}
}
```
最后我们在入口文件中也是用它
```cpp
|-index.php 项目前台入口文件,单一入口
<?php
/**
* Created by PhpStorm.
* User: find35.com
* Date: 15/12/24
* Time: 下午5:15
*/
echo "<meta charset='utf-8'>";
require_once 'core/App.class.php';
//注册一个
spl_autoload_register(array('App','myAutoloader'));
try{
App::run();
throw new MyException();
}catch(MyException $e){
$e->showError(($e->getMessage()));
}
```
此时我们在浏览器中输入
```cpp
http://localhost/php_mvc//home2/index
```
如果返回的如下,则表示成功
```cpp
这里是自定义的异常报错模板:控制器不存在
```
#12.项目后台mvc架构
其实后台和前台差不多,在web下也有一个index.php文件,然后也需要隐藏index.php,所以我们直接将.htaccess也拿过来。
```cpp
|-web 后台
| |-.htaccess 后台分布式配置文件
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FIELNAME} !-d
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
```
然后后台入口文件和前台的入口文件内容也差不多,只是引入文件的路径需要调整。还有就是我们要区分前后台,根据前后台定义的变量APP来判断。前台入口文件也需要定义 define('APP','app');
```cpp
|-web 后台
| |-index.php 项目后台入口文件,单一入口
<?php
/**
* 项目后台入口文件
* User: find35.com
* Date: 15/12/27
* Time: 下午3:12
*/
echo "<meta charset='utf-8'>";
require_once '../core/App.class.php';
//注册一个
define('APP','web');
spl_autoload_register(array('App','myAutoloader'));
try{
App::run();
}catch(MyException $e){
$e->showError(($e->getMessage()));
}
```
然后我们也需要根据入口文件传递的APP来调整app.class.php文件里面的路径。
```cpp
|-core 框架核心目录
| |-App.class.php URL重写类
<?php
/**
* Created by PhpStorm.
* User: find35.com
* Date: 15/12/25
* Time: 上午10:53
*/
class App
{
protected static $controller = 'Home';//控制器名
protected static $method = 'index';//方法名
protected static $pams = array();//其他参数
/**
* url重写路由的url地址解析方法
*/
protected static function paseUrl(){
if(!empty($_GET['url'])){
$url = explode('/',$_GET['url']);
//得到控制器
if(isset($url[0])){
self::$controller = $url[0];
unset($url[0]);
}
//得到方法名
if(isset($url[1])){
self::$method = $url[1];
unset($url[1]);
}
//判断是否有其他参数
if(isset($url)){
self::$pams = array_values($url);
}
}
}
/**
* 项目的入口方法
* @throws Exception
*/
public static function run(){
self::paseUrl();
//判断是前台还是后台
if(APP == 'app'){
//得到控制器的路径
$url = 'app/controllers/'.self::$controller.'.class.php';
}
if(APP == 'web'){
//得到控制器的路径
$url = 'controllers/'.self::$controller.'.class.php';
}
//判断控制器文件是否存在
if(file_exists($url)){
$c = new self::$controller;
}else{
throw new MyException('控制器不存在');
}
//执行方法
if(method_exists($c,self::$method)){
$m = self::$method;
$new_pams = array();
$num = count(self::$pams);
//传递参数,判断是否有参数
if($num > 0){
//判断传递的参数的数量是否是2的倍数
if($num % 2 == 0){
//将参数进行处理
for($i=0;$i<$num;$i+=2){
$new_pams[self::$pams[$i]] = self::$pams[$i+1];
}
}else{
throw new MyException('非法参数!');
}
}
$c->$m($new_pams);
}else{
throw new MyException('方法不存在');
}
}
/**
* 自动加载类方法
* @param $className
* @throws Exception
*/
public static function myAutoloader($className){
if(APP == 'app'){
//控制器类文件目录
$controller = 'app/controllers/'.$className.'.class.php';
//模型类文件目录
$model = 'app/models/'.$className.'.class.php';
//核心类文件目录
$core = 'core/'.$className.'.class.php';
}
if(APP == 'web'){
//控制器类文件目录
$controller = 'controllers/'.$className.'.class.php';
//模型类文件目录
$model = 'models/'.$className.'.class.php';
//核心类文件目录
$core = '../core/'.$className.'.class.php';
}
if(file_exists($controller)){
require_once $controller;
}else if(file_exists($model)){
require_once $model;
}else if(file_exists($core)){
require_once $core;
}else{
throw new MyException('类文件不存在');
}
}
}
```
然后我们也建立一个后台首页控制器
```cpp
|-web 后台
| |-controllers 存放所有的控制器目录
| |-Home.class.php 后台首页控制器
<?php
/**
* 网站后台首页
* User: find35.com
* Date: 15/12/27
* Time: 下午3:19
*/
class Home
{
public function index(){
echo '这里是后台的控制器首页';
}
}
```
此时我们在浏览器中输入
```cpp
http://localhost/php_mvc/web/
```
如果显示如下则表示成功
```cpp
这里是后台的控制器首页
```
#13.封装一个单例模式的MODEL类
单例模式就是一个类在使用的时候只能有一个实例,类的实例是在类内部进行创建,好处是大大的节省了资源的开销,方便访问类的实例,使用最多的地方是数据库类的封装。具体实现的方法是将我们的构造方法进行private进行修饰,禁止在类外进行实例化,因此只要你在类面进行实例化,就会报错。再类里面定义一个,可以在类里面得到一个实例的方法。判断类是否存在,如果存在则返回否则创建。
我们创建一个model.class.php数据库操作类
```cpp
|-core 框架核心目录
| |-Model.class.php 数据库操作类 业务逻辑层
<?php
/**
* 数据库操作类
* User: find35.com
* Date: 15/12/27
* Time: 下午4:52
*/
class Model
{
protected static $_instance;
//单例模式 不允许在类外对类进行实例化
private function __construct(){}
//获得类的实例
public static function getSingleton(){
//判断我们类的实例是否存在,没有则创建之
if(!isset(self::$_instance)){
self::$_instance = new self();
}
return self::$_instance;
}
}
```
再类中对构造函数给予了private权限,因此无法在外面对这个类进行实例化,然后我们在入口文件这里执行以下这个方法,看看有没有东西
```cpp
|-index.php 项目前台入口文件,单一入口
<?php
/**
* 项目前台入口文件
* User: find35.com
* Date: 15/12/24
* Time: 下午5:15
*/
echo "<meta charset='utf-8'>";
require_once 'core/App.class.php';
//注册一个
define('APP','app');
spl_autoload_register(array('App','myAutoloader'));
try{
App::run();
}catch(MyException $e){
$e->showError($e->getMessage());
}
$db = Model::getStringleton();
print_r($db);
```
此时我们在浏览器中输入
```cpp
http://localhost/php_mvc/
```
如果返回如下,则表示成功
```cpp
前台首页Model Object ( )
```
#14.PDO方式连接MySQL数据库
采用的是PDO的方式进行封装,PDO是PHP中的一个扩展库,他是一个纯面向对象的,好处就是提供了一个可以连接各种数据库的接口,另外PDO中的prepare预处理的方式可以大大减少sql语句的注入机器相关的安全性。
和其他框架一样,我们将关于数据库信息写入一个配置文件中。
```cpp
|-config 配置
| |-constants.php 项目常量文件
<?php
/**
* 项目常量文件
* User: find35.com
* Date: 15/12/30
* Time: 下午8:49
*/
define('HOST','localhost');//服务器地址
define('UNAME','root');//数据库账号
define('UPASS','');//数据库密码
define('DBNAME','test');//操作的数据库名
```
之后我们在前台入口文件引入这个文件。然后我们在model.class.php类中封装一个方法来连接数据库。
```cpp
|-core 框架核心目录
| |-Model.class.php 数据库操作类 业务逻辑层
<?php
/**
* 数据库操作类
* User: find35.com
* Date: 15/12/27
* Time: 下午4:52
*/
class Model
{
protected static $_instance;
protected static $_link;
/**
* 单例模式 不允许在类外对类进行实例化
*/
private function __construct(){}
/**
* 获得类的实例
* @return mixed|Model
*/
public static function getStringleton(){
//判断我们类的实例是否存在,没有则创建之
if(!isset(self::$_instance)){
self::$_instance = new self();
}
//连接数据库
self::connect(HOST,UNAME,UPASS,DBNAME);
return self::$_instance;
}
/**
* 连接数据库方法
* @param $host 服务器地址
* @param $username 数据库账号
* @param $userpass 数据库密码
* @param $dbname 操作的数据库名
*/
protected static function connect($host,$username,$userpass,$dbname){
try{
self::$_link = new PDO("mysql:host={$host};dbname={$dbname}",$username,$userpass);
if(self::$_link){
echo '连接数据库成功';
}
}catch (PDOException $e){
MyException::showError($e->getMessage());
}
}
}
```
我们把自定义的异常报错类的showerror方法设置为static属性,此处不贴代码了
然后我们在入口文件调用下,看看是否可以
```cpp
|-index.php 项目前台入口文件,单一入口
<?php
/**
* 项目前台入口文件
* User: find35.com
* Date: 15/12/24
* Time: 下午5:15
*/
echo "<meta charset='utf-8'>";
require_once 'core/App.class.php';
require_once 'config/constants.php';
//注册一个
define('APP','app');
spl_autoload_register(array('App','myAutoloader'));
try{
App::run();
}catch(MyException $e){
$e->showError($e->getMessage());
}
$db = Model::getStringleton();
```
浏览器中输入
```cpp
http://localhost/php_mvc
```
返回如下,则表示成功
```cpp
前台首页连接数据库成功
```
#15.MODEL层CURD的实现
首先我们在test库中先建立数据库表users
```cpp
CREATE TABLE users (
`id` INT(10) AUTO_INCREMENT ,
`username` VARCHAR(255) ,
`userpass` VARCHAR(255) ,
`create_time` INT(10),PRIMARY KEY (id));
INSERT INTO users (username,userpass,create_time) VALUES
('zhang','e10adc3949ba59abbe56e057f20f883e',1451515405),
('zhang2','e10adc3949ba59abbe56e057f20f883e',1451515406),
('zhang3','e10adc3949ba59abbe56e057f20f883e',1451515407),
('zhang4','e10adc3949ba59abbe56e057f20f883e',1451515408),
('zhang5','e10adc3949ba59abbe56e057f20f883e',1451515409);
```
这里是使用预处理语言来查询数据库,注意设置数据返回类型,数据如何进行报错。MODEL的CURD来了。
```cpp
|-core 框架核心目录
| |-Model.class.php 数据库操作类 业务逻辑层
<?php
/**
* 数据库操作类
* User: find35.com
* Date: 15/12/27
* Time: 下午4:52
*/
class Model
{
protected static $_instance;
protected static $_link;
protected $whereStr = '';//用来存储where条件
/**
* 单例模式 不允许在类外对类进行实例化
*/
private function __construct(){}
/**
* 获得类的实例
* @return mixed|Model
*/
public static function getStringleton(){
//判断我们类的实例是否存在,没有则创建之
if(!isset(self::$_instance)){
self::$_instance = new self();
}
//连接数据库
self::connect(HOST,UNAME,UPASS,DBNAME);
return self::$_instance;
}
/**
* 连接数据库方法
* @param $host 服务器地址
* @param $username 数据库账号
* @param $userpass 数据库密码
* @param $dbname 操作的数据库名
*/
protected static function connect($host,$username,$userpass,$dbname){
try{
self::$_link = new PDO("mysql:host={$host};dbname={$dbname}",$username,$userpass);
//设置返回数据的类型
self::$_link->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC);
//设置操作数据库的报错模式
// self::$_link->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_WARNING);
// if(self::$_link){
// echo '连接数据库成功';
// }
}catch (PDOException $e){
MyException::showError($e->getMessage());
}
}
/**
* 直接执行sql语句查询数据库的方法
* @param $sql mysql语句
* @param array $where 条件数据
* @return mixed 成功数组
*/
public function queryString($sql,$where=array()){
try{
//使用预处理语句来执行sql
$stmt = self::$_link->prepare($sql);
//判断是否有条件数组
if(empty($where)){
$stmt->execute();
}else{
$stmt->execute($where);
}
//判断执行是否成功
if($stmt->rowCount() > 0){
return $stmt->fetchAll();
}else {
//得到错误信息
$err = $stmt->errorInfo();
throw new PDOException($err[2]);
}
}catch(PDOException $e){
MyException::showError($e->getMessage());
}
}
/**
* 内部sql处理好的查询方法
* @param $table 表名
* @param array $where 查询条件
* @return mixed 成功返回数组
*/
public function select($table,$where = array()){
$sql = "select * from {$table} ";
if(!empty($this->whereStr)){
$sql .= $this->whereStr;
}
try{
//执行sql语句
$stmt = self::$_link->prepare($sql);
if(empty($where)){
$stmt->execute();
}else{
$stmt->execute($where);
}
//判断是否成功,如果不成功爆出异常
if($stmt->rowCount() > 0){
return $stmt->fetchAll();
}else{
$err = $stmt->errorInfo();
return $err[2];
}
}catch(PDOException $e){
MyException::showError($e->getMessage());
}
}
/**
* where条件方法
* @param string $whereStr
* @return $this
*/
public function where($whereStr = ''){
$this->whereStr = $whereStr;
return $this;//返回当前对象
}
/**
* 查询单条数据的方法
* @param $table 表名
* @param array $where 查询的条件,:key=value
* @return mixed 成功返回数组
*/
public function find($table,$where = array()){
$sql = "select * from {$table} ";
if(!empty($this->whereStr)){
$sql .= $this->whereStr;
}
try{
//执行sql
$stmt = self::$_link->prepare($sql);
if(empty($where)){
$stmt->execute();
}else{
$stmt->execute($where);
}
//判断是否成功
if($stmt->rowCount() > 0){
$result = $stmt->fetchAll();
return $result[0];
}else{
$err = $stmt->errorInfo();
throw new PDOException($err[2]);
}
}catch(PDOException $e){
MyException::showError($e->getMessage());
}
}
/**
* 添加单条数据的方法
* @param $table 表名
* @param array(':username'=>'zhang6',':userpass'=>md5(123456),':create_time'=>time()) $data
* @return int 成功返回1
*/
public function insert($table,array $data){
$sql = "insert into {$table} ";
$fields = "";
$values = "";
foreach($data as $k => $v){
$fields .= ltrim($k,":").",";
$values .= "'".ltrim($v,":")."',";
}
$sql .= "(".rtrim($fields,",").") values (".rtrim($values,",").")";
try{
//开启事务
self::$_link->beginTransaction();
$stmt = self::$_link->prepare($sql);
$stmt->execute($data);
if($stmt->rowCount() > 0){
self::$_link->commit();
return 1;
}else{
self::$_link->rollback();
$err = $stmt->errorInfo();
throw new PDOException($err[2]);
}
}catch(PDOException $e){
MyException::showError($e->getMessage());
}
}
/**
* 更新数据
* @param $table 表名
* @param array $data array(':username'=>'lisi',':userpass'=>md5(456789));
* @param array $where array(':id'=>9);
* @return int
*/
public function update($table,array $data,array $where){
$sql = "update {$table} set ";
$set_str = '';
foreach($data as $k => $v){
$set_str .= ltrim($k,":")."=$k,";
}
$sql .= rtrim($set_str,',').' '.$this->whereStr;
try{
self::$_link->beginTransaction();
$stmt = self::$_link->prepare($sql);
$data2 = array_merge($data,$where);
$stmt->execute($data2);
if($stmt->rowCount() > 0){
self::$_link->commit();
return 1;
}else{
self::$_link->rollback();
$err = $stmt->errorInfo();
throw new PDOException($err[2]);
}
}catch(PDOException $e){
MyException::showError($e->getMessage());
}
}
/**
* 删除数据方法
* @param $table 表名
* @param array $where
* @return int
*/
public function delete($table,array $where){
$sql = "delete from {$table} ".$this->whereStr;
try{
self::$_link->beginTransaction();
$stmt = self::$_link->prepare($sql);
$stmt->execute($where);
if($stmt->rowCount() > 0){
self::$_link->commit();
return 1;
}else{
self::$_link->rollback();
$err = $stmt->errorInfo();
throw new PDOException($err[2]);
}
}catch(PDOException $e){
MyException::showError($e->getMessage());
}
}
/**
* 析构方法
* 销毁对象
*/
public function __destruct(){
self::$_link = null;
}
}
```
然后我们开始进行测试
```cpp
|-index.php 项目前台入口文件,单一入口
<?php
/**
* 项目前台入口文件
* User: find35.com
* Date: 15/12/24
* Time: 下午5:15
*/
echo "<meta charset='utf-8'>";
require_once 'core/App.class.php';
require_once 'config/constants.php';
//注册一个
define('APP','app');
spl_autoload_register(array('App','myAutoloader'));
try{
App::run();
}catch(MyException $e){
$e->showError($e->getMessage());
}
$db = Model::getStringleton();
//直接执行sql语句
//$result = $db->queryString('select * from users where username=:username',array(':username'=>'zhang'));
//查询某个表
//$result = $db->select('users');
//查询某个表,并增加where条件
//$result = $db->where('where username=:username')->select('users',array(':username'=>'zhang'));
//查询单条数据
//$result = $db->where('where id=:id')->find('users',array(':id'=>'3'));
//插入数据
//$data = array(':username'=>'zhang6',':userpass'=>md5(123456),':create_time2'=>time());
//$result = $db->insert('users',$data);
//更新数据
//$data = array(':username'=>'lisi',':userpass'=>md5(456789));
//$where = array(':id'=>9);
//$result = $db->where('where id=:id')->update('users',$data,$where);
//删除数据
$result = $db->where('where id=:id')->delete('users',array('id'=>9));
echo "<pre>";
print_r($result);
```
如果结果都是1,那么表示都成功了。