因此,我想填充并显示一个2列宽的HTML表格。但是,无论我尝试什么,我的数据总会粘在一起。
问题是,我实际上想将整个数据放入一个表中,并在其中按起始字母排序。
这是我的代码:
<?php include_once 'Database/dbh.php'; ?>
<!DOCTYPE html>
<html>
<body>
<?php
$fetch = "SELECT DISTINCT Kategori, LEFT(Kategori, 1) AS Letter FROM kategorier ORDER BY Kategori;";
$result = mysqli_query($conn, $fetch);
$resultCheck = mysqli_num_rows($result);
if ($resultCheck > 0) {
while ($row = mysqli_fetch_assoc($result)) {
if (!isset($lastLetter) || $lastLetter != $row['Letter']) {
$lastLetter = $row['Letter'];
echo "<h2>", $row['Letter'], "</h2>";
}
echo "<table><tr><td>" . $row['Kategori'] . "</td></tr></table>";
}
}
?>
</body>
</html>
以下是当前情况的图片:
这是我想要的样子:
答案 0 :(得分:1)
很抱歉让您失望,但是解决方案比您想象的要复杂一些。如我所见,@ aynber已经建议了它。因此,如果他写了答案,那么首先考虑他的答案就很公平了。
关于关注点分离:
现在让我开始说,对于未来,您应该熟悉Separation of Concerns原则。简而言之,以您的代码为例:始终将涉及访问数据库(用于获取数据,更新等)的代码与显示数据的代码(例如页面的HTML部分)分开。
这意味着,如果需要从数据库中获取数据,请在网页顶部进行处理,然后将其保存在数组中。然后,只需在网页的HTML部分中使用这些数组即可,而不是使用与数据库相关的某些功能,例如mysqli_query
或mysqli_fetch_assoc
等。为清楚起见,请参见我提供的代码的HTML部分(“ index.php” )。
此方法的一大优势是,您可以将整个php代码从页面顶部移到php函数或类方法中。然后,数组将仅包含调用这些函数/方法所产生的数据。
以上所有陈述的重点?随心所欲地使用php代码和网页顶部的数据,并将结果保存在php数组中。最后,数组应该具有这样的结构,即网页HTML部分的工作将非常简单:仅读取和显示数组元素。
因此,请勿将HTML代码与数据库相关的代码混合使用。如果这样做,那么代码将很难维护。
关于从PHP打印客户端代码:
您将来应该记住的另一个重要约定是,不要使用php代码打印任何客户端代码。例如。不要使用echo "<table><tr><td>"...
之类的语句。在这种echo
情况下,只需将要显示的内容保存到变量中,然后根据需要将其显示在网页的HTML部分中即可。
关于准备好的陈述:
如果需要使用参数执行sql语句,请使用prepared statements(在这种情况下,请使用mysqli::query
)。他们将保护您的代码免受最终sql injections的侵害。为了完整起见,在此答案的结尾,我发布了一个使用准备好的语句而不是index.php
的示例mysqli::query
。
步骤:
关于我准备的解决方案,它涉及四个步骤:
$data
)中。$formattedData
)。然后遍历$data
并将其项保存到$formattedData
中,这样就可以很容易地将它们显示在选定的HTML结构中(字母为div
,类别为table
)。$formattedData
,并在每个字母的最后一个类别行中为每个缺少的类别附加一个null
作为类别名称的项目。抱歉,我在这里写了一句话,但是,如果您阅读代码中的注释,您一定会更好地理解我的意思。$formattedData
并读取其值,在页面的HTML部分中显示数据。当然,您可以根据需要优化php代码和/或将其分配给两个函数。然后,您可以调用它们并将它们的返回值分配给$data
和$formattedData
变量。
注意:
如果您使用我的连接代码,请不要忘记用您的数据库凭据替换我的数据库凭据。
index.php
<?php
require 'Database/dbh.php';
$sql = 'SELECT
DISTINCT Kategori,
LEFT(Kategori, 1) AS Letter
FROM kategorier
ORDER BY Kategori';
$result = mysqli_query($conn, $sql);
/*
* Fetch all data at once, into an array like this:
*
* Array
* (
* [0] => Array
* (
* [Kategori] => Artiskok
* [Letter] => A
* )
*
* [1] => Array
* (
* [Kategori] => Asiatisk
* [Letter] => A
* )
*
* [2] => Array
* (
* [Kategori] => Burger
* [Letter] => B
* )
*
* [...] => [...]
*
* )
*/
$data = mysqli_fetch_all($result, MYSQLI_ASSOC);
/*
* Free the memory associated with the result. You should
* always free your result when it is not needed anymore.
*
* @link http://php.net/manual/en/mysqli-result.free.php
*/
mysqli_free_result($result);
/*
* Close the previously opened database connection. Not really needed because
* the PHP engine closes the connection anyway when the PHP script is finished.
*
* @link http://php.net/manual/en/mysqli.close.php
*/
mysqli_close($conn);
/*
* Iterate through the fetched data and save it into a new array, with a structure suited for the
* required HTML display. To each letter, a list of category rows is assigned. The new array will
* look like this, when the maximal number of categories per category row is 2:
*
* Array
* (
* [A] => Array
* (
* [0] => Array
* (
* [0] => Aoiuoiiiu
* [1] => Aqewroiuoiiu
* )
*
* [1] => Array
* (
* [0] => Artiskok
* [1] => Asiatisk
* )
*
* [2] => Array
* (
* [0] => Azkajhsdfjkh
* )
*
* )
*
* [B] => Array
* (
* [0] => Array
* (
* [0] => Bhaskdfhjkh
* [1] => Biuzutt
* )
*
* [1] => Array
* (
* [0] => Burger
* )
*
* )
*
* [...] => [...]
*
* )
*/
$formattedData = [];
// The maximal number of categories per each category row.
$maximalNumberOfCategoriesPerCategoryRow = 2;
// The number of categories per current category row.
$numberOfCategoriesPerCurrentCategoryRow = 0;
// The index of a category row in the list of all category rows assigned to a letter.
$indexOfCurrentCategoryRow = 0;
foreach ($data as $item) {
$letter = $item['Letter'];
$category = $item['Kategori'];
if (!array_key_exists($letter, $formattedData)) {
/*
* Assign an item with the current letter as key and an array as value.
* The array holds all category rows for the current letter.
*/
$formattedData[$letter] = [];
// Reset.
$indexOfCurrentCategoryRow = 0;
// Reset.
$numberOfCategoriesPerCurrentCategoryRow = 0;
}
// Append the current category to the current category row for the current letter.
$formattedData[$letter][$indexOfCurrentCategoryRow][] = $category;
// Increment.
$numberOfCategoriesPerCurrentCategoryRow++;
/*
* If the maximal number of categories per category row is reached...
*
* @see "Modulo" operator at https://secure.php.net/manual/en/language.operators.arithmetic.php
*/
if (
$numberOfCategoriesPerCurrentCategoryRow %
$maximalNumberOfCategoriesPerCategoryRow === 0
) {
// Reset.
$numberOfCategoriesPerCurrentCategoryRow = 0;
// Increment.
$indexOfCurrentCategoryRow++;
}
}
/*
* Append an item with "null" as category for each missing category in the last
* category row of each letter. The array holding the formatted data will look
* like this, when the maximal number of categories per category row is 2:
*
* Array
* (
* [A] => Array
* (
* [...] => [...]
*
* [2] => Array
* (
* [0] => Azkajhsdfjkh
* [1] => null
* )
*
* )
*
* [B] => Array
* (
* [...] => [...]
*
* [1] => Array
* (
* [0] => Burger
* [1] => null
* )
*
* )
*
* [...] => [...]
*
* )
*/
foreach ($formattedData as $letter => $categoryRows) {
$lastCategoryRow = end($categoryRows);
$lastCategoryRowKey = key($categoryRows);
$numberOfCategoriesPerLastCategoryRow = count($lastCategoryRow);
$numberOfMissingCategoriesInLastCategoryRow = $maximalNumberOfCategoriesPerCategoryRow -
$numberOfCategoriesPerLastCategoryRow;
for ($i = 0; $i < $numberOfMissingCategoriesInLastCategoryRow; $i++) {
// Append an item with "null" as category.
$formattedData[$letter][$lastCategoryRowKey][] = null;
}
}
//=====================================================================================
//@todo Just for testing: uncomment the next two lines to display the arrays on screen.
//=====================================================================================
//echo '<pre>' . print_r($data, TRUE) . '</pre>';
//echo '<pre>' . print_r($formattedData, TRUE) . '</pre>';
//=====================================================================================
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes" />
<meta charset="UTF-8" />
<!-- The above 3 meta tags must come first in the head -->
<title>Demo</title>
<link href="custom.css" type="text/css" rel="stylesheet">
</head>
<body>
<h3>
Demo: Print a list of categories per category letter, on multiple columns.
</h3>
<?php
if ($formattedData) { /* Data exists */
foreach ($formattedData as $letter => $categoryRows) {
?>
<div class="categories-container">
<div class="letter">
<?php echo $letter; ?>
</div>
<table class="categories">
<?php
foreach ($categoryRows as $categoryRow) {
?>
<tr>
<?php
foreach ($categoryRow as $category) {
?>
<td>
<?php echo $category; ?>
</td>
<?php
}
?>
</tr>
<?php
}
?>
</table>
</div>
<?php
}
} else { /* No data */
?>
<p class="no-data">
No data found
</p>
<?php
}
?>
</body>
</html>
custom.css
body {
margin: 0;
padding: 20px;
color: #333;
}
a {
text-decoration: none;
}
.categories-container {
margin-bottom: 10px;
}
.letter {
padding: 10px;
text-align: left;
font-weight: 700;
background-color: #a0c3e5;
}
.categories {
width: 100%;
border-spacing: 1px;
border-collapse: separate;
}
.categories td {
width: 50%;
padding: 10px;
background-color: #f4f4f4;
}
.no-data {
padding: 10px;
background-color: #f4f4f4;
}
数据库/dbh.php
<?php
/*
* This page contains the code for creating a mysqli connection instance.
*/
// Db configs.
define('HOST', 'localhost');
define('PORT', 3306);
define('DATABASE', 'tests');
define('USERNAME', 'root');
define('PASSWORD', 'root');
// Error reporting.
error_reporting(E_ALL);
ini_set('display_errors', 1); /* SET IT TO 0 ON A LIVE SERVER! */
/*
* Enable internal report functions. This enables the exception handling,
* e.g. mysqli will not throw PHP warnings anymore, but mysqli exceptions
* (mysqli_sql_exception).
*
* MYSQLI_REPORT_ERROR: Report errors from mysqli function calls.
* MYSQLI_REPORT_STRICT: Throw a mysqli_sql_exception for errors instead of warnings.
*
* @link http://php.net/manual/en/class.mysqli-driver.php
* @link http://php.net/manual/en/mysqli-driver.report-mode.php
* @link http://php.net/manual/en/mysqli.constants.php
*/
$mysqliDriver = new mysqli_driver();
$mysqliDriver->report_mode = (MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
// Create a new db connection.
$conn = mysqli_connect(HOST, USERNAME, PASSWORD, DATABASE, PORT);
用于测试的数据
CREATE TABLE `kategorier` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`Kategori` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `kategorier` (`id`, `Kategori`)
VALUES
(1,'Artiskok'),
(2,'Asiatisk'),
(3,'Burger'),
(4,'Pizza'),
(5,'Asiatisk'),
(6,'Artiskok'),
(7,'Artiskok'),
(8,'Durum'),
(9,'Durum'),
(10,'Pizza'),
(11,'Chinaboks'),
(12,'Azkajhsdfjkh'),
(13,'Aoiuoiiiu'),
(14,'Aqewroiuoiiu'),
(15,'Bhaskdfhjkh'),
(16,'Biuzutt');
结果
这是一个如何使用准备好的语句而不是mysqli::query
来获取数据的示例。请注意,我仅将数据获取代码放在此处。其余代码与上述index.php
页面的omolog部分相同,后者使用mysqli::query
。
index.php
<?php
require 'Database/dbh.php';
/*
* Save the values, with which the database data will be filtered, into variables.
* These values will replace the parameter markers in the sql statement.
* They can come, for example, from a POST request of a submitted form.
*/
$letterParam1 = 'A';
$letterParam2 = 'C';
$letterParam3 = 'P';
/*
* The SQL statement to be prepared. Notice the so-called markers, e.g. the "?" signs. They
* will be replaced later with the corresponding values when using mysqli_stmt::bind_param.
*
* @link http://php.net/manual/en/mysqli.prepare.php
*/
$sql = 'SELECT
DISTINCT Kategori,
LEFT(Kategori, 1) AS Letter
FROM kategorier
WHERE
LEFT(Kategori, 1) = ?
OR LEFT(Kategori, 1) = ?
OR LEFT(Kategori, 1) = ?
ORDER BY Kategori';
/*
* Prepare the SQL statement for execution - ONLY ONCE.
*
* @link http://php.net/manual/en/mysqli.prepare.php
*/
$statement = mysqli_prepare($conn, $sql);
/*
* Bind variables for the parameter markers (?) in the SQL statement that was passed to prepare().
* The first argument of bind_param() is a string that contains one or more characters which
* specify the types for the corresponding bind variables.
*
* @link http://php.net/manual/en/mysqli-stmt.bind-param.php
*/
mysqli_stmt_bind_param($statement, 'sss'
, $letterParam1
, $letterParam2
, $letterParam3
);
/*
* Execute the prepared SQL statement. When executed any parameter markers
* which exist will automatically be replaced with the appropriate data.
*
* @link http://php.net/manual/en/mysqli-stmt.execute.php
*/
mysqli_stmt_execute($statement);
/*
* Get the result set from the prepared statement.
*
* NOTA BENE:
*
* Available only with mysqlnd ("MySQL Native Driver")! If this is not installed, then
* uncomment "extension=php_mysqli_mysqlnd.dll" in PHP config file (php.ini) and restart
* web server (I assume Apache) and mysql service. Or use the following functions instead:
* mysqli_stmt::store_result + mysqli_stmt::bind_result + mysqli_stmt::fetch.
*
* @link http://php.net/manual/en/mysqli-stmt.get-result.php
* @link https://stackoverflow.com/questions/8321096/call-to-undefined-method-mysqli-stmtget-result
*/
$result = mysqli_stmt_get_result($statement);
/*
* Fetch all data at once, into an array like this:
*
* Array
* (
* [0] => Array
* (
* [Kategori] => Artiskok
* [Letter] => A
* )
*
* [1] => Array
* (
* [Kategori] => Asiatisk
* [Letter] => A
* )
*
* [2] => Array
* (
* [Kategori] => Burger
* [Letter] => B
* )
*
* [...] => [...]
*
* )
*/
$data = mysqli_fetch_all($result, MYSQLI_ASSOC);
/*
* Free the memory associated with the result. You should
* always free your result when it is not needed anymore.
*
* @link http://php.net/manual/en/mysqli-result.free.php
*/
mysqli_free_result($result);
/*
* Close the prepared statement. It also deallocates the statement handle.
* If the statement has pending or unread results, it cancels them
* so that the next query can be executed.
*
* @link http://php.net/manual/en/mysqli-stmt.close.php
*/
mysqli_stmt_close($statement);
/*
* Close the previously opened database connection. Not really needed because
* the PHP engine closes the connection anyway when the PHP script is finished.
*
* @link http://php.net/manual/en/mysqli.close.php
*/
mysqli_close($conn);
/*
* ---------------------------------------------------------------------------------------------
* The rest of the page is identical with the omolog part of index.php, which uses mysqli::query
* ---------------------------------------------------------------------------------------------
*/
// ...
答案 1 :(得分:1)
我建议使用定义列表元素dl
代替Table元素,因为它的目的是您想要的显示结果。
不过,为解决您的问题,我建议您进行一些修改。
首先,请记住,DISTINCT
适用于整个列列表,而不仅仅是它旁边的列。其次,来自MySQL的ORDER BY
通常比在PHP中按字母顺序排序要慢,并且MySQL不会按字母数字值naturally
进行排序。最后,由于数据需要按字母顺序进行迭代以确定要移至下一组之前要显示的第一个字母,因此实际上不需要在MySQL查询中使用LEFT()
。
$sql = 'SELECT DISTINCT Kategori FROM kategorier;';
$result = mysqli_query($conn, $sql);
if ($kategorier = mysqli_fetch_all($result, MYSQLI_ASSOC)) {
//convert array of arrays to single array - PDO supports PDO::FETCH_GROUP|PDO::FETCH_COLUMN
$kategorier = array_column($kategorier, 'Kategori');
//sort the resulting array as a human would
natsort($kategorier);
}
$previousLetter = null;
?>
<dl>
<?php
foreach ($kategorier as $kategori) {
$currentLetter = strtoupper($kategori[0]);
if ($previousLetter !== $currentLetter) { ?>
<dt><h2><?php echo $currentLetter; ?></h2></dt>
<?php } ?>
<dd><?php echo $kategori; ?></dd>
<?php $previousLetter = $currentLetter;
} ?>
<dl>
表格数据:
Kategori
---------
Burger
Pizza
Pizza2
Pizza3
Chinaboks
Artiskok
Durum
Asiatisk
Asiatisk2
Asiatisk20
Asiatisk3
数据库结果:(MySQL将按1, 10, 2, 20
而不是1, 2, 10, 20
进行排序)
[
['Kategori' => 'Burger'],
['Kategori' => 'Pizza'],
['Kategori' => 'Pizza2'],
['Kategori' => 'Pizza3'],
['Kategori' => 'Chinaboks'],
['Kategori' => 'Artiskok'],
['Kategori' => 'Durum'],
['Kategori' => 'Asiatisk'],
['Kategori' => 'Asiatisk2'],
['Kategori' => 'Asiatisk20'],
['Kategori' => 'Asiatisk3'],
];
PHP数组排序结果:
[
'Artiskok',
'Artiskok2',
'Artiskok3',
'Artiskok20',
'Asiatisk',
'Burger',
'Chinaboks',
'Durum',
'Pizza',
'Pizza2',
'Pizza3',
];
结果HTML输出:
注意:使用float
将从左到右排序,即
1 | 2
3 | 4
5
/* Suggested CSS */
* {
box-sizing: border-box;
}
dd {
margin-left: 0;
float: left;
width: 50%;
}
dt {
padding: 12px 0;
clear: both;
}
<dl>
<dt><h2>A</h2></dt>
<dd>Artiskok</dd>
<dd>Asiatisk</dd>
<dd>Asiatisk2</dd>
<dd>Asiatisk3</dd>
<dd>Asiatisk20</dd>
<dt><h2>B</h2></dt>
<dd>Burger</dd>
<dt><h2>C</h2></dt>
<dd>Chinaboks</dd>
<dt><h2>D</h2></dt>
<dd>Durum</dd>
<dt><h2>P</h2></dt>
<dd>Pizza</dd>
<dd>Pizza2</dd>
<dd>Pizza3</dd>
<dl>
如果您想进行更复杂的CSS样式设置,例如columns
排序。您可以改用嵌套列表。但是,最好先组织数据,然后再在显示视图中对其进行迭代。
//...
$letters = [];
if (!empty($kategorier)) {
$kategorier = array_column($kategorier, 'Kategori');
//sort the resulting array as a human would
natsort($kategorier);
foreach ($kategorier as $kategori) {
$k = strtoupper($kategori[0]);
if (!isset($letters[$k])) {
$letters[$k] = [];
}
$letters[$k][] = $kategori;
}
}
?>
<ul class="category-index">
<?php foreach ($letters as $k => $kategorier) { ?>
<li>
<h2><?php echo $k; ?></h2>
<ul class="category">
<?php foreach ($kategorier as $kategori) { ?>
<li><?php echo $kategori; ?></li>
<?php } ?>
</ul>
</li>
<?php } ?>
</ul>
结果HTML输出:
注意:使用columns
CSS定义,将从左上到右下进行排序。
1 | 4
2 | 5
3
/* Suggested CSS */
* {
box-sizing: border-box;
}
ul.category-index ul,
ul.category-index {
list-style: none;
margin: 0;
padding: 0;
}
ul.category-index li {
padding: 0;
margin: 0;
}
ul.category {
columns: 2;
}
<ul class="category-index">
<li>
<h2>A</h2>
<ul class="category">
<li>Artiskok</li>
<li>Asiatisk</li>
<li>Asiatisk2</li>
<li>Asiatisk3</li>
<li>Asiatisk20</li>
</ul>
</li>
<li>
<h2>B</h2>
<ul class="category">
<li>Burger</li>
</ul>
</li>
<li>
<h2>C</h2>
<ul class="category">
<li>Chinaboks</li>
</ul>
</li>
<li>
<h2>D</h2>
<ul class="category">
<li>Durum</li>
</ul>
</li>
<li>
<h2>P</h2>
<ul class="category">
<li>Pizza</li>
<li>Pizza2</li>
<li>Pizza3</li>
</ul>
</li>
</ul>
关于所需的table
元素用法。您可以使用嵌套列表示例PHP代码,并包含以下html。但是,要确保整个表是统一的,您将需要使用单个表。 colspan="2"
可用于类别索引行,以将其扩展到两列。为了确定何时需要新行,使用了% 2
,它将在类别的每次偶数迭代中发生。以及使用count
确定是否需要将一个空表单元格添加到当前类别列表中,并避免创建额外的空表行。
//...
$letters = [];
if (!empty($kategorier)) {
$kategorier = array_column($kategorier, 'Kategori');
//sort the resulting array as a human would
natsort($kategorier);
foreach ($kategorier as $kategori) {
$k = strtoupper($kategori[0]);
if (!isset($letters[$k])) {
$letters[$k] = [];
}
$letters[$k][] = $kategori;
}
}
?>
<table>
<tbody>
<?php foreach($letters as $k => $kategorier) { ?>
<tr>
<td colspan="2"><h2><?php echo $k; ?></h2></td>
</tr>
<tr>
<?php
$l = count($kategorier);
if ($l & 1) {
$kategorier[] = '';
$l++;
}
$y = $l - 1;
foreach ($kategorier as $i => $kategori) { ?>
<td><?php echo $kategori; ?></td>
<?php if ($i % 2 && $i !== $y) { ?></tr><tr><?php } ?>
<?php } ?>
</tr>
<?php } ?>
</tbody>
</table>
结果HTML:
注意:使用table
将从左到右排序。即
1 | 2
3 | 4
5 |
/* Example CSS */
* {
box-sizing: border-box;
}
table {
width: 100%;
}
<table>
<tr>
<td colspan="2">
<h2>A</h2>
</td>
</tr>
<tr>
<td>Artiskok</td>
<td>Asiatisk</td>
</tr>
<tr>
<td>Asiatisk2</td>
<td>Asiatisk3</td>
</tr>
<tr>
<td>Asiatisk20</td>
<td></td>
</tr>
<tr>
<td colspan="2">
<h2>B</h2>
</td>
</tr>
<tr>
<td>Burger</td>
<td></td>
</tr>
<tr>
<td colspan="2">
<h2>C</h2>
</td>
</tr>
<tr>
<td>Chinaboks</td>
<td></td>
</tr>
<tr>
<td colspan="2">
<h2>D</h2>
</td>
</tr>
<tr>
<td>Durum</td>
<td></td>
</tr>
<tr>
<td colspan="2">
<h2>P</h2>
</td>
</tr>
<tr>
<td>Pizza</td>
<td>Pizza2</td>
</tr>
<tr>
<td>Pizza3</td>
<td></td>
</tr>
</table>
答案 2 :(得分:0)
更改为(未试用):
while ($row = mysqli_fetch_assoc($result)) {
if (!isset($lastLetter) || $lastLetter != $row['Letter']) {
echo "</tr></table>";
$lastLetter = $row['Letter'];
echo "<h2>", $row['Letter'], "</h2>";
echo "<table><tr>";
}
echo "<td>" . $row['Kategori'] . "</td>";
}
希望这可以帮助您;)
欢呼
尼古拉斯
答案 3 :(得分:-1)
我建议您可以使用Bootstrap模板,并使用以下代码作为结果,结果将根据需要在两列中分开。
echo "<div class="col-lg-6 col-md-6 col-sm-6"><table><tr><td>" . $row['Kategori'] . "</td></tr></table></div>";