Извините, что разочаровываю вас, но решение немного сложнее, чем вы могли подумать.Как я вижу, @aynber уже предложил это.Поэтому, если он напишет ответ, было бы справедливо сначала принять его к сведению.
Предложения
О разделении интересов:
Позвольте мне начать с того, что в будущем вы должны ознакомиться с принципом Разделение интересов .Проще говоря, взяв код в качестве примера: всегда отделяйте код, включающий доступ к базе данных (для извлечения данных, обновления и т. Д.), От кода, отображающего данные (например, 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-операторы с параметрами, используйте подготовленные операторы (вместо mysqli::query
в данном случае).Они защитят ваш код от возможных sql инъекций .Для завершения, в конце этого ответа я опубликовал пример index.php
с использованием подготовленных утверждений вместо mysqli::query
.
Решение вопроса:
Шаги:
Что касается подготовленного мною решения, оно состоит из четырех шагов:
- Извлечение данных из базы данных и сохранение их в массив (
$data
). - Сначала создайте второй массив (
$formattedData
).Затем выполните итерацию по $data
и сохраните его элементы в $formattedData
таким образом, чтобы их было очень легко отобразить в выбранных структурах HTML (div
для буквы, table
для категорий). - Выполните итерацию по
$formattedData
и добавьте элемент с null
в качестве имени категории для каждой пропущенной категории в последнем ряду категорий каждой буквы.Извините, я написал здесь одно предложение, но, если вы прочитаете комментарии в моем коде, вы, несомненно, лучше поймете, что я имею в виду. - Отображение данных в HTML-части страницы с помощью итерации
$formattedData
и чтение его значений.
Конечно, вы можете оптимизировать php-код по своему желанию и / или распределить его по двум-трем функциям.Затем вы можете просто вызвать их и присвоить их возвращенные значения переменным $data
и $formattedData
.
Примечание:
Если вы используете мой код подключения,не забудьте заменить мои учетные данные БД вашими.
index.php
<code><?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) . '
';// echo '
' . print_r($formattedData, TRUE) . '
';// =====================================================================================?> Демо
Демонстрация: печать списка категорий для каждой буквы категории в нескольких столбцах.
$ categoryRows) {?> Данные не найдены
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');
Результат
![enter image description here](https://i.stack.imgur.com/0KdrW.png)
Дополнительный контент:
Это пример того, как извлекать данные, используя подготовленные операторы вместо mysqli::query
.Обратите внимание, что я поместил здесь только код извлечения данных.Остальная часть кода идентична омологическому разделу вышеупомянутой страницы index.php
, которая использует 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 /4992216/vyzov-neopredelennogo-metoda-mysqlistmt-getresult
*/
$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
* ---------------------------------------------------------------------------------------------
*/
// ...