CRUD — dodawanie, edycja i usuwanie rekordów (PDO)
- Baza danych
- poziom: średni
- aktualizacja: 12 lipca 2026
CRUD (Create, Read, Update, Delete) to szkielet niemal każdej aplikacji — od książki adresowej po panel administracyjny. Ten skrypt pokazuje pełny, bezpieczny obieg: listę rekordów, formularz dodawania i edycji oraz usuwanie, wszystko w jednym pliku.
Operacje zmieniające dane wykonują się wyłącznie metodą POST i są chronione tokenem CSRF, a każde zapytanie z danymi użytkownika korzysta z zapytań przygotowanych.
<?php
/**
* CRUD: książka adresowa — skryptyPHP.com
* Zapisz jako: kontakty.php
*
* Najpierw utwórz tabelę (np. w phpMyAdmin):
*
* CREATE TABLE kontakty (
* id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
* imie VARCHAR(100) NOT NULL,
* email VARCHAR(190) NOT NULL,
* telefon VARCHAR(20) NOT NULL DEFAULT '',
* utworzono DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_polish_ci;
*/
session_start();
require 'baza.php'; // funkcja polacz_z_baza() — skrypt „Połączenie z bazą (PDO)"
$pdo = polacz_z_baza();
// ── Token CSRF: chroni formularze przed wywołaniem z obcej strony ──
if (empty($_SESSION['csrf'])) {
$_SESSION['csrf'] = bin2hex(random_bytes(32));
}
function sprawdz_csrf(): void
{
if (!hash_equals($_SESSION['csrf'], $_POST['csrf'] ?? '')) {
http_response_code(403);
exit('Nieprawidłowy token bezpieczeństwa.');
}
}
$bledy = [];
$edytowany = null;
// ── ZAPIS: dodawanie i aktualizacja (POST) ──────────────────
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['zapisz'])) {
sprawdz_csrf();
$id = (int) ($_POST['id'] ?? 0);
$imie = trim($_POST['imie'] ?? '');
$email = trim($_POST['email'] ?? '');
$telefon = trim($_POST['telefon'] ?? '');
if ($imie === '' || mb_strlen($imie) > 100) {
$bledy[] = 'Podaj imię i nazwisko (do 100 znaków).';
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$bledy[] = 'Podaj poprawny adres e-mail.';
}
if (!$bledy) {
if ($id > 0) { // aktualizacja
$stmt = $pdo->prepare(
'UPDATE kontakty SET imie = ?, email = ?, telefon = ? WHERE id = ?'
);
$stmt->execute([$imie, $email, $telefon, $id]);
} else { // nowy rekord
$stmt = $pdo->prepare(
'INSERT INTO kontakty (imie, email, telefon) VALUES (?, ?, ?)'
);
$stmt->execute([$imie, $email, $telefon]);
}
header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?')); // wzorzec PRG
exit;
}
}
// ── USUWANIE (POST) ─────────────────────────────────────────
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['usun'])) {
sprawdz_csrf();
$stmt = $pdo->prepare('DELETE FROM kontakty WHERE id = ?');
$stmt->execute([(int) $_POST['usun']]);
header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?'));
exit;
}
// ── EDYCJA: wczytanie rekordu do formularza (GET) ───────────
if (isset($_GET['edytuj'])) {
$stmt = $pdo->prepare('SELECT * FROM kontakty WHERE id = ?');
$stmt->execute([(int) $_GET['edytuj']]);
$edytowany = $stmt->fetch() ?: null;
}
// ── ODCZYT: lista rekordów ──────────────────────────────────
$kontakty = $pdo->query(
'SELECT * FROM kontakty ORDER BY imie ASC'
)->fetchAll();
?>
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Książka adresowa</title>
<style>
body { font-family: sans-serif; max-width: 760px; margin: 20px auto; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ccc; padding: 8px 10px; text-align: left; }
th { background: #f2f2f2; }
form.wiersz { display: inline; }
.bledy { color: #b00020; }
fieldset { margin-bottom: 24px; }
input { padding: 6px; margin: 2px 8px 2px 0; }
</style>
</head>
<body>
<h1>Książka adresowa</h1>
<?php if ($bledy): ?>
<ul class="bledy">
<?php foreach ($bledy as $blad): ?>
<li><?= htmlspecialchars($blad) ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<form method="post" action="">
<fieldset>
<legend><?= $edytowany ? 'Edytuj kontakt' : 'Dodaj kontakt' ?></legend>
<input type="hidden" name="csrf" value="<?= htmlspecialchars($_SESSION['csrf']) ?>">
<input type="hidden" name="id" value="<?= (int) ($edytowany['id'] ?? 0) ?>">
<input type="text" name="imie" placeholder="Imię i nazwisko" required
value="<?= htmlspecialchars($edytowany['imie'] ?? '') ?>">
<input type="email" name="email" placeholder="E-mail" required
value="<?= htmlspecialchars($edytowany['email'] ?? '') ?>">
<input type="text" name="telefon" placeholder="Telefon"
value="<?= htmlspecialchars($edytowany['telefon'] ?? '') ?>">
<button type="submit" name="zapisz" value="1">
<?= $edytowany ? 'Zapisz zmiany' : 'Dodaj' ?>
</button>
<?php if ($edytowany): ?>
<a href="?">anuluj edycję</a>
<?php endif; ?>
</fieldset>
</form>
<table>
<tr><th>Imię i nazwisko</th><th>E-mail</th><th>Telefon</th><th>Akcje</th></tr>
<?php foreach ($kontakty as $k): ?>
<tr>
<td><?= htmlspecialchars($k['imie']) ?></td>
<td><?= htmlspecialchars($k['email']) ?></td>
<td><?= htmlspecialchars($k['telefon']) ?></td>
<td>
<a href="?edytuj=<?= (int) $k['id'] ?>">edytuj</a>
<form class="wiersz" method="post" action=""
onsubmit="return confirm('Usunąć ten kontakt?');">
<input type="hidden" name="csrf"
value="<?= htmlspecialchars($_SESSION['csrf']) ?>">
<button type="submit" name="usun" value="<?= (int) $k['id'] ?>">
usuń
</button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php if (!$kontakty): ?>
<tr><td colspan="4">Brak kontaktów — dodaj pierwszy powyżej.</td></tr>
<?php endif; ?>
</table>
</body>
</html>
Jak użyć tego skryptu
- Utwórz tabelę
kontakty— gotowe zapytanieCREATE TABLEznajdziesz w komentarzu na górze skryptu (wklej je np. w phpMyAdmin). - Obok zapisz plik
baza.phpze skryptu „Bezpieczne połączenie z bazą MySQL (PDO)" i uzupełnij w nim dane dostępowe. - Zapisz kod jako
kontakty.php, wgraj oba pliki na serwer i otwórz w przeglądarce. - Dostosuj kolumny do swoich potrzeb — wystarczy zmienić tabelę, pola formularza i listę kolumn w zapytaniach.
Warto wiedzieć
- Usuwanie i zapis działają wyłącznie przez POST z tokenem CSRF — dzięki temu obca strona nie wywoła tych operacji za plecami zalogowanego użytkownika.
- Po każdej operacji zapisu skrypt przekierowuje (wzorzec Post/Redirect/Get), więc odświeżenie strony nie powtórzy operacji.
- W realnej aplikacji dodaj do tego logowanie (skrypt „Logowanie użytkowników") — panel do edycji danych nie powinien być publiczny.
Podobne skrypty
Bezpieczne połączenie z bazą MySQL (PDO)
Wzorcowe połączenie z bazą MySQL przez PDO: kodowanie utf8mb4, obsługa błędów przez wyjątki, wyłączona emulacja zapytań i przykład zapytania przygotowanego.
początkujący Zobacz kod → Baza danychPaginacja (stronicowanie) wyników z bazy danych
Stronicowanie wyników zapytania SQL z linkami 1 … 4 5 6 … 20, poprawnym LIMIT/OFFSET na parametrach oraz zabezpieczeniem numeru strony.
średni Zobacz kod → Baza danychWyszukiwarka na stronie z podświetlaniem wyników
Prosta, bezpieczna wyszukiwarka rekordów w MySQL: LIKE z poprawnym maskowaniem znaków specjalnych, obsługa wielu słów naraz i podświetlanie znalezionej frazy.
średni Zobacz kod →