Displaying large datasets from a database can be challenging, especially when you want to maintain a clean user interface and provide easy navigation. This guide will walk you through creating a sophisticated solution using PHP: an accordion table that presents data in expandable rows, coupled with an efficient pagination system. This approach allows users to view summary information at a glance and expand rows for details, all while navigating through data in manageable chunks.
Creating this dynamic table involves several interconnected components. We'll use PHP for server-side logic (database interaction, pagination), HTML for the structure, CSS for styling the accordion and table, and a touch of JavaScript for the interactive accordion functionality.
Visual representation of accordion UI principles. Accordions help manage content density.
First, we need to establish a connection to your MySQL database. We'll use PDO (PHP Data Objects) as it provides a consistent way to access databases and supports prepared statements, which are crucial for security (preventing SQL injection).
You'll need to have a database and a table with some data. For this example, let's assume a table named `products` with columns like `id`, `name`, `category`, and `description`.
<?php
// Database configuration
$dbHost = 'localhost';
$dbName = 'your_database_name'; // Replace with your database name
$dbUser = 'your_username'; // Replace with your database username
$dbPass = 'your_password'; // Replace with your database password
$tableName = 'products'; // Replace with your table name
// PDO DSN (Data Source Name)
$dsn = "mysql:host={$dbHost};dbname={$dbName};charset=utf8mb4";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // Throw exceptions on errors
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // Fetch associative arrays
PDO::ATTR_EMULATE_PREPARES => false, // Use native prepared statements
];
try {
$pdo = new PDO($dsn, $dbUser, $dbPass, $options);
} catch (PDOException $e) {
// It's better to log this error than to display it publicly in production
throw new PDOException($e->getMessage(), (int)$e->getCode());
}
?>
Ensure you replace the placeholder credentials with your actual database details. This code sets up a PDO connection and configures it to throw exceptions on errors and fetch data as associative arrays.
Pagination involves calculating the total number of records, determining the number of pages, and fetching only the subset of data relevant to the current page.
A typical pagination control allows users to navigate through pages of content.
<?php
// Pagination parameters
$limit = 5; // Number of records to display per page
$currentPage = isset($_GET['page']) && is_numeric($_GET['page']) ? (int)$_GET['page'] : 1;
if ($currentPage < 1) {
$currentPage = 1;
}
$offset = ($currentPage - 1) * $limit;
// Get total number of records
$totalRecordsStmt = $pdo->query("SELECT COUNT(*) FROM {$tableName}");
$totalRecords = $totalRecordsStmt->fetchColumn();
$totalPages = ceil($totalRecords / $limit);
// Fetch records for the current page
$stmt = $pdo->prepare("SELECT id, name, category, description FROM {$tableName} ORDER BY id ASC LIMIT :limit OFFSET :offset");
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$records = $stmt->fetchAll();
?>
This snippet defines how many records appear per page (`$limit`), gets the current page number from the URL (defaulting to 1), calculates the `$offset` for the SQL query, determines the total number of pages, and finally fetches the appropriate records for the current page using a prepared statement.
The accordion table will use HTML for its structure, CSS for styling the collapsed/expanded states and overall appearance, and JavaScript to handle the click events that toggle the accordion rows.
This will be part of the larger PHP file where you loop through `$records`.
<table class="accordion-table">
<thead>
<tr>
<th>ID</th>
<th>Product Name</th>
<th>Category</th>
<th>Action</th> <!-- For the toggle indicator -->
</tr>
</thead>
<tbody>
<?php if (count($records) > 0): ?>
<?php foreach ($records as $record): ?>
<tr class="accordion-header">
<td><?php echo htmlspecialchars($record['id']); ?></td>
<td><?php echo htmlspecialchars($record['name']); ?></td>
<td><?php echo htmlspecialchars($record['category']); ?></td>
<td class="indicator">+</td> <!-- Indicator -->
</tr>
<tr class="accordion-content">
<td colspan="4">
<div class="content-inner">
<strong>Description:</strong>
<p><?php echo nl2br(htmlspecialchars($record['description'])); ?></p>
<!-- Add more details here if needed -->
</div>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="4" style="text-align:center;">No records found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
Place this within a `<style>` tag in your HTML's `<head>` section or in an external CSS file.
body { font-family: Arial, sans-serif; margin: 20px; background-color: #f9f9f9; color: #333; }
.accordion-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
.accordion-table th, .accordion-table td { border: 1px solid #e0e0e0; padding: 12px 15px; text-align: left; }
.accordion-table th { background-color: #388278; color: white; font-weight: bold; }
.accordion-header { cursor: pointer; background-color: #f7f7f7; transition: background-color 0.3s ease; }
.accordion-header:hover { background-color: #e9e9e9; }
.accordion-header td.indicator { text-align: center; font-weight: bold; width: 50px; }
.accordion-header.active td.indicator { content: '-'; } /* JS will toggle this class */
.accordion-content { max-height: 0; overflow: hidden; transition: max-height 0.3s ease-out; background-color: #fff; }
.accordion-content.show { /* max-height will be set by JS to content's scrollHeight */ }
.accordion-content .content-inner { padding: 15px; border-top: 1px dashed #ccc; }
.accordion-content p { margin-top: 5px; line-height: 1.6; }
/* Pagination Styling */
.pagination { display: flex; justify-content: center; padding: 0; list-style: none; margin-top: 25px; }
.pagination li { margin: 0 5px; }
.pagination li a, .pagination li span { display: block; padding: 8px 16px; text-decoration: none; color: #388278; border: 1px solid #ddd; border-radius: 4px; transition: background-color 0.3s, color 0.3s; }
.pagination li a:hover { background-color: #388278; color: white; border-color: #388278;}
.pagination li span.current-page { background-color: #cc9900; color: white; border-color: #cc9900; cursor: default; }
.pagination li.disabled span { background-color: #f0f0f0; color: #aaa; border-color: #ddd; cursor: not-allowed; }
Place this within a `<script>` tag before your closing `</body>` tag.
document.addEventListener('DOMContentLoaded', function () {
const accordionHeaders = document.querySelectorAll('.accordion-header');
accordionHeaders.forEach(header => {
header.addEventListener('click', function () {
// Toggle active class on the header
this.classList.toggle('active');
// Get the indicator cell and change its text
const indicator = this.querySelector('.indicator');
if (this.classList.contains('active')) {
indicator.textContent = '-';
} else {
indicator.textContent = '+';
}
// Get the next sibling element, which is the content row
const contentRow = this.nextElementSibling;
if (contentRow.classList.contains('show')) {
contentRow.classList.remove('show');
contentRow.style.maxHeight = '0';
} else {
contentRow.classList.add('show');
// Set max-height to the content's scroll height for smooth animation
contentRow.style.maxHeight = contentRow.scrollHeight + 'px';
}
});
});
});
Below the table, we'll add links for navigating between pages.
<?php if ($totalPages > 1): ?>
<ul class="pagination">
<?php if ($currentPage > 1): ?>
<li><a href="?page=<?php echo $currentPage - 1; ?>">Previous</a></li>
<?php else: ?>
<li class="disabled"><span>Previous</span></li>
<?php endif; ?>
<?php
// Logic for displaying a limited range of page numbers
$numPageLinksToShow = 5; // Example: 1 ... 4 5 6 ... 10
$startPage = max(1, $currentPage - floor($numPageLinksToShow / 2));
$endPage = min($totalPages, $startPage + $numPageLinksToShow - 1);
// Adjust if we're at the beginning or end
if ($endPage - $startPage + 1 < $numPageLinksToShow) {
if ($startPage == 1) {
$endPage = min($totalPages, $startPage + $numPageLinksToShow - 1);
} elseif ($endPage == $totalPages) {
$startPage = max(1, $totalPages - $numPageLinksToShow + 1);
}
}
if ($startPage > 1) {
echo '<li><a href="?page=1">1</a></li>';
if ($startPage > 2) {
echo '<li class="disabled"><span>...</span></li>';
}
}
for ($i = $startPage; $i <= $endPage; $i++): ?>
<li>
<?php if ($i == $currentPage): ?>
<span class="current-page"><?php echo $i; ?></span>
<?php else: ?>
<a href="?page=<?php echo $i; ?>"><?php echo $i; ?></a>
<?php endif; ?>
</li>
<?php endfor; ?>
if ($endPage < $totalPages) {
if ($endPage < $totalPages - 1) {
echo '<li class="disabled"><span>...</span></li>';
}
echo '<li><a href="?page='.$totalPages.'">'.$totalPages.'</a></li>';
}
?>
<?php if ($currentPage < $totalPages): ?>
<li><a href="?page=<?php echo $currentPage + 1; ?>">Next</a></li>
<?php else: ?>
<li class="disabled"><span>Next</span></li>
<?php endif; ?>
</ul>
<?php endif; ?>
This PHP code generates "Previous" and "Next" buttons, along with numbered page links. It also includes logic to show a condensed list of page numbers (e.g., 1 ... 4 5 6 ... 10) if there are many pages, improving usability.
To better understand the different facets of this solution, the radar chart below illustrates key considerations and their typical importance and complexity in such a project. These are opinion-based estimations for a moderately complex implementation.
This chart highlights that while all aspects are important, data security and pagination logic often require careful attention due to their impact and potential complexity.
The mindmap below provides a high-level overview of how the different components of this system interact, from the user request to the final display of data.
`, ` | `)"]
id3_1_2["Specific Rows for Accordion Header & Content"]
id3_1_3["Pagination Controls (`
|
---|