Podstawy MySQL i Połączenie z PHP (PDO)

INF.03.7 (2) — Komunikacja serwera aplikacji z bazą danych

Wprowadzenie Baza Danych i PHP

MySQL (lub MariaDB) to najpopularniejszy system zarządzania relacyjnymi bazami danych (RDBMS) używany z PHP. Aby połączyć się z bazą danych i wykonywać zapytania SQL, używamy w PHP rozszerzenia PDO (PHP Data Objects).

  • RDBMS: Bazy przechowują dane w tabelach z relacjami (np. tabela „Klienci" powiązana z tabelą „Zamówienia").
  • SQL: Structured Query Language — język komunikacji z bazą (pobieranie, dodawanie, modyfikowanie danych).
  • PDO: Nowoczesny, bezpieczny i uniwersalny interfejs do baz danych w PHP (zalecany zamiast starszego mysqli_).

Baza danych GameVault — inicjalizacja krok po kroku

Zanim napiszesz pierwszy skrypt PHP, musisz mieć działającą bazę danych. Przejdź kolejno przez 4 kroki.

01
Uruchom XAMPP

Otwórz XAMPP Control Panel. Kliknij Start przy Apache i MySQL. Oba muszą świecić na zielono.

02
Otwórz phpMyAdmin

W przeglądarce wejdź na http://localhost/phpmyadmin. Logowanie: użytkownik root, hasło puste (XAMPP domyślnie).

03
Utwórz bazę

Kliknij Nowa w lewym panelu. Wpisz nazwę gamevault, kodowanie utf8mb4_unicode_ci, kliknij Utwórz.

04
Wklej skrypt SQL

Wybierz bazę gamevault, kliknij zakładkę SQL, wklej skrypt z sekcji poniżej i kliknij Wykonaj.

Schemat relacji tabel (ERD)

🧑 gracze
🔑 gracz_idINT PK AI
nickVARCHAR(50) UQ
emailVARCHAR(100)
krajVARCHAR(3)
────
1 : wiele
🛒 zakupy
🔑 zakup_idINT PK AI
🔗 gracz_idFK → gracze
🔗 gra_idFK → gry
data_zakupuDATE
cena_zakupuDECIMAL(7,2)
────
wiele : 1
🎮 gry
🔑 gra_idINT PK AI
tytulVARCHAR(100)
gatunekVARCHAR(50)
cenaDECIMAL(7,2)
🔗 wydawca_idFK → wydawcy
🔑 PK = klucz główny (PRIMARY KEY) 🔗 FK = klucz obcy (FOREIGN KEY) AI = AUTO_INCREMENT  ·  UQ = UNIQUE

Opis tabel

🧑 gracze

Konta użytkowników sklepu. gracz_id to unikalny identyfikator (PK). nick jest UNIQUE — dwóch graczy nie może mieć tego samego nicku. Jeden gracz może mieć wiele zakupów.

🎮 gry

Katalog gier w sklepie. gatunek pozwala filtrować po typie (RPG, FPS…). wydawca_id to klucz obcy — każda gra należy do wydawcy. CHECK pilnuje, że cena nie jest ujemna.

🛒 zakupy

Tabela łącząca graczy z grami — realizuje relację wiele-do-wielu. Jeden gracz kupuje wiele gier, jedną grę może kupić wielu graczy. Przechowuje historyczną cenę i datę zakupu.

Skrypt inicjalizacyjny — wklej do phpMyAdmin → zakładka SQL

USE gamevault; -- Czyszczenie — na wypadek ponownego uruchomienia skryptu -- Kolejność: najpierw tabele podrzędne (te z kluczami obcymi) DROP TABLE IF EXISTS zakupy; DROP TABLE IF EXISTS gry; DROP TABLE IF EXISTS gracze; DROP TABLE IF EXISTS wydawcy; -- Tabela: wydawcy -- Firmy wydające gry (CD Projekt, Valve itp.) -- AUTO_INCREMENT: baza nadaje ID automatycznie (1, 2, 3...) CREATE TABLE wydawcy ( wydawca_id INT PRIMARY KEY AUTO_INCREMENT, nazwa VARCHAR(100) NOT NULL, kraj VARCHAR(3) ); -- Tabela: gracze -- Konta użytkowników sklepu GameVault -- UNIQUE na nick: dwóch graczy nie może mieć tego samego nicku CREATE TABLE gracze ( gracz_id INT PRIMARY KEY AUTO_INCREMENT, nick VARCHAR(50) NOT NULL UNIQUE, email VARCHAR(100), kraj VARCHAR(3) ); -- Tabela: gry -- Katalog gier dostępnych w sklepie -- CHECK: cena nie może być ujemna -- FOREIGN KEY: każda gra musi mieć istniejącego wydawcę CREATE TABLE gry ( gra_id INT PRIMARY KEY AUTO_INCREMENT, tytul VARCHAR(100) NOT NULL, gatunek VARCHAR(50), cena DECIMAL(7,2) NOT NULL CHECK (cena >= 0), wydawca_id INT, FOREIGN KEY (wydawca_id) REFERENCES wydawcy(wydawca_id) ); -- Tabela: zakupy -- Realizuje relację wiele-do-wielu: gracze ↔ gry -- ON DELETE CASCADE: usunięcie gracza usuwa też jego zakupy automatycznie CREATE TABLE zakupy ( zakup_id INT PRIMARY KEY AUTO_INCREMENT, gracz_id INT NOT NULL, gra_id INT NOT NULL, data_zakupu DATE DEFAULT (CURRENT_DATE), cena_zakupu DECIMAL(7,2) NOT NULL, FOREIGN KEY (gracz_id) REFERENCES gracze(gracz_id) ON DELETE CASCADE, FOREIGN KEY (gra_id) REFERENCES gry(gra_id) ); -- Dane testowe INSERT INTO wydawcy (nazwa, kraj) VALUES ('CD Projekt', 'PL'), ('Valve', 'US'), ('IndieSoft', 'DE'); INSERT INTO gry (tytul, gatunek, cena, wydawca_id) VALUES ('Cyberpunk 2077', 'RPG', 199.99, 1), ('Half-Life: Alyx', 'FPS', 149.99, 2), ('Portal 2', 'Puzzle', 39.99, 2), ('PixelDungeon', 'RPG', 19.99, 3), ('SpeedRacer VR', 'Racing', 89.99, 3); INSERT INTO gracze (nick, email, kraj) VALUES ('ShadowWolf', 'wolf@gv.pl', 'PL'), ('NeonByte', 'neon@gv.us', 'US'), ('PixelQueen', 'queen@gv.de', 'DE'), ('NoobMaster', 'noob@gv.pl', 'PL'); -- gracz bez zakupów INSERT INTO zakupy (gracz_id, gra_id, data_zakupu, cena_zakupu) VALUES (1, 1, '2024-11-01', 199.99), -- ShadowWolf → Cyberpunk (1, 3, '2024-11-15', 39.99), -- ShadowWolf → Portal 2 (2, 2, '2024-12-01', 149.99), -- NeonByte → Half-Life (3, 4, '2025-01-10', 19.99), -- PixelQueen → PixelDungeon (3, 1, '2025-01-20', 199.99); -- PixelQueen → Cyberpunk -- Weryfikacja — sprawdź czy dane są poprawne SELECT * FROM gracze; SELECT * FROM gry; SELECT * FROM zakupy;
💡 Po wklejeniu skryptu kliknij Wykonaj w phpMyAdmin. W lewym panelu pojawią się 4 tabele: gracze, gry, zakupy, wydawcy.

PDO Krok 1: Nawiązanie Połączenia

Połączenie z bazą danych ustanawiamy przez obiekt PDO. Wszelkie operacje bazodanowe wykonuje się za jego pomocą. Zapisz poniższy kod jako polaczenie.php i dołączaj go do kolejnych plików przez require_once 'polaczenie.php';.

<?php
    $host = 'localhost';
    $baza = 'gamevault';  // nazwa bazy utworzonej w phpMyAdmin
    $user = 'root';
    $haslo = '';          // XAMPP: domyślnie puste
    $charset = 'utf8mb4';

    // DSN = Data Source Name — mówi PDO jak i gdzie się połączyć
    $dsn = "mysql:host=$host;dbname=$baza;charset=$charset";
    $opcje = [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,   // błędy jako wyjątki
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,       // $row['kolumna']
        PDO::ATTR_EMULATE_PREPARES   => false,
    ];

    try {
        $pdo = new PDO($dsn, $user, $haslo, $opcje);
        // echo "Połączono pomyślnie!";
    } catch (\PDOException $e) {
        echo "Błąd połączenia: " . $e->getMessage();
    }
?>

Konstrukcja try...catch jest kluczowa — obsługuje błędy połączenia (np. brak serwera lub złe hasło).

SQL Krok 2: Proste Zapytania (SELECT)

Do pobierania danych używamy instrukcji SQL SELECT. Wywołaj metodę query() na obiekcie PDO, a następnie iteruj przez wyniki pętlą.

Instrukcja SELECT i wyświetlanie wyników

SELECT nick, kraj FROM gracze WHERE kraj = 'PL' ORDER BY nick;
<?php
    require_once 'polaczenie.php';  // $pdo gotowy do użycia

    $stmt = $pdo->query('SELECT nick, kraj FROM gracze ORDER BY nick');

    echo "<ul>";
    // Pobieramy wiersz po wierszu — $row to tablica asocjacyjna
    while ($row = $stmt->fetch()) {
        // $row['nick'] i $row['kraj'] to klucze = nazwy kolumn SQL
        echo "<li>{$row['nick']} ({$row['kraj']})</li>";
    }
    echo "</ul>";
?>

Ponadpodstawowe Krok 3: Złożone Zapytania (JOIN)

Aby połączyć dane z kilku tabel w jednym wyniku, używamy instrukcji JOIN (łączenia).

  • Relacja 1:Wiele: Jeden gracz ma wiele zakupów.
  • INNER JOIN: Zwraca tylko wiersze powiązane w obu tabelach.

Przykład JOIN — zakupy graczy z tytułami gier

SELECT g.nick AS gracz, gr.tytul AS gra, z.data_zakupu
FROM gracze g
INNER JOIN zakupy z ON g.gracz_id = z.gracz_id
INNER JOIN gry gr ON z.gra_id = gr.gra_id
ORDER BY g.nick;
<?php
    require_once 'polaczenie.php';

    $sql = 'SELECT g.nick AS gracz, gr.tytul AS gra, z.data_zakupu
            FROM gracze g
            JOIN zakupy z ON g.gracz_id = z.gracz_id
            JOIN gry gr ON z.gra_id = gr.gra_id
            ORDER BY g.nick';

    $zakupy = $pdo->query($sql)->fetchAll();

    foreach ($zakupy as $r) {
        echo "<p>{$r['gracz']} kupił: {$r['gra']} ({$r['data_zakupu']})</p>";
    }
?>
⚠️ Bezpieczeństwo! Nigdy nie wstawiaj danych od użytkownika bezpośrednio do zapytania ("SELECT * WHERE nick='" . $_GET['n'] . "'"). Używaj Prepared Statements, aby uniknąć ataków SQL Injection.

Ćwiczenie Zadania Praktyczne

Zadanie 1: Połączenie i Proste Pobieranie (Podstawowe)

Połącz się z bazą gamevault i wyświetl listę wszystkich graczy.

Zadanie 2: Prepared Statement z Parametrem (Ponadpodstawowe)

Pobierz gry wybranego gatunku — gatunek jako parametr URL (?gatunek=RPG).

Rozwiązania Przykładowy Kod PHP

Rozwiązanie Zadania 1 — polaczenie.php + gracze.php

<?php // polaczenie.php
$host = 'localhost'; $baza = 'gamevault'; $user = 'root'; $haslo = '';
$dsn = "mysql:host=$host;dbname=$baza;charset=utf8mb4";
$opcje = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
          PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC];
try { $pdo = new PDO($dsn, $user, $haslo, $opcje); }
catch (\PDOException $e) { echo "Błąd: " . $e->getMessage(); exit; }
?>

<?php // gracze.php
require_once 'polaczenie.php';
$stmt = $pdo->query('SELECT nick, kraj FROM gracze ORDER BY nick');
echo "<h2>Lista graczy GameVault</h2><ul>";
while ($row = $stmt->fetch()) {
    echo "<li>{$row['nick']} ({$row['kraj']})</li>";
}
echo "</ul>";
?>

Rozwiązanie Zadania 2 — Prepared Statement z parametrem URL

<?php // gry.php
require_once 'polaczenie.php';

// Pobieramy parametr z URL: ?gatunek=RPG (domyślnie RPG)
$gatunek = $_GET['gatunek'] ?? 'RPG';

// Krok 1: przygotuj zapytanie z placeholderem ?
$stmt = $pdo->prepare('SELECT tytul, cena FROM gry WHERE gatunek = ? ORDER BY cena DESC');

// Krok 2: wykonaj bezpiecznie — PDO samo zabezpieczy dane
$stmt->execute([$gatunek]);
$gry = $stmt->fetchAll();

echo "<h2>Gry z gatunku: $gatunek</h2>";
echo "<table border='1' cellpadding='8'><tr><th>Tytuł</th><th>Cena</th></tr>";
foreach ($gry as $g) {
    echo "<tr><td>{$g['tytul']}</td><td>{$g['cena']} zł</td></tr>";
}
echo "</table>";
?>

Materiały Narzędzia i Linki

  • PHP Manual: PDO Manual — oficjalna dokumentacja, podstawa nowoczesnego PHP.
  • W3Schools SQL: SQL JOIN — wizualizacja różnych typów łączeń tabel.
  • Bezpieczeństwo: Wyszukaj „PHP PDO prepared statements" — klucz do bezpiecznej komunikacji z bazą danych.
Materiały: Podstawy MySQL i Połączenie z PHP · Opracowanie na podstawie wymagań INF.03 | Autor: Tomasz Puchała (toloki.pl)