To convert the provided PHP code that outputs a DataTable on page load to use AJAX for loading the DataTable while preserving its functionality, we'll need to make several changes. The goal is to fetch the table data dynamically via an AJAX request and initialize the DataTable with that data, maintaining features like filtering by date, status, and responsive behavior. Below is the refactored solution.
Approach
- Separate PHP Logic for Data Retrieval: Create a separate PHP script (e.g., ajax/inbox_data.php) to handle the data query and return JSON data for the DataTable.
- Modify the HTML: Remove the PHP loop that generates table rows ($INBOX_TD) and initialize an empty DataTable.
- Update JavaScript: Use DataTables' AJAX feature to fetch data from the new PHP script and handle dynamic updates.
- Preserve Functionality: Ensure date filtering, status filtering, and modal interactions work as in the original code.
- Handle Attachments and Status Styling: Replicate the logic for displaying attachments and status badges in the AJAX response.
Step 1: Create the AJAX Handler PHP Script (ajax/inbox_data.php)
This script will handle the data retrieval logic from the original code and return JSON data for the DataTable.
<?php
// ajax/inbox_data.php
header('Content-Type: application/json');
function testDateCk($test_date) {
$test_arr = explode('-', $test_date);
return checkdate($test_arr[0], $test_arr[1], $test_arr[2]);
}
function validateDate($date, $format = 'Y-m-d') {
$d = DateTime::createFromFormat($format, $date);
return $d && $d->format($format) === $date;
}
// Assume $SQLite3 and $_SESSION are already set up (e.g., via an include)
require_once 'path/to/your/database_connection.php'; // Adjust as needed
$TBdate = date('Y-m-d');
$startDate = $TBdate;
$endDate = $TBdate;
// Handle date range filtering
if (isset($_GET['start']) && validateDate($_GET['start'])) {
$startDate = $_GET['start'];
}
if (isset($_GET['end']) && validateDate($_GET['end'])) {
$endDate = $_GET['end'];
} elseif (isset($_GET['end']) && $_GET['end'] == '') {
$endDate = $TBdate;
}
$dateTableSQL = "DATE(date_create) BETWEEN '$startDate' AND '$endDate'";
// Handle access control
$access_company_email_id = '';
foreach ($_SESSION['company_email_id'] as $key => $val) {
if ($_SESSION['access_role'][$key] != 0) {
$access_company_email_id .= $val . ',';
}
}
$access_company_email_id = rtrim($access_company_email_id, ',');
// Handle status filter
$stkp_n = isset($_GET['stkp']) ? $_GET['stkp'] : 'all';
if ($stkp_n != 'all') {
$stkp_n = (int)$stkp_n;
$stkp = "SELECT * FROM inbox_mail WHERE status_kp='$stkp_n' AND $dateTableSQL AND company_email_id IN ($access_company_email_id) ORDER BY date_create, date_update DESC";
} else {
$stkp = "SELECT * FROM inbox_mail WHERE $dateTableSQL AND company_email_id IN ($access_company_email_id) ORDER BY date_create, date_update DESC";
}
// Execute query
$Result = $SQLite3->query($stkp);
$INBOX_ALL = [];
while ($row = $Result->fetchArray(SQLITE3_ASSOC)) {
$INBOX_ALL[] = $row;
}
// Process data for DataTable
$data = [];
foreach ($INBOX_ALL as $valueTable) {
$name_org_info = '';
if ($name_org = $SQLite3->querySingle("SELECT name_org FROM organization WHERE email_org='{$valueTable['inbox_from']}'")) {
$name_org_info = '<br><span class="badge bg-secondary">' . htmlspecialchars($name_org) . '</span>';
}
$data_item = [];
$data_item['file'] = '';
$id_inbox = $valueTable['id'];
$inbox_udate = $valueTable['inbox_udate'];
$date_in_time = date('Y/m/d', $inbox_udate);
$path_save = $date_in_time . '/' . md5($inbox_udate);
if (file_exists('attachments/' . $path_save . '/' . $inbox_udate . '.json')) {
$mail_inbox = json_decode(file_get_contents('attachments/' . $path_save . '/' . $inbox_udate . '.json'), true);
$email_info = isset($mail_inbox['data'][0]['from']['name'])
? '<i class="text-muted">автор:</i> <span class="badge bg-light-subtle border border-light-subtle text-light-emphasis rounded-pill">' . htmlspecialchars($mail_inbox['data'][0]['from']['name']) . '</span>'
: '';
$data_item['file'] = (isset($mail_inbox['data'][0]['attachments']) && count($mail_inbox['data'][0]['attachments']))
? ' <img src="images/paperclip.svg" style="margin-top: -5px; width: 20px;">'
: '';
} else {
$email_info = '';
}
$dt = new DateTime($valueTable['date_create']);
$formatted_date_create = $dt->format('d.m.Y H:i:s');
// Subject column
$subject = htmlspecialchars($valueTable['subject']) . $data_item['file'] . '<br>' . $email_info;
// Email column
$email = htmlspecialchars($valueTable['inbox_from']) . $name_org_info;
// Status column
$status = '';
if ($stkp_n === 'all') {
$status = '<span class="badge bg-secondary" style="' . $STATUS_KP[$valueTable['status_kp']][1] . '">' . $STATUS_KP[$valueTable['status_kp']][0] . '</span>';
} elseif ($stkp_n == 0) {
$status = '<span class="badge bg-info" style="' . $STATUS_KP[$valueTable['status_kp']][1] . '">' . $STATUS_KP[$valueTable['status_kp']][0] . '</span>';
} elseif ($stkp_n == 1) {
$status = '<span class="badge bg-danger" style="' . $STATUS_KP[$valueTable['status_kp']][1] . '">' . $STATUS_KP[$valueTable['status_kp']][0] . '</span>';
} elseif ($stkp_n == 2 || $stkp_n == 4) {
$status = '<span class="badge bg-primary" style="' . $STATUS_KP[$valueTable['status_kp']][1] . '">' . $STATUS_KP[$valueTable['status_kp']][0] . '</span>';
} elseif ($stkp_n == 3) {
$status = '<span class="badge bg-success" style="' . $STATUS_KP[$valueTable['status_kp']][1] . '">' . str_replace('TL_KP', '- ', strstr($valueTable['subject'], 'TL_KP')) . '</span>';
}
// Button column
$button_class = ($stkp_n == 0 || $stkp_n == 2 || $stkp_n == 4) ? 'btn-outline-warning' : ($stkp_n == 1 ? 'btn-outline-danger' : ($stkp_n == 3 ? 'btn-outline-success' : 'btn-light'));
$button = '<button type="button" class="btn ' . $button_class . '" data-bs-toggle="modal" data-bs-target="#exampleModalScrollable" data-id="' . $valueTable['id'] . '" data-stkp="' . $stkp_n . '">открыть</button>';
$data[] = [
'subject' => $subject,
'email' => $email,
'date_create' => $formatted_date_create,
'status' => $status,
'action' => $button
];
}
$response = [
'data' => $data,
'lastIDtd' => date('Y-m-d H:i:s')
];
echo json_encode($response);
exit;Step 2: Modify the Main PHP/HTML File
Remove the PHP loop that generates $INBOX_TD and set up an empty DataTable. The table will be populated via AJAX.
<?php
// Assume $SQLite3, $_SESSION, and $STATUS_KP are defined
require_once 'path/to/your/database_connection.php'; // Adjust as needed
// The date filtering and status logic can remain for setting initial form values
function testDateCk($test_date) {
$test_arr = explode('-', $test_date);
return checkdate($test_arr[0], $test_arr[1], $test_arr[2]);
}
function validateDate($date, $format = 'Y-m-d') {
$d = DateTime::createFromFormat($format, $date);
return $d && $d->format($format) === $date;
}
$TBdate = date('Y-m-d');
$startDate = $TBdate;
$endDate = $TBdate;
if (isset($_GET['start']) && validateDate($_GET['start'])) {
$startDate = $_GET['start'];
}
if (isset($_GET['end']) && validateDate($_GET['end'])) {
$endDate = $_GET['end'];
}
$stkp_n = isset($_GET['stkp']) ? $_GET['stkp'] : 'all';
?>
<section class="section">
<div class="card">
<div class="card-header border-bottom d-flex justify-content-between align-items-center">
<h5 class="card-title d-flex">INBOX письма</h5>
<ul class="list-inline d-flex mb-0">
<li class="d-flex align-items-center">
<i class="bx bx-check-circle font-medium-3 me-50"></i>
<div class="dropdown dropstart">
<div class="dropdown-toggle me-1" role="button" id="dropdownMenuButton" data-bs-display="static" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Статус INBOX</div>
<div class="dropdown-menu dropdown-menu-end dropdown-menu-lg-start" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" href="?page=inbox&stkp=0">не прочитано</a>
<a class="dropdown-item" href="?page=inbox&stkp=1">удалено</a>
<a class="dropdown-item" href="?page=inbox&stkp=2">прочитано не заказ</a>
<a class="dropdown-item" href="?page=inbox&stkp=4">ожидание сделки</a>
<a class="dropdown-item" href="?page=inbox&stkp=3">добавлено сделка</a>
<a class="dropdown-item" href="?page=inbox&stkp=all">Все</a>
</div>
</div>
</li>
</ul>
</div>
<div class="card-body">
<form id="dateRangeForm" method="get" action="?page=inbox" class="js-uajax-form1 mb-4" data-callback="myFunc" data-target="#target">
<div class="form-row row">
<div class="form-group col-md-2">
<label for="startDate">Начальная дата</label>
<input type="date" class="form-control" value="<?php echo htmlspecialchars($startDate); ?>" id="startDate" name="start">
</div>
<div class="form-group col-md-2">
<label for="endDate">Конечная дата</label>
<input type="date" class="form-control" value="<?php echo htmlspecialchars($endDate); ?>" id="endDate" name="end">
</div>
<div class="form-group col-md-4 align-self-end mt-1">
<button type="submit" class="btn btn-primary">Применить</button>
</div>
</div>
</form>
<div class="table-responsive">
<div id="table1_wrapper" class="dataTables_wrapper dt-bootstrap5 no-footer">
<div class="row dt-row">
<div class="col-sm-12">
<table class="table dataTable no-footer" id="table1" aria-describedby="table1_info">
<thead>
<tr>
<th class="sorting" style="width: 143.462px;">Тема письма</th>
<th class="sorting" style="width: 31.781px;">Email</th>
<th class="sorting" style="width: 20.396px;">Дата</th>
<th class="sorting" style="width: 20.406px;">Статус</th>
<th style="width: 15.104px;"></th>
</tr>
</thead>
<tbody></tbody>
</table>
<input type="hidden" value="<?php echo date('Y-m-d H:i:s'); ?>" id="lastIDtd">
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Include JavaScript dependencies -->
<script src="../dashboard/assets/extensions/jquery/jquery.min.js"></script>
<script src="../dashboard/assets/extensions/datatables.net/js/jquery.dataTables.min.js"></script>
<script src="../dashboard/assets/extensions/datatables.net-bs5/js/dataTables.bootstrap5.min.js"></script>
<script src="/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2@4.0.13/dist/css/select2.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" />
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script src="js/tinymce/tinymce.min.js" referrerpolicy="origin"></script>
<script src="/js/main.js"></script>Step 3: Update the JavaScript
Modify the JavaScript to initialize the DataTable with AJAX and handle dynamic updates.
<script>
$(document).ready(function() {
// Initialize TinyMCE
tinymce.init({
selector: '#message_to',
plugins: 'image paste',
toolbar: 'undo redo | styles | bold italic | image',
language: 'ru',
language_url: 'js/tinymce/langs/ru.js',
paste_data_images: true,
automatic_uploads: true,
images_upload_handler: function(blobInfo) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.readAsDataURL(blobInfo.blob());
});
}
});
// Get URL parameters
function getUrlParameter(name) {
var params = new URLSearchParams(window.location.search);
return params.get(name);
}
// Initialize DataTable with AJAX
var table1 = $('#table1').DataTable({
ajax: {
url: 'ajax/inbox_data.php',
data: function(d) {
d.start = getUrlParameter('start') || '';
d.end = getUrlParameter('end') || '';
d.stkp = getUrlParameter('stkp') || 'all';
},
dataSrc: 'data'
},
columns: [
{ data: 'subject' },
{ data: 'email' },
{ data: 'date_create' },
{ data: 'status' },
{ data: 'action', searchable: false, orderable: false }
],
pageLength: parseInt(localStorage.getItem("table1_length") || 10),
responsive: true,
scrollCollapse: true,
scrollY: '680px',
order: [[2, 'desc']],
columnDefs: [{ targets: 4, searchable: false, orderable: false }],
processing: true,
language: { url: "//control.mail.com/ru/datatable/Russian.json" }
});
// Save page length to localStorage
table1.on('length.dt', function(e, settings, len) {
localStorage.setItem("table1_length", len);
});
// Date range form submission
$('#dateRangeForm').on('submit', function(e) {
e.preventDefault();
var startDate = $('#startDate').val();
var endDate = $('#endDate').val();
if (startDate && endDate) {
var url = new URL(window.location.href);
url.searchParams.set('start', startDate);
url.searchParams.set('end', endDate);
window.location.href = url.toString();
} else {
var url = new URL(window.location.href);
url.searchParams.delete('start');
url.searchParams.delete('end');
window.location.href = url.toString();
}
});
// Update URL on date input change
function updateUrl() {
var params = new URLSearchParams(window.location.search);
var newStart = $('#startDate').val();
var newEnd = $('#endDate').val();
if (newStart) params.set('start', newStart);
else params.delete('start');
if (newEnd) params.set('end', newEnd);
else params.delete('end');
var newUrl = window.location.pathname + (params.toString() ? '?' + params.toString() : '') + window.location.hash;
history.pushState({}, '', newUrl);
}
$('#startDate, #endDate').on('change', function() {
updateUrl();
});
// Periodic table refresh
var dataTablNow = setInterval(function() {
$.post('ajax/inbox_data.php', { table_row: $('#lastIDtd').val() }, function(data) {
table1.clear().rows.add(data.data).draw(false);
$('#lastIDtd').val(data.lastIDtd);
}, 'json');
}, 48000);
// Clear interval when modal is closed
$('#exampleModalScrollable').on('hidden.bs.modal', function() {
clearInterval(dataTablNow);
dataTablNow = setInterval(function() {
$.post('ajax/inbox_data.php', { table_row: $('#lastIDtd').val() }, function(data) {
table1.clear().rows.add(data.data).draw(false);
$('#lastIDtd').val(data.lastIDtd);
}, 'json');
}, 48000);
});
});
</script>Explanation of Changes
- AJAX Handler (inbox_data.php):
- Replicates the original PHP logic for querying the inbox_mail table based on date range and status (stkp).
- Processes each row to generate the same HTML structure for the subject, email, status, and action columns, including badges and attachment icons.
- Returns a JSON response with a data array for DataTables and a lastIDtd timestamp.
- HTML Modifications:
- Removed the PHP loop (foreach ($INBOX_ALL as $key => $valueTable)) and the $INBOX_TD variable.
- Kept the table structure with an empty <tbody>.
- Set initial form values for startDate and endDate using PHP to maintain form state.
- JavaScript Modifications:
- Initialized DataTable with an ajax option pointing to ajax/inbox_data.php.
- Defined columns to map the JSON data fields to table columns.
- Preserved the original DataTable settings (e.g., pageLength, responsive, scrollY, order, etc.).
- Updated the date range form to reload the page with new URL parameters, triggering a new AJAX request.
- Modified the periodic refresh (dataTaNow) to use the AJAX endpoint and update the table with clear().rows.add().draw().
- Maintained the TinyMCE initialization and modal event handling.
- Preserved Functionality:
- Date Filtering: The form submits date parameters via URL, which are passed to the AJAX handler.
- Status Filtering: The stkp parameter is passed via URL and included in the AJAX request.
- Responsive Design: Retained responsive: true and scrollY: '680px'.
- Status Badges and Buttons: The AJAX handler generates the same HTML for badges and buttons based on stkp_n.
- Periodic Updates: The table refreshes every 48 seconds, fetching new data and updating lastIDtd.
Notes
- Dependencies: Ensure all JavaScript and CSS dependencies (jQuery, DataTables, Bootstrap, Select2, TinyMCE) are correctly included and accessible.
- Database Connection: Replace 'path/to/your/database_connection.php' with the actual path to your SQLite connection script.
- Security: The code assumes $_SESSION['company_email_id'] and $STATUS_KP are defined. Ensure these are sanitized to prevent SQL injection or XSS vulnerabilities.
- Error Handling: Add error handling in inbox_data.php for cases where the query fails or no data is returned.
- Testing: Test the AJAX endpoint (ajax/inbox_data.php) directly to ensure it returns valid JSON. Use browser developer tools to debug AJAX requests.
This refactored solution maintains all original functionality while loading the DataTable via AJAX, improving performance by reducing initial page load time and enabling dynamic updates.