AJAX dengan PHP
AJAX (Asynchronous JavaScript and XML) memungkinkan kita membuat aplikasi web yang interaktif dengan mengirim dan menerima data dari server tanpa me-refresh halaman.
Apa itu AJAX?
AJAX adalah teknik untuk membuat aplikasi web yang responsif dengan bertukar data dengan server di belakang layar tanpa mengganggu tampilan dan perilaku halaman yang ada.
Keuntungan AJAX
- ✅ Tidak perlu refresh halaman
- ✅ User experience lebih baik
- ✅ Loading data lebih cepat
- ✅ Menghemat bandwidth
- ✅ Real-time updates
- ✅ Validasi form dinamis
- ✅ Auto-complete search
- ✅ Live notifications
Cara Kerja AJAX
Alur Kerja:
- User melakukan aksi (click, submit, dll)
- JavaScript mengirim request ke server
- Server memproses request (PHP)
- Server mengirim response (biasanya JSON)
- JavaScript menerima response
- Halaman diupdate tanpa refresh
AJAX Dasar dengan XMLHttpRequest
File PHP (backend.php)
<?php
header('Content-Type: application/json');
if ($_POST['action'] == 'get_user') {
$userId = $_POST['user_id'];
// Simulasi data user
$users = [
1 => ['name' => 'John Doe', 'email' => 'john@example.com'],
2 => ['name' => 'Jane Smith', 'email' => 'jane@example.com'],
3 => ['name' => 'Bob Johnson', 'email' => 'bob@example.com']
];
if (isset($users[$userId])) {
echo json_encode([
'status' => 'success',
'data' => $users[$userId]
]);
} else {
echo json_encode([
'status' => 'error',
'message' => 'User tidak ditemukan'
]);
}
}
?>
HTML dan JavaScript
<!DOCTYPE html>
<html>
<head>
<title>AJAX Basic Example</title>
</head>
<body>
<h1>Data User</h1>
<select id="userSelect">
<option value="">Pilih User</option>
<option value="1">User 1</option>
<option value="2">User 2</option>
<option value="3">User 3</option>
</select>
<div id="userInfo"></div>
<script>
document.getElementById('userSelect').addEventListener('change', function() {
const userId = this.value;
if (userId) {
// Buat XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
if (response.status === 'success') {
document.getElementById('userInfo').innerHTML =
'<h3>' + response.data.name + '</h3>' +
'<p>Email: ' + response.data.email + '</p>';
} else {
document.getElementById('userInfo').innerHTML =
'<p style="color:red;">' + response.message + '</p>';
}
}
};
xhr.open('POST', 'backend.php', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('action=get_user&user_id=' + userId);
} else {
document.getElementById('userInfo').innerHTML = '';
}
});
</script>
</body>
</html>
Modern AJAX dengan Fetch API
GET Request
// Fetch API - GET request
async function getUsers() {
try {
const response = await fetch('api/users.php');
const data = await response.json();
if (data.status === 'success') {
displayUsers(data.users);
} else {
console.error('Error:', data.message);
}
} catch (error) {
console.error('Network error:', error);
}
}
function displayUsers(users) {
const container = document.getElementById('users-container');
container.innerHTML = '';
users.forEach(user => {
const userDiv = document.createElement('div');
userDiv.innerHTML = `
<div class="card mb-2">
<div class="card-body">
<h5>${user.name}</h5>
<p>${user.email}</p>
</div>
</div>
`;
container.appendChild(userDiv);
});
}
POST Request
// Fetch API - POST request
async function createUser(userData) {
try {
const response = await fetch('api/users.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(userData)
});
const result = await response.json();
if (result.status === 'success') {
alert('User berhasil dibuat!');
getUsers(); // Refresh list
} else {
alert('Error: ' + result.message);
}
} catch (error) {
alert('Network error: ' + error.message);
}
}
PHP API Endpoint
<?php
// api/users.php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
header('Access-Control-Allow-Headers: Content-Type');
$method = $_SERVER['REQUEST_METHOD'];
switch ($method) {
case 'GET':
// Ambil semua users
$users = [
['id' => 1, 'name' => 'John Doe', 'email' => 'john@example.com'],
['id' => 2, 'name' => 'Jane Smith', 'email' => 'jane@example.com']
];
echo json_encode([
'status' => 'success',
'users' => $users
]);
break;
case 'POST':
// Buat user baru
$input = json_decode(file_get_contents('php://input'), true);
if (!$input['name'] || !$input['email']) {
echo json_encode([
'status' => 'error',
'message' => 'Name dan email harus diisi'
]);
exit;
}
// Validasi email
if (!filter_var($input['email'], FILTER_VALIDATE_EMAIL)) {
echo json_encode([
'status' => 'error',
'message' => 'Format email tidak valid'
]);
exit;
}
// Simpan ke database (contoh)
$newUser = [
'id' => time(), // ID sederhana
'name' => $input['name'],
'email' => $input['email']
];
echo json_encode([
'status' => 'success',
'message' => 'User berhasil dibuat',
'user' => $newUser
]);
break;
}
?>
Form Submission dengan AJAX
HTML Form
<form id="userForm">
<div class="mb-3">
<label for="name" class="form-label">Nama</label>
<input type="text" class="form-control" id="name" name="name" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" required>
</div>
<div class="mb-3">
<label for="phone" class="form-label">Telepon</label>
<input type="tel" class="form-control" id="phone" name="phone">
</div>
<button type="submit" class="btn btn-primary">
<span class="spinner-border spinner-border-sm d-none" id="loading"></span>
Simpan
</button>
</form>
<div id="message" class="mt-3"></div>
JavaScript Form Handler
document.getElementById('userForm').addEventListener('submit', async function(e) {
e.preventDefault(); // Cegah submit default
const form = this;
const submitBtn = form.querySelector('button[type="submit"]');
const loading = document.getElementById('loading');
const messageDiv = document.getElementById('message');
// Tampilkan loading
loading.classList.remove('d-none');
submitBtn.disabled = true;
// Buat FormData object
const formData = new FormData(form);
try {
const response = await fetch('process_form.php', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.status === 'success') {
messageDiv.innerHTML = `
<div class="alert alert-success">
${result.message}
</div>
`;
form.reset(); // Reset form
} else {
messageDiv.innerHTML = `
<div class="alert alert-danger">
${result.message}
</div>
`;
}
} catch (error) {
messageDiv.innerHTML = `
<div class="alert alert-danger">
Terjadi kesalahan: ${error.message}
</div>
`;
} finally {
// Sembunyikan loading
loading.classList.add('d-none');
submitBtn.disabled = false;
}
});
PHP Form Processor
<?php
// process_form.php
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo json_encode([
'status' => 'error',
'message' => 'Method tidak valid'
]);
exit;
}
// Validasi input
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$phone = trim($_POST['phone'] ?? '');
$errors = [];
if (empty($name)) {
$errors[] = 'Nama harus diisi';
}
if (empty($email)) {
$errors[] = 'Email harus diisi';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Format email tidak valid';
}
if (!empty($phone) && !preg_match('/^[\d\-\+\s\(\)]+$/', $phone)) {
$errors[] = 'Format telepon tidak valid';
}
if (!empty($errors)) {
echo json_encode([
'status' => 'error',
'message' => implode(', ', $errors)
]);
exit;
}
// Simulasi penyimpanan ke database
try {
// $pdo = new PDO(...);
// $stmt = $pdo->prepare("INSERT INTO users (name, email, phone) VALUES (?, ?, ?)");
// $stmt->execute([$name, $email, $phone]);
echo json_encode([
'status' => 'success',
'message' => 'Data berhasil disimpan!'
]);
} catch (Exception $e) {
echo json_encode([
'status' => 'error',
'message' => 'Gagal menyimpan data: ' . $e->getMessage()
]);
}
?>
Real-time Updates
Live Search
<input type="text" id="searchInput" placeholder="Cari produk..." class="form-control">
<div id="searchResults"></div>
<script>
let searchTimeout;
document.getElementById('searchInput').addEventListener('input', function() {
const query = this.value.trim();
// Debounce untuk menghindari request berlebihan
clearTimeout(searchTimeout);
if (query.length > 2) {
searchTimeout = setTimeout(() => {
searchProducts(query);
}, 300);
} else {
document.getElementById('searchResults').innerHTML = '';
}
});
async function searchProducts(query) {
try {
const response = await fetch(`search.php?q=${encodeURIComponent(query)}`);
const data = await response.json();
const resultsDiv = document.getElementById('searchResults');
if (data.products.length > 0) {
resultsDiv.innerHTML = data.products.map(product => `
<div class="search-result-item p-2 border-bottom">
<strong>${product.name}</strong>
<span class="text-muted"> - Rp ${product.price}</span>
</div>
`).join('');
} else {
resultsDiv.innerHTML = '<p class="text-muted p-2">Produk tidak ditemukan</p>';
}
} catch (error) {
console.error('Search error:', error);
}
}
</script>
Auto-refresh Data
// Auto-refresh setiap 30 detik
setInterval(async function() {
try {
const response = await fetch('api/latest_data.php');
const data = await response.json();
if (data.status === 'success') {
updateDashboard(data.data);
}
} catch (error) {
console.error('Auto-refresh error:', error);
}
}, 30000);
function updateDashboard(data) {
// Update counter
document.getElementById('totalUsers').textContent = data.total_users;
document.getElementById('totalSales').textContent = 'Rp ' + data.total_sales;
// Update chart atau table
if (typeof updateChart === 'function') {
updateChart(data.chart_data);
}
}
Notification System
// Polling untuk notifikasi baru
setInterval(checkNotifications, 60000); // Setiap 1 menit
async function checkNotifications() {
try {
const response = await fetch('api/notifications.php');
const data = await response.json();
if (data.new_notifications > 0) {
showNotificationBadge(data.new_notifications);
// Tampilkan toast notification
data.notifications.forEach(notification => {
showToast(notification.message, notification.type);
});
}
} catch (error) {
console.error('Notification check error:', error);
}
}
function showNotificationBadge(count) {
const badge = document.getElementById('notificationBadge');
badge.textContent = count;
badge.classList.remove('d-none');
}
function showToast(message, type = 'info') {
const toast = document.createElement('div');
toast.className = `toast align-items-center text-white bg-${type} border-0`;
toast.innerHTML = `
<div class="d-flex">
<div class="toast-body">${message}</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto"></button>
</div>
`;
document.getElementById('toastContainer').appendChild(toast);
const bsToast = new bootstrap.Toast(toast);
bsToast.show();
}
💡 Tips dan Best Practices
- 🛡️ Security: Selalu validasi dan sanitasi input dari client-side
- 🎯 Error Handling: Implementasikan proper error handling di frontend dan backend
- ⚡ Performance: Gunakan debouncing untuk mencegah request berlebihan
- 📱 User Experience: Tampilkan loading state saat request sedang berjalan
- 🔄 Fallback: Sediakan fallback untuk browser yang tidak support AJAX