🎯 Czego się nauczysz
CRUD = Create, Read, Update, Delete
- Łączenie PHP z MySQL przez PDO
- Wyświetlanie danych z bazy
- Dodawanie nowych rekordów
- Edycja istniejących danych
- Usuwanie rekordów
- Zabezpieczenia przed SQL Injection
📥 Pobierz bazę danych
Baza ksiazki_db - katalog książek w bibliotece
📁 Struktura projektu
W folderze htdocs (XAMPP) utwórz folder "biblioteka" z plikami:
biblioteka/
├── config.php (połączenie z bazą)
├── index.php (lista książek)
├── dodaj.php (dodawanie)
├── edytuj.php (edycja)
└── usun.php (usuwanie)
🔌 Plik 1: config.php
config.php
<?php
$host = 'localhost';
$dbname = 'ksiazki_db';
$username = 'root';
$password = '';
try {
$pdo = new PDO(
"mysql:host=$host;dbname=$dbname;charset=utf8mb4",
$username,
$password
);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Błąd połączenia: " . $e->getMessage());
}
?>
Wyjaśnienie: PDO (PHP Data Objects) to bezpieczny sposób łączenia z bazą danych. Używamy prepared statements, które chronią przed SQL Injection.
📖 Plik 2: index.php (Lista)
index.php
<?php
require_once 'config.php';
// Pobierz wszystkie książki z bazy
$sql = "SELECT * FROM ksiazki ORDER BY tytul";
$stmt = $pdo->query($sql);
$ksiazki = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Biblioteka</title>
<style>
body { font-family: Arial; max-width: 1200px; margin: 0 auto; padding: 20px; }
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
th, td { padding: 12px; border: 1px solid #ddd; }
th { background: #f093fb; color: white; }
.btn { padding: 8px 15px; text-decoration: none; border-radius: 5px; }
.btn-add { background: #4caf50; color: white; }
.btn-edit { background: #2196f3; color: white; }
.btn-delete { background: #f44336; color: white; }
</style>
</head>
<body>
<h1>📚 Biblioteka - Katalog Książek</h1>
<a href="dodaj.php" class="btn btn-add">➕ Dodaj książkę</a>
<table>
<tr>
<th>ID</th>
<th>Tytuł</th>
<th>Autor</th>
<th>Rok</th>
<th>Gatunek</th>
<th>Akcje</th>
</tr>
<?php foreach ($ksiazki as $k): ?>
<tr>
<td><?php echo htmlspecialchars($k['id']); ?></td>
<td><?php echo htmlspecialchars($k['tytul']); ?></td>
<td><?php echo htmlspecialchars($k['autor']); ?></td>
<td><?php echo htmlspecialchars($k['rok_wydania']); ?></td>
<td><?php echo htmlspecialchars($k['gatunek']); ?></td>
<td>
<a href="edytuj.php?id=<?php echo $k['id']; ?>" class="btn btn-edit">Edytuj</a>
<a href="usun.php?id=<?php echo $k['id']; ?>" class="btn btn-delete"
onclick="return confirm('Usunąć?')">Usuń</a>
</td>
</tr>
<?php endforeach; ?>
</table>
</body>
</html>
Bezpieczeństwo: Funkcja
htmlspecialchars() chroni przed atakami XSS, zamieniając znaki specjalne na bezpieczne encje HTML.
➕ Plik 3: dodaj.php (Dodawanie)
dodaj.php
<?php
require_once 'config.php';
$komunikat = '';
$blad = '';
// Sprawdź czy formularz został wysłany
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$tytul = trim($_POST['tytul']);
$autor = trim($_POST['autor']);
$rok = trim($_POST['rok_wydania']);
$gatunek = trim($_POST['gatunek']);
// Walidacja danych
if (empty($tytul) || empty($autor)) {
$blad = "Tytuł i autor są wymagane!";
} else {
// Prepared statement - bezpieczne zapytanie
$sql = "INSERT INTO ksiazki (tytul, autor, rok_wydania, gatunek)
VALUES (:tytul, :autor, :rok, :gatunek)";
try {
$stmt = $pdo->prepare($sql);
$stmt->execute([
':tytul' => $tytul,
':autor' => $autor,
':rok' => $rok,
':gatunek' => $gatunek
]);
$komunikat = "Książka dodana!";
header("refresh:2;url=index.php");
} catch (PDOException $e) {
$blad = "Błąd: " . $e->getMessage();
}
}
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Dodaj książkę</title>
</head>
<body>
<h1>Dodaj książkę</h1>
<?php if ($komunikat): ?>
<div style="background:#d4edda;padding:15px;margin:10px 0;">
<?php echo $komunikat; ?>
</div>
<?php endif; ?>
<?php if ($blad): ?>
<div style="background:#f8d7da;padding:15px;margin:10px 0;">
<?php echo $blad; ?>
</div>
<?php endif; ?>
<form method="POST">
<p><label>Tytuł:</label><br>
<input type="text" name="tytul" required></p>
<p><label>Autor:</label><br>
<input type="text" name="autor" required></p>
<p><label>Rok wydania:</label><br>
<input type="number" name="rok_wydania"></p>
<p><label>Gatunek:</label><br>
<select name="gatunek">
<option value="">-- Wybierz --</option>
<option>Fantasy</option>
<option>Kryminał</option>
<option>Romans</option>
<option>Science Fiction</option>
</select></p>
<button type="submit">Zapisz</button>
</form>
<p><a href="index.php">← Powrót</a></p>
</body>
</html>
Prepared Statements: Parametry
:tytul, :autor itd. są bezpiecznie wstawiane przez PDO, co chroni przed SQL Injection!
✏️ Plik 4: edytuj.php (Edycja)
edytuj.php
<?php
require_once 'config.php';
if (!isset($_GET['id'])) {
header("Location: index.php");
exit();
}
$id = (int)$_GET['id'];
// Pobierz dane książki
$sql = "SELECT * FROM ksiazki WHERE id = :id";
$stmt = $pdo->prepare($sql);
$stmt->execute([':id' => $id]);
$ksiazka = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$ksiazka) {
header("Location: index.php");
exit();
}
// Obsługa formularza
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$sql = "UPDATE ksiazki SET tytul=:tytul, autor=:autor,
rok_wydania=:rok, gatunek=:gatunek WHERE id=:id";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':tytul' => $_POST['tytul'],
':autor' => $_POST['autor'],
':rok' => $_POST['rok_wydania'],
':gatunek' => $_POST['gatunek'],
':id' => $id
]);
header("Location: index.php");
exit();
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Edytuj książkę</title>
</head>
<body>
<h1>Edytuj książkę</h1>
<form method="POST">
<p><label>Tytuł:</label><br>
<input type="text" name="tytul"
value="<?php echo htmlspecialchars($ksiazka['tytul']); ?>"></p>
<p><label>Autor:</label><br>
<input type="text" name="autor"
value="<?php echo htmlspecialchars($ksiazka['autor']); ?>"></p>
<p><label>Rok:</label><br>
<input type="number" name="rok_wydania"
value="<?php echo htmlspecialchars($ksiazka['rok_wydania']); ?>"></p>
<p><label>Gatunek:</label><br>
<input type="text" name="gatunek"
value="<?php echo htmlspecialchars($ksiazka['gatunek']); ?>"></p>
<button type="submit">Zapisz zmiany</button>
</form>
<p><a href="index.php">← Powrót</a></p>
</body>
</html>
🗑️ Plik 5: usun.php (Usuwanie)
usun.php
<?php
require_once 'config.php';
if (!isset($_GET['id'])) {
header("Location: index.php");
exit();
}
$id = (int)$_GET['id'];
$sql = "DELETE FROM ksiazki WHERE id = :id";
$stmt = $pdo->prepare($sql);
$stmt->execute([':id' => $id]);
header("Location: index.php");
?>
🔒 Bezpieczeństwo
1. SQL Injection - używaj Prepared Statements
// ❌ ŹLE
$sql = "SELECT * FROM ksiazki WHERE id = " . $_GET['id'];
// ✅ DOBRZE
$sql = "SELECT * FROM ksiazki WHERE id = :id";
$stmt = $pdo->prepare($sql);
$stmt->execute([':id' => $_GET['id']]);
2. XSS - zawsze escapuj output
// ❌ ŹLE
echo $ksiazka['tytul'];
// ✅ DOBRZE
echo htmlspecialchars($ksiazka['tytul']);
✏️ Zadania praktyczne
Zadanie 1: Uruchom system (ŁATWY)
- Zaimportuj bazę danych ksiazki_db.sql w phpMyAdmin
- Stwórz folder "biblioteka" w htdocs
- Skopiuj wszystkie 5 plików PHP do folderu
- Otwórz http://localhost/biblioteka/
- Dodaj 3 nowe książki
- Edytuj jedną z nich
- Usuń jedną książkę
Zadanie 2: Wyszukiwarka (ŚREDNI)
Dodaj formularz wyszukiwania na stronie głównej:
- Input tekstowy do wpisania frazy
- Wyszukiwanie po tytule LUB autorze (użyj LIKE)
- Przycisk "Wyczyść" wracający do pełnej listy
Wskazówka:
WHERE tytul LIKE '%fraza%' OR autor LIKE '%fraza%'
Zadanie 3: Paginacja (ŚREDNI)
Dodaj stronicowanie - 10 książek na stronę:
- Oblicz liczbę stron: CEIL(liczba_ksiazek / 10)
- Użyj LIMIT i OFFSET w SQL
- Linki do poprzedniej/następnej strony
- Numerowane przyciski stron
Zadanie 4: System logowania (TRUDNY)
Dodaj zabezpieczenie - tylko zalogowani mogą zarządzać:
- Stwórz tabelę "uzytkownicy" (login, haslo_hash)
- Plik login.php z formularzem
- Hashuj hasła: password_hash()
- Używaj sesji: session_start()
- Chroń strony: sprawdzaj isset($_SESSION['user_id'])
🎓 Podsumowanie
✅ Czego się nauczyłeś:
- Łączenia PHP z MySQL przez PDO
- Wyświetlania danych z bazy (SELECT)
- Dodawania rekordów (INSERT)
- Edycji danych (UPDATE)
- Usuwania rekordów (DELETE)
- Zabezpieczeń (Prepared Statements, htmlspecialchars)
- Walidacji danych
Teraz możesz tworzyć dynamiczne aplikacje webowe! 🚀