正如前面提到的,PHP 提供了许多 mysqli 函数。本节只解释那些用来实现银行场景的函数。具体地说,这里讨论连接、读、写和释放连接等数据库函数。更多细节请参考 参考资料 中列出的 PHP 官方文档。
在执行任何数据库操作之前,必须建立到数据库服务器的连接。在 PHP 中,使用以下函数连接数据库:
$link = mysqli_connect("hostname", "user", "password", "dbname");
这个函数尝试用正确的凭证连接位于指定主机上的指定数据库。这个函数还接受其他可选参数(参见 PHP 文档)。如果成功地连接到数据库,这个函数会返回一个连接对象,后续的数据库读写操作需要使用这个对象。如果无法建立数据库连接,那么这个函数返回 false。如果不希望在代码中以明文形式直接嵌入数据库服务器密码,那么可以考虑使用 MySQL 的密码摘要特性。
与连接操作对应的函数是数据库关闭函数:
$result = mysqli_close($link);
这个函数尝试关闭以前打开的数据库连接。它以 MySQL 数据库连接对象作为参数;如果数据库关闭操作成功,它就返回 true,否则返回 false。
在讨论了连接管理函数之后,我们来看看数据库读/写操作涉及的函数。下面是读/写操作涉及的一些函数:
(1) $result = mysqli_query($link, $queryStr);
(2) $numberOfRows = mysqli_num_rows($result);
(3) $row = mysqli_fetch_assoc($result);
(4) $row = mysqli_fetch_object($result);
函数 (1) 对数据库执行一个 SQL 查询。它的参数是连接对象和一个有效的 SQL 查询字符串。它在查询成功时返回 true,在失败时返回 false。但是,对于 SELECT SQL 查询,这个函数返回一个结果对象。
函数 (2) 计算一个 SQL 查询返回的结果集中的行数。它的参数是结果对象。
函数 (3) 以 PHP 关联数组的形式返回结果行。这个数组以数据库列名作为数组键,以数据库字段值作为数组值。例如,如果一个数据库查询返回 “capital” 列的值,那么可以通过引用数组来访问结果:
$stateCapital = $row['capital'];
函数 (4) 以 PHP 对象的形式返回结果行。这个对象以数据库列名作为对象属性,以数据库字段值作为对象属性值。例如,如果一个数据库查询返回 “park” 列的值,那么可以通过引用对象值来访问结果:
$nationalPark = $row->park;
函数 (3) 和 (4) 可以非常简便地将数据库值映射到程序变量。正如前面提到的,PHP 还提供了执行数据库值映射的其他方法。
在我们的场景中,在执行数据库操作之后,要使用另外两个函数进行错误处理。了解数据库操作是否成功执行是非常重要的。下面两个函数可以方便地处理这个问题:
$returnCode = mysqli_errno($link);
$errorMsg = mysqli_error($link);
mysqli_errno 函数返回最近的函数调用的错误码。它的参数是连接对象,它返回已经执行的函数的返回码。返回码为 0 就说明没有错误。mysqli_error 函数返回最近的错误的字符串描述。这两个函数对于完成数据库查询的整个处理过程是非常重要的
本文来自编程入门网:http://www.bianceng.cn/webkf/ajax/201007/18672_4.htm
在这个分三部分的系列的 第 1 部分 中,了解了 Firefox、Zend Core 和 MySQL 等开放源码技术的重要特性。我们讨论了一个比较复杂的银行场景,这个场景跨越 Ajax 应用程序的所有三层。还设置了开发端到端 Ajax 应用程序所需的数据库服务器、中间层服务器和基于 Eclipse 的 IDE。在本系列的第 2 部分中,将开发银行场景的一些部分。具体地说,将使用 MySQL 数据库创建一个后端数据库。我们将研究几个 MySQL 命令行工具,并使用这些工具连接数据库、在数据库中创建、定义和填充与银行相关的数据。然后,开发一个中间层 PHP 模块来提供银行的业务逻辑,这个模块使用 ODBC 连接 MySQL 数据库。最后,开发一个银行门户,用户可以通过这个简单的浏览器用户界面与这个端到端应用程序进行交互,这个应用程序不久之后就可以在 Zend Core 上运行。
简介
正如 第 1 部分 所指出的,这个银行场景主要提供银行出纳员执行的基本服务。如果您还不了解这个场景,请阅读 第 1 部分。客户数据是这个场景的重要部分。对于本系列的场景,所有客户数据将同时填充进数据库表中。在此之后,可以通过 Zend Core 提供的 ODBC MySQL 驱动程序获取和更新存储的客户数据。处理了客户数据之后,重点转移到提供银行出纳功能所需的核心银行逻辑。我们开发一个 PHP 代码模块来提供核心银行逻辑,并使用 ODBC 进行必要的数据库访问。使用 Zend Core 和 PHP 实现银行逻辑的主要优点是,可以利用内置的 MySQL 支持。
在建立数据库并开发 PHP 模块之后,为银行出纳员提供执行四个核心功能的用户界面。我们通过一个瘦客户机访问 PHP 模块中封装的核心银行逻辑。具体地说,这个基于 Web 的瘦客户机是按照 Ajax 风格生成的:XHTML、Cascading Style Sheet(CSS)、JavaScript 和 XMLHttpRequest(XHR)。它为银行出纳员提供执行核心银行功能的简单用户界面。这个浏览器用户界面还演示浏览器客户机逻辑与服务器端 PHP 逻辑进行网络通信的方法。
到本文结束时,我们会建立数据库、一个提供核心银行逻辑的 PHP 模块以及一个单页面浏览器用户界面,这些都是银行场景的组成部分。
MySQL 数据库
正如在 第 1 部分 中指出的,MySQL 是一种开放源码数据库。在我们的场景中,使用社区服务器版本,这是一个紧凑的数据库服务器,具有许多有用的特性。因为这个银行场景的实现基于开放源码产品,所以 MySQL 和 Zend Core PHP 是合适的组合。Zend Core 本身支持 MySQL,还有各种支持 MySQL 管理和编程的工具。在我们的场景中,只使用 MySQL 命令行客户机执行 MySQL 的管理。我们将用 MySQL 数据库为这个场景建立银行帐户数据库。
创建并填充银行数据库
在这个场景中,将为给定的客户存储以下帐户信息:
AccountHolderName
AccountNumber
CheckingBalance
StockName
StockQuantity
StockValue
给定客户的帐户信息包括帐户持有人的姓名、帐户号、当前的资产余额、客户拥有的一只股票的编号、拥有的股票总数以及股票投资组合的当前市值。下面几节详细介绍如何创建数据库表,然后用一些虚构的银行客户的帐户信息填充这个表。我们开始吧!
按照以下步骤创建数据库,然后用应用程序相关数据填充这个表:
如果 Eclipse 还未运行的话,就启动它(c:eclipseeclipse.exe)。
确保在 Eclipse 中启用了 PHP 透视图:
选择 Window->Open Perspective->Other->PHP 并单击 OK。
在 Eclipse 中,选择 File->New->Project。
选择 General->Project 并单击 Next。
在 project name 字段中输入 BankDB。
单击 Finish。
右击 BankDB project 并选择 New->Other。
选择 General-> File 并单击 Next。
在 File name 字段中,输入 BankDB.sql 并单击 Finish。
输入或粘贴 清单 1 中的代码作为 BankDB.sql 的内容。
保存并关闭这个文件。
为了启动 MySQL 命令行客户机,单击 Windows Start Menu->All Programs->MySQL->MySQL Server->MySQL Command Line Client。
在 MySQL 命令行窗口中,输入密码 webtech 并按 Enter。
在 mysql> 提示下,输入 source c:eclipseworkspaceBankDBBankDB.sql 并按 Enter。
检查 BankDB 数据库是否存在,以此确认前面的命令已经正确地执行了。检查所用的命令如下:
show databases;
use bankdb;
show tables;
describe account;
清单 1. BankDB.sql 文件的内容
使用 PHP 访问 MySQL 数据库
PHP 最受人喜爱的特性之一是,它为访问不同厂商的数据库中的数据提供了简单且出色的支持,包括 MySQL。它提供了一种集成业务逻辑和数据库的简便有效的方法。在过去几年中,PHP 社区完成了几项改进,比如 PHP Data Objects(PDO),PDO 提供一个抽象层,无论使用哪种数据库服务器,这个抽象层都公开同样的 API 函数。有两种风格的 PHP 数据库 API 函数:过程式的和面向对象的。在这个场景中,我们使用针对 MySQL 的直接数据库 API 来获得一个基本的了解。PHP 提供两种访问 MySQL 的方法:
MySQL
MySQL Improved
常规的 MySQL 扩展不完全支持 MySQL 4.1.0 的所有功能。这些特性包括存储过程、触发器和视图。MySQL Improved(mysqli)扩展是访问这些功能的最新方式。在 PHP 5.0 或更高版本中,可以使用 mysqli 的扩展。但是,mysqli 在默认情况下没有启用 —— 必须使用 Zend Core 管理工具启用它。在这个系列的 第 1 部分 中,在安装和配置 Zend Core 时已经启用了 mysqli。
PHP 数据库 API 的出色特性之一是,它们可以将数据库操作的结果以许多不同方式映射到 PHP 数据结构。换句话说,PHP 中的数据库 API 可以以 PHP 关联数组的形式返回数据库结果,其中数据库表的列名作为数组的键。在另一种情况下,它可以以 PHP 类对象的形式返回结果,其中数据库表的列名作为对象的属性。因此可以以方便的方式表示数据库查询的结果
在 PHP 模块中实现银行业务逻辑
按照以下步骤创建一个 PHP 模块,并实现中间层业务逻辑:
切换到 Eclipse PHP 透视图:单击 Window->Open Perspective->Other->PHP 并单击 OK。
单击 File->New->PHP Project:
在 Project name 字段中,输入 BankTeller。
单击 Finish。
在 Project Explorer 视图中,右击 BankTeller 项目并选择 New->PHP File:
在 File Name 字段中,输入 BankLogic.php 并单击 Finish。
输入或粘贴 清单 2 中的源代码作为这个文件的内容。
单击 File->Save 保存文件。
通过这个文件中的注释了解代码的作用,或者参考下一节中对代码逻辑的解释。
可以看出在 PHP 中执行数据库操作是多么简便。您可能会注意到,在清单 2 中密码是明文形式的。这个密码只用在为本文创建的测试数据库中。可以使用密码摘要替代明文,从而进一步改进这个 PHP 模块。
account transaction.
This function takes the account holder name, an amount and
the transaction type as function parameter.
The transaction type is 1 for deposit into checking account.
The transaction type is 2 for debit from a checking account.
After the transaction is done, it returns the snapshot of
the account before this transaction and another snapshot of
the account after this transaction.
============================================================
*/
function accountTransaction($accountHolderName, $amount, $transactionType) {
// We will use these globally scoped variables.
global $link, $finalResult, $dbResult;
// Get a database connection.
$result = connect_to_db();
// If the connection failed, return now.
if ($result == false) {
return ($finalResult);
} // End of if ($result == false)
// Make an SQL statement to read a particular account data for a
// given account holder name.
$queryStr = "Select * from account where AccountHolderName='$accountHolderName'";
// Perform the SQL query.
$dbResult = mysqli_query($link, $queryStr);
// Did you encounter a DB error?
if (mysqli_errno($link)) {
// Close the connection now.
close_connection_to_db();
// Set the DB error message.
$resultMsg = "DB read error: " . mysqli_errno($link) .
" [" . mysqli_error($link) . "]";
// Set the application-specific result code.
$finalResult["ResultCode"] = 1;
// Set the result message.
$finalResult["ResultMsg"] = $resultMsg;
// Return the associative array with the error result.
return ($finalResult);
} // End of if (mysqli_errno($link))
// How many rows were read from the DB?
$rowCount = mysqli_num_rows($dbResult);
// If no rows are obtained from DB, return now.
if ($rowCount <= 0) {
// Close the connection now.
close_connection_to_db();
// Setup the return value as not a successful one.
$finalResult["ResultCode"] = 1;
$finalResult["ResultMsg"] = "No accounts were found in BankDB.";
// Return the final result.
return ($finalResult);
} // End of if ($rowCount <= 0)
// Store a record with the current checking balance.
// Create an array to hold the pre-transaction and
// post-transaction account snapshots.
$accountInfo = array();
// Fetch the query result as an associative array.
$row = mysqli_fetch_assoc($dbResult);
$data = null;
// Record the filed values from the database row into
// a new associative array.
$data["AccountHolderName"] = $row["AccountHolderName"];
$data["AccountNumber"] = $row["AccountNumber"];
$data["PreviousCheckingBalance"] = doubleVal($row["CheckingBalance"]);
$data["StockName"] = $row["StockName"];
$data["StockQuantity"] = intval($row["StockQuantity"]);
$data["StockValue"] = doubleVal($row["StockValue"]);
// Store the pre-transaction account snapshot into a regular array.
$accountInfo[0] = $data;
// Initialize the variables.
$newBalance = 0.0;
// Since we are doing a transaction that changes the
// checking balance, we need to update the DB later.
$updateDB = true;
if ($transactionType == 1) {
// It is deposit.
// Ensure that user wants to deposit an amount greater than 0.
if ($amount > 0) {
$newBalance = doubleval($data["PreviousCheckingBalance"]) + $amount;
} else {
// Deposit amount is 0. There is no need to do a database update.
$updateDB = false;
$newBalance = doubleval($data["PreviousCheckingBalance"]);
} // End of if ($amount > 0)
} else if ($transactionType == 2) {
// It is debit.
// Ensure that the user wants to debit an amount greater than 0 and
// the amount is less than the money available in the checking account.
if (($amount > 0) &&
($amount < doubleval($data["PreviousCheckingBalance"]))) {
$newBalance = doubleval($data["PreviousCheckingBalance"]) - $amount;
} else {
// Debit amount is either 0 or it is bigger than a allowed value.
$updateDB = false;
$newBalance = doubleval($data["PreviousCheckingBalance"]);
}
} // End of if ($transactionType == 1)
// Bank teller transaction is completed now.
// Life is good. Send the result back.
$finalResult["ResultCode"] = 0;
$finalResult["ResultMsg"] = "AccountTransaction successful.";
// If we modified the current checking balance because of a
// deposited or debited amount, then update the database.
if ($updateDB == true) {
// Prepare an SQL Update statement.
$updateStr = "update account set CheckingBalance=$newBalance " .
"where AccountHolderName='$accountHolderName'";
// Execute the update query.
$dbResult = mysqli_query($link, $updateStr);
// See if there are any DB errors?
if (mysqli_errno($link)) {
// Close the connection to the database.
close_connection_to_db();
// Prepare the error message to be returned.
$resultMsg = "DB write error: " . mysqli_errno($link) .
" [" . mysqli_error($link) . "]";
$finalResult["ResultCode"] = 1;
$finalResult["ResultMsg"] = $resultMsg;
// Return the error message.
return ($finalResult);
}
if ($dbResult == true) {
// All fine.
// Prepare to send back the result of this operation.
$finalResult["ResultCode"] = 0;
$finalResult["ResultMsg"] =
"New balance for $accountHolderName has been stored in DB.";
} else {
$finalResult["ResultCode"] = 1;
$finalResult["ResultMsg"] =
"New balance for $accountHolderName could not be stored in DB.";
// Return the final result that has an error.
return($finalResult);
} // End of if ($dbResult == true)
} // End of if ($updateDB == true)
// We already stored the pre-transaction account snapshot in an
// array at the top of this function.
// Store a second record that will have the updated checking balance.
$data = null;
$data["AccountHolderName"] = $row["AccountHolderName"];
$data["TransactionAmount"] = $amount;
$data["AccountNumber"] = $row["AccountNumber"];
$data["NewCheckingBalance"] = $newBalance;
$data["StockName"] = $row["StockName"];
$data["StockQuantity"] = intval($row["StockQuantity"]);
$data["StockValue"] = doubleval($row["StockValue"]);
$accountInfo[1] = $data;
$finalResult["AccountInfo"] = $accountInfo;
// Close the DB connection.
close_connection_to_db();
// Return the final result.
return($finalResult);
} // End of function accountTransaction.
/*
============================================================
Function: portfolioValue
Last modified: May/11/2007.
This function stores the current portfolio value of a
given account holder to the database. It takes the
account holder name and the current stock price as input
arguments. It reads the currently held position (in the
Bank scenario, every account holder owns only a single
company's stock. It is done to make the task simpler.)
and computes the current market value. Then it updates
the database with the new total stock value.
============================================================
*/
function portfolioValue($accountHolderName, $stockPrice) {
// We will use the globally scoped variables.
global $link, $finalResult, $dbResult;
// Connect to the MySQL BankDB database.
$result = connect_to_db();
// If error in connecting to the DB, return now.
if ($result == false) {
return ($finalResult);
} // End of if ($result == false)
// Prepare the query string to get account info for a given account holder.
$queryStr = "Select * from account where AccountHolderName='$accountHolderName'";
// Execute the MySQL query.
$dbResult = mysqli_query($link, $queryStr);
// Are there any errors?
if (mysqli_errno($link)) {
// Error there. Close the db connection.
close_connection_to_db();
// Prepare error message to be returned.
$resultMsg = "DB read error: " . mysqli_errno($link) .
" [" . mysqli_error($link) . "]";
$finalResult["ResultCode"] = 1;
$finalResult["ResultMsg"] = $resultMsg;
// Return the error.
return ($finalResult);
} // End of if (mysqli_errno($link))
// Did we read any rows from the DB?
$rowCount = mysqli_num_rows($dbResult);
// If there are no matching rows in DB, return now.
if ($rowCount <= 0) {
// Close the DB connection.
close_connection_to_db();
$finalResult["ResultCode"] = 1;
$finalResult["ResultMsg"] = "No accounts were found in BankDB.";
// Return the final result.
return ($finalResult);
} // End of if ($rowCount <= 0)
// Store a record with the current portfolio value.
// Allocate an array to store the pre-transaction and the
// post-transaction account snapshots.
$accountInfo = array();
// Do a database fetch to get the results as an associative array.
$row = mysqli_fetch_assoc($dbResult);
// Store the data fields of a row to an associative array.
$data = null;
$data["AccountHolderName"] = $row["AccountHolderName"];
$data["AccountNumber"] = $row["AccountNumber"];
$data["CheckingBalance"] = doubleval($row["CheckingBalance"]);
$data["StockName"] = $row["StockName"];
$data["StockQuantity"] = intval($row["StockQuantity"]);
$data["PreviousPortfolioValue"] = doubleval($row["StockValue"]);
// Store the associative array in a regular array.
$accountInfo[0] = $data;
// Calculate the portfolio value.
$finalResult["ResultCode"] = 0;
$finalResult["ResultMsg"] = "PortfolioValue successfully calculated.";
$newPortfolioValue = intval($row["StockQuantity"]) * doubleval($stockPrice);
// Since the stock portfolio value has changed, update it in DB.
$updateStr = "update account set StockValue=$newPortfolioValue " .
"where AccountHolderName='$accountHolderName'";
// Perform an SQL update.
$dbResult = mysqli_query($link, $updateStr);
// Check if there are any DB errors.
if (mysqli_errno($link)) {
// Close the connection to DB.
close_connection_to_db();
// Prepare the error message to returned.
$resultMsg = "DB write error: " . mysqli_errno($link) .
" [" . mysqli_error($link) . "]";
$finalResult["ResultCode"] = 1;
$finalResult["ResultMsg"] = $resultMsg;
// Return the error message.
return ($finalResult);
} // End of if (mysqli_errno($link))
if ($dbResult == true) {
// All fine.
// Prepare the success message to be returned.
$finalResult["ResultCode"] = 0;
$finalResult["ResultMsg"] =
"New stock value for $accountHolderName has been stored in DB.";
} else {
$finalResult["ResultCode"] = 1;
$finalResult["ResultMsg"] =
"New stock value for $accountHolderName could not be stored in DB.";
// Update failed. Hence return the error result.
return($finalResult);
} // End of else in if ($dbResult == true)
// Store a second record that will have the updated portfolio value.
// We have already stored the pre-transaction account snapshot in an array.
// Let us store the post-transaction account snapshot also there.
$data = null;
$data["AccountHolderName"] = $row["AccountHolderName"];
$data["AccountNumber"] = $row["AccountNumber"];
$data["CheckingBalance"] = doubleval($row["CheckingBalance"]);
$data["StockName"] = $row["StockName"];
$data["CurrentStockPrice"] = doubleval($stockPrice);
$data["StockQuantity"] = intval($row["StockQuantity"]);
$data["NewPortfolioValue"] = $newPortfolioValue;
// Store the associative array in a regular array.
$accountInfo[1] = $data;
$finalResult["AccountInfo"] = $accountInfo;
// Close the DB connection and return the result.
close_connection_to_db();
return($finalResult);
} // End of function portfolioValue.
?>
错误之处还请见谅