介绍
Note:这个答案详细介绍了创建多记录到一行安排。但是,可以更改此答案以提供单记录到一行安排。
分离关注点将帮助您编写更清晰的代码。分离关注点将使您的代码更容易维护。干净的代码是松散耦合,不受嵌入依赖项的负担。干净的代码识别其依赖关系函数签名 and 类构造函数期望这些需求能够从外部得到满足。干净的代码有紧密的凝聚力。这意味着函数/方法有一个任务,类有一个目标。干净的代码通常反映在已分解和细化的任务中(但并非总是如此)。干净的代码是我努力追求的理想,但没有人是完美的。
尝试想办法获得尽可能多的东西SQL and PHP从你的HTML文件。仅插入变量和显示函数的返回结果可以使 HTML 更易于阅读。良好的 HTML 结构也很重要。
分解动态构建的任务<table>
基于 SQL 查询的结果是很有可能的。最终,您可能决定使用CSS and divs出于样式和响应能力的原因。可以更改此代码来实现此目的(毕竟,您只是将盒子堆叠成行)。
最终,创建一个 OOP 类(带有自定义命名空间)非常适合模块化您的代码并从代码中获取绝大多数符号(变量名称等)。全局命名空间.
在我们开始之前:php.ini:include_path
您想为您的项目设置逻辑目录架构吗?
Set the include_path
代替php.ini
.
如果您搜索您的php.ini
为了include_path
设置,您可以将其设置为一个目录,或任何一组适当的目录。这样,您就可以按照您想要的方式将文件排列在目录中,并且您的include
, include_once
, require
, and require_once
语句仍然会找到他们想要导入的文件。您不必输入绝对路径,例如/dir/dir/file.php
或相对路径,如../../core/database.php
。在这两种情况下,您都可以只指定文件名。
Example:
include 'file.php'; //Finds the file if it is in the include_path.
require 'database.php'; //Finds the file if it is in the include_path.
Note:将库文件和其他纯 PHP 编码文件(等)保留在 webroot 或任何可公开访问的目录之外。将它们逻辑地保持在网络根目录之上。设置include_path
所以你不必继续做../../blah/foo
一直。
Tasks
1)首先,创建一个函数来获取 a 的实例mysqli_result
object.
/**
* Returns a string, or
* throws an UnexpectedValueException, otherwise.
*/
function isString($string)
{
if (!is_string($string)) {
throw new UnexpectedValueException("$string must be a string data type.");
}
return $string;
}
/**
* Returns a mysqli_result object, or throws an `UnexpectedValueException`.
* You can reuse this for other SELECT, SHOW, DESCRIBE or EXPLAIN queries.
*/
function getMySQLiResult(MySQLi $db, $sql)
{
$result = $db->query(isString($sql));
if (!($result instanceof mysqli_result)) {
throw new UnexpectedValueException("<p>MySQLi error no {$db->errno} : {$db->error}</p>");
}
return $result;
}
2) 其次,创建一个函数来容纳 SQL 并调用 getMySQLiResult()。
/**
* Make sure you can get the data first.
* returns a mysqli_result object.
*/
function getInterviews(MySQLi $db)
{
$sql = "SELECT * FROM `interviews` WHERE `featured` = 1";
return getMySQLiResult($db, $sql);
}
3)创建一个用于构建表数据的函数(<td></td>
) 单元格及其内容。放allHTML 或您需要的数据对每个记录重复在这里。
/**
* Returns one database table record a table data cell.
*/
function buildCell(array $record)
{
return "<td>\n".
'<img src="' .$record['image']. '" alt="' .$record['title']. '">' ."\n".
'<h4>' .$record['title']. '</h4>' . "\n" .
'<hr>' . "\n" .
'<p class="description">' .$record['description']. '</p>' . "\n" .
'<div id="hovers">
<a href="#" class="button">
<span class="contentbut">Read More</span>
</a>
</div>' . "\n
</td>\n";
}
4)创建一个用于构建表行的函数。警惕部分行。 :-)
First,一点辅助功能。
/**
* Returns one <tr></tr> element. Helper.
*/
function makeTr($tds)
{
return "<tr>\n" .isString($tds). "\n</tr>";
}
Second, 真正的交易。
function buildTableRow (array $tableRow)
{
return makeTr(buildCell($tableRow)) . "\n"; //Done!
}
/**
* Returns a string of multiple <tr></tr> elements,
* $maxRecords per row.
*/
function buildTableRows(array $tableRows, $numRecords, $maxPerRow)
{
$rows = []; // Holds finished groups of <tr>s
$row = ''; // Temporary variable for building row of <td>s
$numCells = 0; // Number of cells currently in a row of <td>s.
$numRows = (int)($numRecords / $maxPerRow); //Rows to make.
$numStragglers = $numRecords % $maxPerRow; // Extra <td>s, partialRow.
if ($numStragglers !== 0) { //Check if extra row is needed.
$numRows += 1;
}
foreach ($tableRows as $record)
{
$row .= buildCell($record);
++$numCells;
if ($numCells === $numRecords) { // Builds partial, last row, if needed.
$rows[] = makeTr($row);
break; // Done!
}
if ($numCells === $maxPerRow) { // Builds full row.
$rows[] = makeTr($row); // Save the row.
$numCells = 0; // Start cell counter over.
$row = ''; // Start a new row.
}
}
if(count($rows) !== $numRows) { //Verify all rows were created.
throw new RuntimeException("Rows (<tr>) for all records were not created!");
}
return implode("\n", $rows) . "\n"; //Return all rows as a string.
}
5) 创建一个函数,在页面上输出您需要的 HTML。在这种情况下,您只需要一 (1) 次替换即可出现在 HTML 中。
/**
* returns a set of HTML table rows (<tr></tr>) to fill a <tbody>.
* or, returns an alternative message.
*/
function drawInterviews(MySQLi $db, $maxPerRow) //PDO is recommened. Dependency injection.
{
$defaultMessage = "<tr>\n<td>There are no featured interviewers.<td>\n<\tr>\n";
try {
if (!is_int($maxPerRow) || $maxPerRow < 1) {
throw new RangeException("The number of interviews per row must be an integer equal to 1, or greater than 1.");
}
//Make a robust connection sequence, or pass it in like above.
//$db = new mysqli('host', 'user', 'password', 'dbname');
$result = getInterviews($db);
$numRecords = result->num_rows;
if ($numRecords < 1) {
return $defaultMessage;
}
if ($numRecords === 1) {
return buildTableRow($result->fetch_assoc());
}
return buildTableRows($result->fetch_all(), $numRecords, $maxPerRow);
} catch (Exception $e)
//Something went wrong with the query.
error_log($e->getMessage());
} finally { //PHP 5.5+
$result->free();
}
return $defaultMessage;
}
6) 现在,有一个好的 HTML<table>
结构。只需要一次插值。假设三个<td>
每行 s(记录)...
不管怎样,如果你想要一张桌子,就把这张桌子“骨架”的副本放在里面academytest.php
,介于header和footer(即主要<body>
HTML 文档的内容)。
<table>
<caption>Featured Interviewers</caption> <!-- Centers above table. -->
<thead>
<tr> <!-- If needed. -->
<th>Heading1</th> <!-- If needed. -->
<th>Heading2</th> <!-- If needed. -->
<th>Heading3</th> <!-- If needed. -->
</tr>
</thead>
<tfoot></tfoot> <!-- If needed. Yes, it goes after <thead>. -->
<tbody>
<!-- <div id="element1"></div> --> //What goes between here?
<!-- <div id="content1"> --> //What's this?
<?= drawInterviews($db, 3); ?> <!-- Dependency injection. -->
</tbody>
</table>
所有这些都可以变得更加模块化和可重用(甚至面向对象)。
Update:
根据您的 Dropbox 代码...
学院测试.php
1)最好的办法是创建一个名为的单独的 PHP 文件tbodyFiller.php
,或类似的东西。把所有的函数都放到这个文件里,except for getInterviews()
and drawInterviews()
这将进入academyLibray.php
, isString()
这将进入library.php
, and getMySQLiResult()
哪个将进入database.php
(以前init.php
).
The 开始 of academytest.php
应该看起来像这样:
<?php
// academytest.php
require '../../includes/library.php'; //For now, put generic helper functions here. Group them, later.
require_once '../../core/database.php'; //Formerly, init.php. Put getMySQLiResult() in here.
require '../../includes/academyLibrary.php'; //Put the two "interview" functions here.
$db = getMySQLi(); //Many things are dependent on this being here.
require '../../includes/navigation.php';
/***************** DELETE THESE LINES *****************/
//$sql = "SELECT * FROM interviews WHERE featured = 1";
//$featured = $db->query($sql);
/******************************************************/
在页脚中academytest.php
,关闭与数据库的连接。
<!-- ------FOOTER------ -->
<?php
include '../../includes/footer.php';
$db->close(); //Ensures $db is available to use in the footer, if necessary.
?>
库.php
The 开始 of library.php
应该看起来像这样:
<?php
// library.php
/**
* Returns a string, or
* throws an UnexpectedValueException, otherwise.
*/
function isString($string)
{
if (!is_string($string)) {
throw new UnexpectedValueException("$string must be a string data type.");
}
return $string;
}
I think init.php
应该被命名database.php
。您可以学习使用面向对象的构造函数(使用new
)序列,并在闲暇时进行错误检查。最终,你会想要学习PDO http://php.net/manual/en/book.pdo.php.
另外,创建一个单独的文件来保存您的凭据。现在,这比将它们硬编码到getMySQLi()
功能。
dbCreds.php
<?php
// dbCreds.php
$host = ''; //IP or DNS name: string.
$username = ''; //Your account: string.
$passwd = ''; //The password: string.
$dbname = ''; //The database you want to work with: string.
//*************************************************************************
//$port = '3306'; //Un-comment and change only if you need a differnt TCP port.
//Also, you would need to add a $port as your last argument in new MySQLi(),
//in the getMySQLi() function.
数据库.php
<?php
// database.php
/**
* Returns a mysqli_result object, or throws an `UnexpectedValueException`.
* You can reuse this for other SELECT, SHOW, DESCRIBE or EXPLAIN queries.
*/
function getMySQLiResult(MySQLi $db, $sql)
{
$result = $db->query(isString($sql));
if (!($result instanceof mysqli_result)) {
throw new UnexpectedValueException("<p>MySQLi error no {$db->errno} : {$db->error}</p>");
}
return $result;
}
function getMySQLi() //This can be improved, but that's not the issue right now.
{
require_once 'dbCreds.php'; //Choose your own file name. Do not put in public directory.
$db = new mysqli($host, $username, $passwd, $dbname); //$port would be next.
if(!($db instanceof MySQLi)){
throw new UnexpectedValueException("A MySQLi object was not returned during your connection attempt.");
}
if(isset($db->connect_error)){
throw new UnexpectedValueException("The database connection was not established. {$db->connect_errno} : {$db->connect_error}");
}
return $db
} //Using the object form of MySQLi object has side benenfits.
学院图书馆.php
The 开始 of academyLibrary.php
应该看起来像这样:
<?php
// academyLibrary.php
require 'tbodyFiller.php'; //Put all but four functions in here.
function getInterviews(MySQLi $db)
{
$sql = "SELECT * FROM `interviews` WHERE `featured` = 1";
return getMySQLiResult($db, $sql);
}
/**
* Comments //etc...
*/
function drawInterviews(MySQLi $db, $maxPerRow)
{
//The code, etc ...
}
如果您还没有配置您的include_path
里面的php.ini
, 确保academyLibrary.php
and tbodyFiller.php
位于same目录。
导航.php
我们将用面向对象的形式取代使用 MySQL 的过程形式。这很简单,我们不需要做太多改变。我目前不会替换您的循环或查询,但我的建议是改掉这个习惯放置 PHP 循环和 SQLdirectly在你的 HTML 中。找到一种使用函数或方法的方法,就像我对表中所做的那样academytest.php
。这个时候你应该已经有足够的例子了。 :-)
重构
我花了一些时间重构这个文件。这是我在顶部的内容。再次,您可能希望创建另一个 PHP 文件,例如navLibrary.php
,并将这些函数放入其中。在这种情况下,您可以用一行替换下面看到的所有函数,require 'navLibrary.php';
。当然,这种导入代码的方式可能取决于您的配置include_path
里面的php.ini
.
<?php
// navigation.php
function getPqueryMainData(MySQLi $db)
{
$sql = "SELECT * FROM `mainmenu` WHERE `parent` = 0"; //pqueryMain
return getMySQLiResult($db, $sql);
}
function getPqueryData(MySQLi $db)
{
$sql = "SELECT * FROM `categories` WHERE `parent` = 0"; //pquery
return getMySQLiResult($db, $sql);
}
function getCquery1Data(MySQLi $db)
{
$sql = "SELECT * FROM `categories` WHERE `parent` = 1"; //cquery1
return getMySQLiResult($db, $sql);
}
function getCquery2Data(MySQLi $db, $int)
{
$sql = "SELECT * FROM `categories` WHERE `parent` = '$int'"; //cquery2
return getMySQLiResult($db, $sql);
}
//Consider doing at most 3 queries early on.
//Consider using better names for your variables.
//I get that 'p' means "primary", and 'c' means "child", but come on. :-)
$pqueryMain = getPqueryMainData($db);
$pquery = getPqueryData($db);
$cquery1 = getCquery1Data($db);
$cquery2 = null;