滚动
1.5. 引入用于操作数据库和模板的类
我们已经为框架创建了结构,现在是时候考虑如何存储数据,例如新闻、商品等。用于操作数据库的对象需要具备以下功能:
- 管理数据库连接
- 提供对数据库的轻度抽象
- 缓存查询结果
- 简化常用数据库操作
为此,我们创建一个文件 Registry/objects/db.class.php:
<?php /** * 数据库管理类 * 提供对数据库的轻度抽象 */ class database { private $connections = array(); private $activeConnection = 0; private $queryCache = array(); private $dataCache = array(); private $last; public function __construct() {} public function newConnection( $host, $user, $password, $database ) { $this->connections[] = new mysqli( $host, $user, $password, $database ); $connection_id = count( $this->connections )-1; if( mysqli_connect_errno() ) { trigger_error('连接数据库出错: '.$this->connections[$connection_id]->error, E_USER_ERROR); } return $connection_id; } public function closeConnection() { $this->connections[$this->activeConnection]->close(); } public function setActiveConnection( int $new ) { $this->activeConnection = $new; } public function cacheQuery( $queryStr ) { if( !$result = $this->connections[$this->activeConnection]->query( $queryStr ) ) { trigger_error('执行并缓存查询出错: '.$this->connections[$this->activeConnection]->error, E_USER_ERROR); return -1; } else { $this->queryCache[] = $result; return count($this->queryCache)-1; } } public function numRowsFromCache( $cache_id ) { return $this->queryCache[$cache_id]->num_rows; } public function resultsFromCache( $cache_id ) { return $this->queryCache[$cache_id]->fetch_array(MYSQLI_ASSOC); } public function cacheData( $data ) { $this->dataCache[] = $data; return count( $this->dataCache )-1; } public function dataFromCache( $cache_id ) { return $this->dataCache[$cache_id]; } public function deleteRecords( $table, $condition, $limit ) { $limit = ( $limit == '' ) ? '' : ' LIMIT ' . $limit; $delete = "DELETE FROM {$table} WHERE {$condition} {$limit}"; $this->executeQuery( $delete ); } public function updateRecords( $table, $changes, $condition ) { $update = "UPDATE " . $table . " SET "; foreach( $changes as $field => $value ) { $update .= "`" . $field . "`='{$value}',"; } $update = substr($update, 0, -1); if( $condition != '' ) { $update .= "WHERE " . $condition; } $this->executeQuery( $update ); return true; } public function insertRecords( $table, $data ) { $fields = ""; $values = ""; foreach ($data as $f => $v) { $fields .= "`$f`,"; $values .= ( is_numeric( $v ) && ( intval( $v ) == $v ) ) ? $v."," : "'$v',"; } $fields = substr($fields, 0, -1); $values = substr($values, 0, -1); $insert = "INSERT INTO $table ({$fields}) VALUES({$values})"; $this->executeQuery( $insert ); return true; } public function executeQuery( $queryStr ) { if( !$result = $this->connections[$this->activeConnection]->query( $queryStr ) ) { trigger_error('执行查询出错: '.$this->connections[$this->activeConnection]->error, E_USER_ERROR); } else { $this->last = $result; } } public function getRows() { return $this->last->fetch_array(MYSQLI_ASSOC); } public function affectedRows() { return $this->connections[$this->activeConnection]->affected_rows; } public function sanitizeData( $data ) { return $this->connections[$this->activeConnection]->real_escape_string( $data ); } public function __deconstruct() { foreach( $this->connections as $connection ) { $connection->close(); } } } ?>
在连接数据库之前,我们先看一下这个类的功能。我们可以通过类方法轻松地执行增删改操作:
// 插入数据 $registry->getObject('db')->insertRecords( 'products', array('name'=>'杯子' ) ); // 更新数据 $registry->getObject('db')->updateRecords( 'products', array('name'=>'红色杯子' ), 'ID=2' ); // 删除数据 $registry->getObject('db')->deleteRecords( 'products', "name='红色杯子'", 5 );
此外,该类还支持查询缓存。
接着我们添加一个用于模板管理的类 Registry/objects/template.class.php:
<?php if ( ! defined( 'FW' ) ) { echo '此文件只能从 index.php 调用,不能直接访问'; exit(); } /** * 模板处理类 */ class template { private $page; public function __construct() { include( APP_PATH . '/Registry/objects/page.class.php'); $this->page = new Page(); } public function addTemplateBit( $tag, $bit ) { if( strpos( $bit, 'Views/' ) === false ) { $bit = 'Views/Templates/' . $bit; } $this->page->addTemplateBit( $tag, $bit ); } private function replaceBits() { $bits = $this->page->getBits(); foreach( $bits as $tag => $template ) { $templateContent = file_get_contents( $template ); $newContent = str_replace( '{' . $tag . '}', $templateContent, $this->page->getContent() ); $this->page->setContent( $newContent ); } } private function replaceTags() { $tags = $this->page->getTags(); foreach( $tags as $tag => $data ) { if( is_array( $data ) ) { if( $data[0] == 'SQL' ) { $this->replaceDBTags( $tag, $data[1] ); } elseif( $data[0] == 'DATA' ) { $this->replaceDataTags( $tag, $data[1] ); } } else { $newContent = str_replace( '{' . $tag . '}', $data, $this->page->getContent() ); $this->page->setContent( $newContent ); } } } public function parseTitle() { $newContent = str_replace('<title>', '<title>'. $this->page->getTitle(), $this->page->getContent() ); $this->page->setContent( $newContent ); } public function parseOutput() { $this->replaceBits(); $this->replaceTags(); $this->parseTitle(); } public function getPage() { return $this->page; } } ?>
模板类引用了 Page 对象,因此我们需要在 Registry/objects/page.class.php 中定义它:
<?php /** * 页面类 * 可扩展为支持 JS/CSS、密码保护页面等功能 */ class page { private $title = ''; private $tags = array(); private $bits = array(); private $content = ""; public function getTitle() { return $this->title; } public function setTitle( $title ) { $this->title = $title; } public function setContent( $content ) { $this->content = $content; } public function addTag( $key, $data ) { $this->tags[$key] = $data; } public function getTags() { return $this->tags; } public function addTemplateBit( $tag, $bit ) { $this->bits[ $tag ] = $bit; } public function getBits() { return $this->bits; } public function getContent() { return $this->content; } } ?>
现在我们有了数据库类和模板类,需要在注册器中注册它们。在 Registry/registry.class.php 中添加方法:
public function storeCoreObjects() { $this->storeObject('database', 'db' ); $this->storeObject('template', 'template' ); }
然后我们创建用户表 users,包含字段 id、name、email,并编写主页面模板 Views/Templates/main.tpl.php:
<html> <head> <title> Powered by PCA Framework</title> </head> <body> <h1>Our Members</h1> <p>Below is a list of our members:</p> <ul> <!-- START members --> <li>{name} {email}</li> <!-- END members --> </ul> </body> </html>
接下来修改 index.php 以加载模板和数据库连接:
<?php session_start(); error_reporting(E_ALL); define( "APP_PATH", dirname( __FILE__ ) ."/" ); define( "FW", true ); function __autoload( $class_name ) { require_once('Controllers/' . $class_name . '/' . $class_name . '.php' ); } require_once('Registry/registry.class.php'); $registry = Registry::singleton(); $registry->storeCoreObjects(); $registry->getObject('db')->newConnection('localhost', 'root', '', 'framework'); $registry->getObject('template')->buildFromTemplates('main.tpl.php'); $cache = $registry->getObject('db')->cacheQuery('SELECT * FROM users'); $registry->getObject('template')->getPage()->addTag('users', array('SQL', $cache) ); $registry->getObject('template')->getPage()->setTitle('Our users'); $registry->getObject('template')->parseOutput(); print $registry->getObject('template')->getPage()->getContent(); print $registry->getFrameworkName(); exit(); ?>
如果一切正常并且数据库中有用户数据,页面将显示如下:
如果出现错误,可以参考 GitHub 上的可运行代码。以下是修复示例:
- 修改数据库类名为 db
- 修正模板类中标题替换的 bug
通过这些修改,我们的数据库与模板系统就已成功连接。