Пожалуй, это самый важный первый шаг, который стоит сделать. Для этого несколько модифицируем index.php, выделив из него разметку в отдельный файл /templates/feedback.html.
index.php
<?php
ob_start();
include_once('db.php');
$conn = mysql_connect($db_host, $db_user, $db_password);
if($conn === FALSE){
die('db connect error: ' . mysql_error());
}
if(!mysql_select_db($db_name, $conn)){
die('can not use db: ' . mysql_error());
}
if(isset($_POST['submit'])) {
$name = mysql_escape_string($_POST['name']);
$email = mysql_escape_string($_POST['email']);
$message = mysql_escape_string($_POST['message']);
$time = time();
$sql = "INSERT INTO feedback (name, email, message, time) VALUES ('$name', '$email', '$message', '$time')";
$result = mysql_query($sql, $conn);
if(!$result){
die('invalid query: ' . mysql_error());
}
}
$limit = 3;
$offset = isset($_GET['offset']) ? $_GET['offset'] : 0;
$sql = "SELECT * FROM feedback ORDER BY time DESC LIMIT " . ($offset * $limit) . ", {$limit}";
$fetch_result = mysql_query($sql, $conn);
if(!$fetch_result){
die('invalid query: ' . mysql_error());
}
$sql = "SELECT COUNT(*) as counter FROM feedback";
$count_result = mysql_query($sql, $conn);
if(!$count_result){
die('invalid query: ' . mysql_error());
}
$row = mysql_fetch_assoc($count_result);
$total = (int)$row['counter'];
include_once('templates/feedback.html');
ob_end_flush();
?>
templates/feedback.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<link rel=stylesheet type='text/css' href='styles/main.css'>
<script language="JavaScript" type="text/javascript" src="js/form.js"></script>
</head>
<body>
<table width="100%" style="height:100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td width="10%" style="background-color:#7F1A22">
</td>
<td width="90%" style="padding:5px 10px 5px 10px" valign="top">
<h1>Обратная связь</h1>
<p>
Если у вас есть пожелания или вопросы к сотрудникам нашей компании,<br>
пожалуйста, заполните поля формы.<br>
</p>
<form action="index.php" method="post" onsubmit="return submit_form(this);">
<table>
<tr>
<td align="right">
Ваше имя:
</td>
<td>
<input name="name" value="" type="text">
</td>
</tr>
<tr>
<td align="right">
Email:
</td>
<td>
<input name="email" value="" type="text">
</td>
</tr>
<tr>
<td align="right">
Текст вопроса:
</td>
<td>
<textarea name="message" cols="50" rows="4"></textarea>
</td>
</tr>
<tr>
<td>
</td>
<td>
<input value="Отправить" name="submit" type="submit">
</td>
</tr>
</table>
</form>
<?php if($offset > 0) :?><b><a href="?offset=<?=($offset-1)?>"><</a></b><?php endif; ?>
<?php if($total > 0) :?><?=($offset*$limit)+1?> - <?=(($offset+1)*$limit > $total)? $total : ($offset+1)*$limit ?><?php endif; ?>
<?php if(($offset+1)*$limit < $total) :?><b><a href="?offset=<?=($offset+1)?>">></a></b><?php endif; ?>
<?php
while ($row = mysql_fetch_assoc($fetch_result)) {
?>
<hr/>
<b>Автор:</b> <a href="mailto:<?=htmlspecialchars($row['email']);?>"><?=htmlspecialchars($row['name']);?></a><br/>
<b>Сообщение:</b> <?=htmlspecialchars($row['message']);?><br/>
<b>Время:</b> <?=strftime("%m/%d/%y %H:%M:%S", $row['time'])?><br/>
<?php } ?>
</td>
</tr>
</table>
</body>
</html>
До идела еще далеко, но читается уже намного лучше. Удостоверившись в том, что все функциональные тесты срабатывают, продолжаем дальше.
WACT предоставляет мощные средства для отделения презентационной и бизнес логики. Помимо гибкой шаблонной системы в WACT присутствуют также инструменты для абстрагирования работы с БД. Т.к. эти инструменты очень гибко интегрируюся с шаблонной системой, имеет смысл остановить на них выбор.
WACT требует наличие config.ini файла, в котором описываются глобальные конфигурационные настройки приложения. Перенесем данные из db.php в config.ini.
[templates]
forcecompile = TRUE
[database]
driver = mysql
mysql.database = "feedback"
mysql.user = "root"
mysql.password = "test"
mysql.host = "localhost"
Создадим также файл tests/config.ini, в котором мы будем хранить настройки для тестов.
[templates]
forcecompile = TRUE
[database]
driver = mysql
mysql.database = "feedback-web-tests"
mysql.user = "root"
mysql.password = "test"
mysql.host = "localhost"
Заставим WACT также пользоваться этим конфигурационным файлом при вызове из тестов. Для этого добавим следующую строку в /tests/setup.php:
<?php
define('WACT_CONFIG_DIRECTORY', dirname(__FILE__) . '/');
?>
Нам необходимо заменять config.ini на /tests/config.ini на время выполнения тестов, как мы это делали с db.php, для этого несколько изменим фикстуру:
<?php
class AcceptanceTestOfFeedbackProject extends WebTestCase {
function setUp() {
DBC :: execute('DELETE FROM feedback');
$this->_switchToWebTestingConfig();
}
function tearDown() {
$this->_switchToProductionConfig();
}
function _switchToWebTestingConfig() {
$project_dir = dirname(__FILE__) . '/../';
$tests_dir = dirname(__FILE__) . '/';
if(!file_exists($project_dir . 'config.ini~')) {
rename($project_dir . 'config.ini', $project_dir . 'config.ini~');
}
copy($tests_dir . 'config.ini', $project_dir . 'config.ini');
}
function _switchToProductionConfig() {
$project_dir = dirname(__FILE__) . '/../';
if(file_exists($project_dir . 'config.ini~')) {
unlink($project_dir . 'config.ini');
rename($project_dir . 'config.ini~', $project_dir . 'config.ini');
}
}
[...]
?>
Имеет смысл также пользоваться в тесте средствами WACT для работы с БД. Как можно видеть, вызов DBC :: execute('DELETE FROM feedback') - пришел на смену жесткой привязке к mysql_ функциям.
Все готово к тому, чтобы полностью отделить бизнес логику от презентационной. Начинаем с index.php:
<?php
ob_start();
require_once(dirname(__FILE__) . '/external/wact/framework/common.inc.php');
require_once(WACT_ROOT . '/db/db.inc.php');
require_once(WACT_ROOT . '/template/template.inc.php');
function &getList(&$pager) {
return DBC::NewPagedRecordSet('SELECT * FROM feedback ORDER BY time DESC', $pager);
}
function insertFeedback($arr) {
$dataspace = new DataSpace();
$dataspace->import($arr);
$record =& DBC::NewRecord($dataspace);
return $record->insert('feedback', array('name', 'email', 'message', 'time'));
}
if(isset($_POST['submit'])) {
insertFeedback(array('name' => $_POST['name'],
'email' => $_POST['email'],
'message' => $_POST['message'],
'time' => time()));
}
$page = new Template('/feedback.html');
$pager =& $page->getChild('pager');
$feedback =& $page->findChild('feedback');
$feedback->registerDataSet(getList($pager));
$page->display();
ob_end_flush();
?>
WACT ищет по-умолчанию шаблоны в директории /templates/source, модифицируем и перенесем feedback.html.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<link rel=stylesheet type='text/css' href='styles/main.css'>
<script language="JavaScript" type="text/javascript" src="js/form.js"></script>
</head>
<body>
<table width="100%" style="height:100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td width="10%" style="background-color:#7F1A22">
</td>
<td width="90%" style="padding:5px 10px 5px 10px" valign="top">
<h1>Обратная связь</h1>
<p>
Если у вас есть пожелания или вопросы к сотрудникам нашей компании,<br>
пожалуйста, заполните поля формы.<br>
</p>
<form action="index.php" method="post" onsubmit="return submit_form(this);">
<table>
<tr>
<td align="right">
Ваше имя:
</td>
<td>
<input name="name" value="" type="text">
</td>
</tr>
<tr>
<td align="right">
Email:
</td>
<td>
<input name="email" value="" type="text">
</td>
</tr>
<tr>
<td align="right">
Текст вопроса:
</td>
<td>
<textarea name="message" cols="50" rows="4"></textarea>
</td>
</tr>
<tr>
<td>
</td>
<td>
<input value="Отправить" name="submit" type="submit">
</td>
</tr>
</table>
</form>
<list:LIST id="feedback">
<page:navigator id="pager" items="3">
Страница: {$PageNumber} из {$TotalPages}
<page:first><<</page:first> <page:prev><</page:prev>
<page:list>
<page:number>
<page:elipses>...</page:elipses>
<page:separator> </page:separator>
</page:list>
<page:next>></page:next> <page:last>>></page:last>
</page:navigator>
<list:ITEM>
<hr>
<b>Автор:</b> <a href="mailto:{$email}">{$name}</a><br>
<b>Сообщение:</b> {$message}<br>
<b>Время:</b> {$time|date:"H:i:s m/d/Y"}<br>
</list:ITEM>
</list:LIST>
</td>
</tr>
</table>
</body>
</html>
WACT предоставляет набор компонентов, позволяющих заметно облегчить жизнь верстальщика. При помощи <list:LIST> компонента организуется итерация по сообщениям в шаблоне. <page:navigator> компонент берет полностью на себя всю рутину по организации и выводу пейджера. При этом содержимое index.php и feedback.html заметно упростилось и приобрело более логический вид.
Конечно, мы сделали очень много измненений, но выполняющиеся функциональные тесты позволили нам неустанно держать руку на пульсе разработки.
Далее - Шаг третий - внедряем паттерн ActiveRecord.