Ćwiczenie – Operacje CRUD w PHP i MySQL (INF.03)

1. Przygotowanie bazy danych

Cel: Uczeń potrafi utworzyć bazę danych i tabelę do testów CRUD.
Polecenie: Utwórz bazę crud_test i tabelę users z polami: id, name, email, age.
CREATE DATABASE crud_test;
USE crud_test;

CREATE TABLE users (
    id    INT          AUTO_INCREMENT PRIMARY KEY,
    name  VARCHAR(100) NOT NULL,
    email VARCHAR(100) NOT NULL,
    age   INT          NOT NULL
);
  
Baza danych crud_test oraz tabela users zostały utworzone.

2. Połączenie z bazą danych (PDO)

Cel: Uczeń potrafi nawiązać połączenie PHP z MySQL przez PDO.
Plik: db.php
ℹ️ Dlaczego PDO, a nie mysqli?
PDO (PHP Data Objects) obsługuje Prepared Statements, które chronią przed SQL Injection. Jeden plik db.php dołączamy przez require_once do każdego innego pliku — wtedy zmienna $pdo jest od razu dostępna.
<?php
// db.php – plik konfiguracyjny połączenia z bazą
// Dołączaj go w innych plikach przez: require_once 'db.php';

$host    = 'localhost';
$baza    = 'crud_test';
$user    = 'root';
$haslo   = '';          // XAMPP: domyślnie puste
$charset = 'utf8mb4';

$dsn = "mysql:host=$host;dbname=$baza;charset=$charset";

$opcje = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,  // rzucaj wyjątki przy błędach
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,        // wyniki jako tablice asocjacyjne
    PDO::ATTR_EMULATE_PREPARES   => false,                   // prawdziwe prepared statements
];

try {
    $pdo = new PDO($dsn, $user, $haslo, $opcje);
} catch (PDOException $e) {
    die("Błąd połączenia: " . $e->getMessage());
}
?>
  
Połączenie z bazą danych zostało nawiązane przez PDO.

3. CREATE – Dodawanie danych

Cel: Uczeń potrafi dodać nowy rekord do tabeli users przez Prepared Statement.
Polecenie: Utwórz formularz dodający użytkownika (imię, email, wiek).
🛡️ Prepared Statement – dane z formularza ($_POST) nigdy nie trafiają bezpośrednio do zapytania SQL. PDO traktuje je jako parametry, nie jako kod SQL. To chroni przed SQL Injection.
<?php
// create.php
require_once 'db.php';   // $pdo gotowy

$sukces = '';
$blad   = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Pobieramy dane i usuwamy białe znaki z początku/końca
    $name  = trim($_POST['name']  ?? '');
    $email = trim($_POST['email'] ?? '');
    $age   = (int)($_POST['age']  ?? 0);

    // Prosta walidacja przed zapisem
    if ($name === '' || $email === '' || $age <= 0) {
        $blad = "Wypełnij wszystkie pola poprawnie.";
    } else {
        try {
            // Krok 1: przygotuj zapytanie z placeholderami :name :email :age
            $stmt = $pdo->prepare(
                "INSERT INTO users (name, email, age) VALUES (:name, :email, :age)"
            );
            // Krok 2: wykonaj i przekaż dane – PDO samo je zabezpieczy
            $stmt->execute([':name' => $name, ':email' => $email, ':age' => $age]);
            $sukces = "✅ Użytkownik dodany! ID = " . $pdo->lastInsertId();
        } catch (PDOException $e) {
            $blad = "Błąd zapisu: " . $e->getMessage();
        }
    }
}
?>

<?php if ($sukces): ?><p><?= htmlspecialchars($sukces) ?></p><?php endif; ?>
<?php if ($blad):   ?><p style="color:red"><?= htmlspecialchars($blad) ?></p><?php endif; ?>

<form method="POST">
  Imię:  <input type="text"   name="name"  required><br>
  Email: <input type="email"  name="email" required><br>
  Wiek:  <input type="number" name="age"   required min="1"><br>
  <button type="submit">Dodaj użytkownika</button>
</form>
  
Formularz dodawania użytkowników działa poprawnie.

4. READ – Wyświetlanie danych

Cel: Odczytanie wszystkich danych z tabeli i wyświetlenie w tabeli HTML.
Plik: index.php
🔒 htmlspecialchars() – każda wartość wyświetlana w HTML powinna być przez nią przepuszczona. Chroni przed XSS (wstrzykiwaniem kodu HTML/JS przez dane z bazy).
<?php
// index.php
require_once 'db.php';

// query() wystarczy gdy nie ma parametrów od użytkownika
$stmt  = $pdo->query("SELECT * FROM users ORDER BY id DESC");
$users = $stmt->fetchAll();   // pobiera wszystkie wiersze naraz
?>

<table class="effect-table">
  <tr>
    <th>ID</th><th>Imię</th><th>Email</th><th>Wiek</th><th>Akcje</th>
  </tr>
  <?php foreach ($users as $row): ?>
  <tr>
    <td><?= htmlspecialchars($row['id'])    ?></td>
    <td><?= htmlspecialchars($row['name'])  ?></td>
    <td><?= htmlspecialchars($row['email']) ?></td>
    <td><?= htmlspecialchars($row['age'])   ?></td>
    <td>
      <a href="update.php?id=<?= (int)$row['id'] ?>">✏️ Edytuj</a> |
      <a href="delete.php?id=<?= (int)$row['id'] ?>">🗑️ Usuń</a>
    </td>
  </tr>
  <?php endforeach; ?>
</table>
  
IDImięEmailWiekAkcje
1Janjan@wp.pl25✏️ Edytuj | 🗑️ Usuń
2Agnieszkaaga@onet.pl31✏️ Edytuj | 🗑️ Usuń

5. UPDATE – Edycja danych

Cel: Uczeń potrafi edytować istniejący rekord przez Prepared Statement.
Plik: update.php
⚠️ ID z URL zawsze rzutuj na int!
$id = (int)$_GET['id']; – nawet jeśli ktoś wpisze w URL złośliwy tekst, rzutowanie na int zamieni go na 0. Bezpieczne i proste.
<?php
// update.php
require_once 'db.php';

// ID z URL – zawsze rzutujemy na int, nigdy nie ufamy $_GET bezpośrednio
$id = (int)($_GET['id'] ?? 0);

if ($id <= 0) {
    die("Nieprawidłowe ID.");
}

// Pobierz aktualny rekord przez Prepared Statement
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
$row = $stmt->fetch();

if (!$row) {
    die("Użytkownik nie istnieje.");
}

$sukces = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $name  = trim($_POST['name']  ?? '');
    $email = trim($_POST['email'] ?? '');
    $age   = (int)($_POST['age']  ?? 0);

    if ($name !== '' && $email !== '' && $age > 0) {
        $stmt = $pdo->prepare(
            "UPDATE users SET name = :name, email = :email, age = :age WHERE id = :id"
        );
        $stmt->execute([':name' => $name, ':email' => $email, ':age' => $age, ':id' => $id]);
        $sukces = "✅ Dane zaktualizowane!";
        // Odświeżamy $row żeby formularz pokazał nowe dane
        $row = ['name' => $name, 'email' => $email, 'age' => $age];
    }
}
?>

<?php if ($sukces): ?>
  <p><?= htmlspecialchars($sukces) ?> <a href="index.php">Powrót do listy</a></p>
<?php endif; ?>

<form method="POST">
  Imię:  <input type="text"   name="name"  value="<?= htmlspecialchars($row['name'])  ?>" required><br>
  Email: <input type="email"  name="email" value="<?= htmlspecialchars($row['email']) ?>" required><br>
  Wiek:  <input type="number" name="age"   value="<?= htmlspecialchars($row['age'])   ?>" required min="1"><br>
  <button type="submit">Zapisz zmiany</button>
</form>
  
Dane użytkownika można teraz edytować.

6. DELETE – Usuwanie danych

Cel: Uczeń potrafi usunąć rekord z tabeli.
Plik: delete.php
⚠️ Nigdy nie usuwaj przez samo GET bez potwierdzenia!
W prawdziwej aplikacji dodaj potwierdzenie (np. formularz POST z tokenem CSRF). Tu dla uproszczenia używamy GET z rzutowaniem ID na int.
<?php
// delete.php
require_once 'db.php';

$id = (int)($_GET['id'] ?? 0);

if ($id <= 0) {
    die("Nieprawidłowe ID.");
}

$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
$stmt->execute([$id]);

echo "🗑️ Użytkownik usunięty. <a href='index.php'>Powrót do listy</a>";
?>
  
Użytkownik został pomyślnie usunięty z bazy.