🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
#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,那么表示都成功了。