滚动
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
通过这些修改,我们的数据库与模板系统就已成功连接。