abstrakt:本章通過手把手寫框架的學習,對MVC的模式有了進一步的認識,通過實踐,可以通過手寫框架實現(xiàn)數(shù)據(jù)的增刪改查。項目結構如下:本次框架主要包含三大目錄及其他文件:基類目錄。該目錄命名為pong。pong目錄下面包含core目錄、基類文件、配置文件、路由文件,其中,core目錄包含了controller、view、model三個基類。自己的應用項目。項目放在app目錄下,app目錄下設admin目錄、ho
本章通過手把手寫框架的學習,對MVC的模式有了進一步的認識,通過實踐,可以通過手寫框架實現(xiàn)數(shù)據(jù)的增刪改查。項目結構如下:
本次框架主要包含三大目錄及其他文件:
基類目錄。該目錄命名為pong。pong目錄下面包含core目錄、基類文件、配置文件、路由文件,其中,core目錄包含了controller、view、model三個基類。
自己的應用項目。項目放在app目錄下,app目錄下設admin目錄、home目錄、Model目錄,admin目錄主要用于存放后臺管理頁面的數(shù)據(jù),里面包含controller和view兩個文件夾,分別存放具體頁面對應的控制器文件和視圖文件。home目錄與admin目錄的架構類型類似,主要用于存放前臺的文件。Model目錄,主要用于存放系統(tǒng)公用的model類,因為model類是可以給前臺和后臺公用,所以獨立到一個文件夾中。
模板框架目錄。該目錄主要是通過composer下載的模板框架和數(shù)據(jù)庫框架。
.htaccess文件。該文件主要用于頁面的重定向。
composer.json和composer.lock文件。這兩個文件主要是通過composer獲取框架后自動生成的文件,其中composer.lock存放了框架的基礎配置,如框架版本。
Index.php文件。該文件為入口文件,通過.htaccess文件配置的重定向,默認訪問域名時,即進入該文件。
框架源碼:
入口文件index.php
<?php /** * 入口文件 */ //導入模板框架 require __DIR__.'/vendor/autoload.php'; //導入基礎類 require __DIR__.'/pong/Base.php'; //獲取配置文件信息 $config=require __DIR__.'/pong/Config.php'; //定義根目錄路徑 define('ROOT_PATH',__DIR__.'/'); //獲取查詢字符串 $queryStr=$_SERVER["QUERY_STRING"]; //啟動框架 (new \pong\Base($config,$queryStr))->run();
基礎類Base.php:
<?php /** * 基類 */ namespace pong; class Base { //配置數(shù)組 protected $config=[]; //查詢字符串 protected $queryStr=[]; public function __construct($config,$queryStr='') { $this->config=$config; $this->queryStr=$queryStr; } //設置調(diào)試狀態(tài) public function setDebug() { //如果調(diào)試模式為true,則開啟調(diào)試模式,否則關閉調(diào)試模式 if ($this->config['app']['debug']) { error_reporting(E_ALL); ini_set('display_errors','On'); } else { ini_set('display_errors','Off'); ini_set('log_errors', 'On'); } } //設置自動加載器 public function loader($class) { //把參數(shù)格式進行轉(zhuǎn)換 $path = ROOT_PATH . str_replace('\\','/',$class) . '.php'; //判斷文件是否存在 if(!file_exists($path)) { header('Location:/');//返回根目錄 } //加載文件 require_once $path; } //啟動框架 public function run() { //設置調(diào)試模式 $this->setDebug(); //自動加載 spl_autoload_register([$this,'loader']); //請求分發(fā) $route=new Route($this->config['route']); echo $route->parse($this->queryStr)->dispatch(); } }
配置基類config.php
<?php /** * 配置文件 */ return [ //應用配置 'app'=>[ //調(diào)試開關 'debug'=>true ], //路由配置 'route'=>[ //默認模塊 'module'=>'admin', //默認控制器 'controller'=>'Index', //默認操作 'action'=>'index' ], 'db'=>[ // 數(shù)據(jù)庫類型 'database_type' => 'mysql', // 默認數(shù)據(jù)庫名稱 'database_name' => 'test', // 默認主機名 'server' => '127.0.0.1', // 默認用戶名 'username' => 'root', // 用戶密碼 'password' => 'root', // 編碼 'charset' => 'utf8', //端口 'port' => 3306, ] ];
路由基類Route.php:
<?php /**路由類*/ namespace pong; class Route { protected $route=[]; protected $passInfo=[]; protected $params=[]; public function __construct($route) { $this->route=$route; } /** * @param string $queryStr 鏈接參數(shù) * @return $this 當前類的實例 */ public function parse($queryStr='') { //去除字符串左右的/,并將參數(shù)全部轉(zhuǎn)為小寫 $queryStr=strtolower(trim($queryStr,'/')); //通過/把字符串轉(zhuǎn)成數(shù)組 $queryArr=explode('/',$queryStr); //數(shù)組過濾掉空字符 $queryArr=array_filter($queryArr,function($value){ return trim($value)!=''; },ARRAY_FILTER_USE_BOTH); //判斷數(shù)組中有幾個參數(shù) switch (count($queryArr)) { case 0: $this->passInfo = $this->route; break; case 1: $this->passInfo['module']=$queryArr[0]; break; case 2: $this->passInfo['module']=$queryArr[0]; $this->passInfo['controller']=$queryArr[1]; break; case 3: $this->passInfo['module']=$queryArr[0]; $this->passInfo['controller']=$queryArr[1]; $this->passInfo['action']=$queryArr[2]; break; default: $this->passInfo['module']=$queryArr[0]; $this->passInfo['controller']=$queryArr[1]; $this->passInfo['action']=$queryArr[2]; //從第四個元素開始遍歷,即過濾掉路由的信息 for($i=3;$i<count($queryArr);$i+=2) { if(isset($queryArr[$i+1])) { $this->params[$queryArr[$i]]=$queryArr[$i+1]; } } break; } //返回當前對象 return $this; } public function dispatch() { //模塊名 $module = $this->passInfo['module']; //控制器名 $controller='\app\\' .$module . '\controller\\' . ucfirst($this->passInfo['controller']) ; //操作 $action= $this->passInfo['action']; // echo $controller , ' ' , $action; // return; //如果找不到,重新返回根目錄 if(!method_exists($controller,$action)) { header('location:/'); } return call_user_func_array([new $controller,$action],$this->params); } } //測試 //$config=require __DIR__.'/Config.php'; //$route=new Route($config['route']); ////測試請求分發(fā) //require __DIR__ . '/../app/admin/controller/Index.php'; //print_r($route->parse($_SERVER['QUERY_STRING'])->dispatch()) ;
控制器基類Controller.php
<?php /**控制器基類*/ namespace pong\core; class Controller { protected $view=null; public function __construct() { $this->view=new namespace\View(); $this->config(); } //配置方法 public function config() { //設置后臺模板目錄的命名空間 $this->view->addFolder('admin', ROOT_PATH.'/app/admin/view'); //設置后臺模板目錄的命名空間 $this->view->addFolder('home', ROOT_PATH.'/app/home/view'); } //模板賦值 public function assign($name, $value) { $this->data[$name] = $value; } //渲染模板 public function fetch($file) { //1.將容器中的關聯(lián)數(shù)據(jù)轉(zhuǎn)為變量:鍵名->變量名,元素值->變量值,返回變量個數(shù) extract($this->data); //2. 將模板加載到當前控制器方法對應的腳本本 require $file; } }
視圖基類view.php
<?php /*視圖基類**/ namespace pong\core; use League\Plates\Engine; class View extends Engine { public function __construct($directory = null, $fileExtension = 'php') { parent::__construct($directory, $fileExtension); } }
模型基類Controller.php
<?php /**模型基類*/ namespace pong\core; use Medoo\Medoo; class Model extends Medoo { public function __construct($option=null) { $config=require __DIR__.'/../Config.php'; $option=$config['db']; parent::__construct($option); } }
樣式文件style.css
table { border-collapse: collapse; width: 60%; text-align: center; margin: 30px auto; } table, th, td { border: 1px solid black; } table tr th { background-color: lightblue } table tr:hover { background-color: antiquewhite; color: green; } table caption { font-size: 1.5em; font-weight: bolder; margin-bottom: 10px; } a { text-decoration-line: none; color: green; } table tr td:last-child a:last-child { color: red; } p { text-align: center; } form { text-align: center }
以下為項目主目錄app文件:
admin/controller/Index.php
<?php namespace app\admin\controller; use app\model\User; use pong\core\Controller; class Index extends Controller { public function __construct() { parent::__construct(); } public function test() { $name='1pong'; $this->assign('name',$name); $this->fetch(__DIR__ . '/../view/index/test.php'); } //管理員登錄 public function login() { if ($_SERVER['REQUEST_METHOD'] == 'POST') { //驗證用戶 $res = (new User)->get('admin', ['name'],[ 'AND' => [ 'email' => $_POST['email'], 'psword' => $_POST['psword'], ] ]); if (empty($res)) { echo "<script>alert('郵箱或密碼不正確');location.href='/';</script>"; } else { $_SESSION['name'] = $res['name']; echo "<script>alert('登陸成功');location.href='/';</script>"; } } } //管理員退出登錄 public function logout() { session_destroy(); echo "<script>alert('退出成功');location.href='/';</script>"; } public function index() { // exit('aaa'); $rows=(new User())->select('user',['uid','name','phone','weight','height','add_time'],[ //搜索條件 'name[~]'=> isset($_POST['name']) ? $_POST['name'] : null ]); //print_r($rows); return $this->view->render('admin::index/index',[ 'rows'=>$rows, 'title'=>'用戶管理', 'loginUrl' => '/admin/index/login', 'logoutUrl' => '/admin/index/logout', 'indexUrl' => '/admin/index/index', 'insUrl' => '/admin/index/insert', 'editUrl' => '/admin/index/edit', 'delUrl' => '/admin/index/delete', ]); } //添加數(shù)據(jù) public function insert() { return $this->view->render('admin::index/insert', [ 'title' => '添加用戶', 'url' => '/admin/index/add' ]); } //執(zhí)行添加操作 public function add() { if ($_SERVER['REQUEST_METHOD'] == 'POST') { //執(zhí)行添加操作 (new User())->insert('user', [ 'name'=>$_POST['name'], 'phone'=>$_POST['phone'], 'weight'=>$_POST['weight'], 'height'=>$_POST['height'], 'add_time'=>time(), ]); echo "<script>alert('添加成功');location.href='/';</script>"; } } //填充需編輯的數(shù)據(jù) public function edit($id='') { $row = (new User())->get('user',['uid','name','phone','weight','height'],['uid'=>$id]); return $this->view->render('admin::index/edit', [ 'title' => '編輯用戶', 'url' => '/admin/index/save', 'row' => $row, ]); } //保存操作 public function save($id) { //判斷請求類型,post提交的才執(zhí)行 if ($_SERVER['REQUEST_METHOD'] == 'POST') { //執(zhí)行更新操作 (new User())->update('user', [ 'name' => $_POST['name'], 'phone' => $_POST['phone'], 'weight'=>$_POST['weight'], 'height'=>$_POST['height'], ], ['uid' => $id]); //提示用戶信息 echo "<script>alert('更新成功');location.href='/';</script>"; } } //執(zhí)行刪除操作 public function delete($uid) { //執(zhí)行刪除操作 (new User())->delete('user',['uid'=>$uid]); //提示用戶信息 echo "<script>alert('刪除成功');location.href='/';</script>"; } }
admin/controller/View/index.php:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <link rel="stylesheet" href="/static/css/style.css"> <title><?=$this->e($title); ?></title> </head> <body> <?php session_start();?> <?php isset($_SESSION['name'])?$_SESSION['name']:'' ?> <?php if(isset($_SESSION['name']) && $_SESSION['name'] =='admin'): ?> <p>歡迎: <?=$this->e($_SESSION['name'])?> | <a href="<?=$this->e($logoutUrl)?>">退出</a> </p> <?php else: ?> <form action="<?=$this->e($loginUrl)?>" method="post"> 郵箱: <input type="email" name="email" required> 密碼: <input type="password" name="psword" required> <button>登錄</button> </form> <?php endif; ?> <table> <caption>日本女明星展示表 <?php if(isset($_SESSION['name']) && $_SESSION['name'] =='admin'): ?> <small><a href="<?=$this->e($insUrl)?>">添加</a></small> <?php endif; ?> </caption> <tr><th>ID</th><th>姓名</th><th>手機號</th><th>胸圍</th><th>身高</th><th>注冊時間</th> <?php if(isset($_SESSION['name']) && $_SESSION['name'] =='admin'): ?> <th>操作</th> <?php endif; ?> </tr> <?php foreach ($rows as $row) : ?> <tr> <td><?=$this->escape($row['uid'])?></td> <td><?php echo $this->escape($row['name']); ?></td> <td><?php echo $this->e($row['phone']); ?></td> <td><?php echo $this->e($row['weight']); ?></td> <td><?=$this->e($row['height'])?></td> <td><?=$this->e(date('Y-m-d H:i:s',$row['add_time']))?></td> <?php if(isset($_SESSION['name']) && $_SESSION['name'] =='admin'): ?> <td><a href="<?=$this->e($editUrl)?>/uid/<?=$this->e($row['uid'])?>">編輯</a> | <a href="javascirpt:;" onclick="del(<?=$this->e($row['uid'])?>);return false">刪除</a> </td> <?php endif; ?> </tr> <?php endforeach; ?> </table> <!-- 搜索功能--> <form action="<?=$this->e($indexUrl)?>" method="post"> 女星名稱: <input type="text" name="name" placeholder="輸入女星關鍵字"> <button>搜索</button> </form> <!--刪除操作--> <script> function del(uid) { if (confirm('是否刪除 id='+uid+' 的記錄?')) { location.href = '/admin/index/delete/uid/'+uid } } </script> </body> </html>
admin/controller/View/edit.php:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title><?=$this->e($title) ?></title> </head> <body> <style> table { border:1px solid lightgrey; background-color: lightcyan; border-radius: 3%; box-shadow: 2px 2px 2px #888; padding: 20px; width: 300px; height: 250px; margin:30px auto; } table caption { font-size: 1.3em; margin-bottom: 10px; } table tr td:first-child {text-align: center} table tr td button { width: 100px; height: 30px; background-color: green; color: white; border: none; } table tr td button:hover {cursor: pointer;background-color: lightcoral} </style> <form action="<?=$this->e($url)?>/uid/<?=$this->e($row['uid'])?>" method="post"> <table> <caption>編輯女明星</caption> <tr> <td><label for="name"></label>女星名稱:</td> <td><input type="text" name="name" id="name" value="<?=$this->e($row['name'])?>"></td> </tr> <tr> <td><label for="phone"></label>手機號:</td> <td><input type="text" name="phone" id="phone" value="<?=$this->e($row['phone'])?>"></td> </tr> <tr> <td><label for="weight"></label>胸圍:</td> <td><input type="text" name="weight" id="weight" value="<?=$this->e($row['weight'])?>"></td> </tr> <tr> <td><label for="height"></label>身高:</td> <td><input type="text" name="height" id="height" value="<?=$this->e($row['height'])?>" ></td> </tr> <tr> <td colspan="2" align="center"><button>提交</button></td> </tr> </table> </form> </body> </html>
admin/controller/View/insert.php:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title><?=$this->e($title) ?></title> </head> <body> <style> table { border:1px solid lightgrey; background-color: lightcyan; border-radius: 3%; box-shadow: 2px 2px 2px #888; padding: 20px; width: 300px; height: 250px; margin:30px auto; } table caption { font-size: 1.3em; margin-bottom: 10px; } table tr td:first-child {text-align: center} table tr td button { width: 100px; height: 30px; background-color: green; color: white; border: none; } table tr td button:hover {cursor: pointer;background-color: lightcoral} </style> <form action="<?=$this->e($url)?>" method="post"> <table> <caption>添加女明星</caption> <tr> <td><label for="name"></label>女星名稱:</td> <td><input type="text" name="name" id="name" required></td> </tr> <tr> <td><label for="phone"></label>手機號:</td> <td><input type="text" name="phone" id="phone"></td> </tr> <tr> <td><label for="weight"></label>胸圍:</td> <td><input type="text" name="weight" id="weight" ></td> </tr> <tr> <td><label for="height"></label>身高:</td> <td><input type="text" name="height" id="height"></td> </tr> <tr> <td colspan="2" align="center"><button>提交</button></td> </tr> </table> </form> </body> </html>
效果如下:
Korrigierender Lehrer:天蓬老師Korrekturzeit:2019-02-18 17:21:01
Zusammenfassung des Lehrers:不論再大的項目, 都是由這些基本的功能組成, 理解了開發(fā)流程, 就好辦了, 這個小框架雖然不具備商業(yè)使用價值,但是用來學習框架開發(fā)入門,足夠了,如果你能完整的寫下來, 再不看教程的情況,自己獨立完成一遍, 那么你的原生php開發(fā)水平, 基本上可以勝任一個程序員的日常工作了