Nie od dziś wiemy, że w teraźniejszym, cyfrowym świecie, zarządzanie danymi to klucz do sukcesu wielu aplikacji webowych. W artykule dowiecie się, czym jest CRUD, jak z niego korzystać, jak przydatnym „zlepkiem narzędzi” jest ten akronim.
PHP i MySQL są potężnym duetem, który umożliwia tworzenie dynamicznych i interaktywnych stron internetowych z zaawansowanym zarządzaniem danymi. Owszem, i zgodzę się z tym, że znajdą się zwolennicy używania PHP i MySQL do tworzenia dynamicznych stron WWW tak samo jak przeciwnicy, którzy uważają, że są znacznie wydajniejsze technologie, a tak w ogóle to PHP to nie programowanie 🙂 Ale nie o tym tu i teraz.
Tu przyjrzymy się, jak można zaimplementować operacje CRUD (Create, Read, Update, Delete) w PHP 8 i MySQL, tworząc prostą bazę danych i interfejs użytkownika do zarządzania danymi.
Czym jest CRUD?
CRUD to akronim od angielskich słów Create, Read, Update, Delete, które odnoszą się do czterech podstawowych funkcji zarządzania danymi w bazach danych.
- Create – tworzenie nowych rekordów;
- Read – odczytywanie istniejących rekordów;
- Update – aktualizowanie istniejących rekordów;
- Delete – usuwanie istniejących rekordów.
Na tych 4 elementach skupimy się w tym artykule, tworząc przykładową bazę danych i ją za pomocą CRUD edytując.
Utworzenie bazy danych i tabeli
Pierwszym krokiem jest utworzenie bazy danych MySQL, która będzie przechowywać nasze dane. Użyjemy do tego MySQL Workbench, phpMyAdmin lub innego narzędzia do zarządzania bazą danych.
CREATE DATABASE blogData;
USE blogData;
CREATE TABLE articles (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(50) NOT NULL,
content TEXT,
publication_date TIMESTAMP
);
Kolejno, żebyśmy tak na prawdę mieli na czym wstępnie ćwiczyć, wstawimy kilka przykładowych rekordów, aby móc na nich pracować:
INSERT INTO articles (title, content, publication_date) VALUES
('Pierwszy artykuł', 'To jest treść pierwszego artykułu.', NOW()),
('Drugi artykuł', 'To jest treść drugiego artykułu.', NOW());
Implementacja CRUD w PHP 8
Teraz, kiedy mamy bazę danych i tabelę, przejdźmy do implementacji funkcji CRUD w PHP. Poniżej znajduje się przykładowy kod pokazujący, jak te operacje mogą być wykonane.
Połączenie z bazą danych
Utworzymy plik db.php
, który będzie odpowiedzialny za nawiązanie połączenia z bazą danych. Dobrą praktyką jest trzymanie w oddzielnym pliku danych konfiguracyjnych, związanych z połączeniem z bazą, podobnie jak trzymanie wszystkich funkcji w 1 pliku, odseparowywanie stałych elementów widoku stron do oddzielnych plików, czy tworzenie plików ze stylami, np. style.css w którym trzymane są wszystkie nasze kaskadowe arkusze styli, klasy, identyfikatory. Jest to o tyle ważne, by w chwili konieczności dokonania zmiany, która ma zadziałać globalnie, dokonywać ją tylko w 1 pliku, include’owanym do wszystkich widoków naszej strony/serwisu, którego dotyczy.
Zawartość pliku db.php
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "blogData";
// Utworzenie połączenia
$conn = new mysqli($servername, $username, $password, $dbname);
// Sprawdzenie połączenia
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
?>
Create – Dodawanie nowych rekordów
Utworzymy prosty formularz HTML, aby użytkownik mógł dodać nowy artykuł, oraz skrypt PHP, który przetworzy formularz i doda rekord do bazy danych.
Formularz (create.html)
<form action="create.php" method="post">
Tytuł: <input type="text" name="title"><br>
Treść: <textarea name="content"></textarea><br>
<input type="submit">
</form>
Skrypt PHP (create.php)
<?php
include 'db.php';
$title = $_POST['title'];
$content = $_POST['content'];
$sql = "INSERT INTO articles (title, content, publication_date) VALUES (?, ?, NOW())";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ss", $title, $content);
$stmt->execute();
echo "Artykuł dodany pomyślnie";
$stmt->close();
$conn->close();
?>
Read – Odczytywanie rekordów
Aby wyświetlić artykuły, użyjemy poniższego skryptu PHP:
Skrypt PHP (read.php)
<?php
include 'db.php';
$sql = "SELECT id, title, content, publication_date FROM articles";
$result = $conn->query($sql);
if (!$result) {
die("Błąd zapytania: " . $conn->error);
}
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
echo "id: " . $row["id"] . " - Tytuł: " . $row["title"] . " - Treść: " . $row["content"] . "<br>";
}
} else {
echo "0 results";
}
$conn->close();
?>
Update i Delete
Aby zająć się tymi dwoma mechanizmami, czyli mechaniką Update, aktualizacji danych poszczególnego rekordu w bazie oraz Delete, czyli możliwością usunięcia rekordu z poziomu PHP, stworzony zostanie plik index.php, w którym wyświetlona zostanie tabela, zawierająca wszystkie rekordy z tabeli, z naszego przykładu. W tabeli zawarte będą też akcje, w postaci linków dynamicznych, kierujące do plików edit.php oraz delete.php. Te jak to nazwałem „akcje” muszą jednak posługiwać się konkretnym identyfikatorem, żeby skrypt wiedział, który rekord w bazie chcemy usunąć lub wyedytować. Przejdźmy więc do realizacji tego problemu:
Plik index.php
wyświetli wszystkie rekordy z tabeli articles
wraz z przyciskami do edycji i usuwania.
Skrypt PHP (index.php)
<?php
include 'db.php';
$sql = "SELECT id, title, publication_date FROM articles";
$result = $conn->query($sql);
?>
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Lista Artykułów</title>
</head>
<body>
<h2>Lista Artykułów</h2>
<table>
<tr>
<th>Tytuł</th>
<th>Data Dodania</th>
<th>Akcje</th>
</tr>
<?php if ($result->num_rows > 0): ?>
<?php while($row = $result->fetch_assoc()): ?>
<tr>
<td><?php echo $row["title"]; ?></td>
<td><?php echo $row["publication_date"]; ?></td>
<td>
<a href="edit.php?id=<?php echo $row["id"]; ?>">Edytuj</a>
<a href="delete.php?id=<?php echo $row["id"]; ?>" onclick="return confirm('Czy na pewno chcesz usunąć ten rekord?');">Usuń</a>
</td>
</tr>
<?php endwhile; ?>
<?php else: ?>
<tr><td colspan="3">Brak artykułów</td></tr>
<?php endif; ?>
</table>
</body>
</html>
<?php
$conn->close();
?>
Skrypt PHP (edit.php)
Strona edit.php
wyświetla formularz do edycji wybranego rekordu.
<?php
include 'db.php';
// Pobieranie danych rekordu na podstawie przekazanego ID
if(isset($_GET['id'])) {
$id = $_GET['id'];
$sql = "SELECT * FROM articles WHERE id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
if($result->num_rows == 1) {
$row = $result->fetch_assoc();
$title = $row['title'];
$content = $row['content'];
} else {
echo "Nie znaleziono artykułu.";
exit;
}
} else {
echo "Nieprawidłowe ID.";
exit;
}
?>
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Edycja Artykułu</title>
</head>
<body>
<h2>Edycja Artykułu</h2>
<form action="update.php" method="post">
<input type="hidden" name="id" value="<?php echo $id; ?>">
Tytuł: <input type="text" name="title" value="<?php echo $title; ?>"><br>
Treść: <textarea name="content"><?php echo $content; ?></textarea><br>
<input type="submit" value="Zaktualizuj">
</form>
</body>
</html>
Skrypt PHP (update.php)
Plik update.php
przetwarza żądanie aktualizacji rekordu.
<?php
include 'db.php';
if(isset($_POST['id']) && isset($_POST['title']) && isset($_POST['content'])) {
$id = $_POST['id'];
$title = $_POST['title'];
$content = $_POST['content'];
$sql = "UPDATE articles SET title = ?, content = ? WHERE id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ssi", $title, $content, $id);
if($stmt->execute()) {
echo "Artykuł zaktualizowany pomyślnie.";
} else {
echo "Błąd podczas aktualizacji artykułu.";
}
$stmt->close();
} else {
echo "Wszystkie pola są wymagane.";
}
$conn->close();
?>
Skrypt PHP (delete.php)
Plik delete.php
odpowiada za usunięcie rekordu.
<?php
include 'db.php';
if(isset($_GET['id'])) {
$id = $_GET['id'];
$sql = "DELETE FROM articles WHERE id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $id);
if($stmt->execute()) {
echo "Artykuł usunięty pomyślnie.";
} else {
echo "Błąd podczas usuwania artykułu.";
}
$stmt->close();
} else {
echo "Nieprawidłowe ID.";
}
$conn->close();
?>
Komentarz
Na koniec, kilka słów komentarza. Otóż pamiętaj, że dla każdego z powyższych skryptów trzeba dostosować dane dostępu do bazy danych w pliku db.php. Jeśli przenosisz swój skrypt, np, ze szkoły do domu, chcesz nad nim nadal pracować, zacznij od sprawdzenia czy dane dostępowe do Twojej bazy w pliku db.php są poprawne na stanowisku, na którym aktualnie pracujesz. Każdy plik php, omówiony w przykładzie posiada na początku include 'db.php';
który to właśnie „include-uje/załącza kod zawarty w db.php tak, by plik docelowy, np. delete.php mógł z niego korzystać.
Omówmy jeszcze kilka zawartych w przykładzie elementów:
Czym jest $stmt->bind_param(„i”, $id);?
Metoda $stmt->bind_param()
w PHP jest używana do przypisania zmiennych do odpowiednich miejsc zastępczych (placeholderów) w zapytaniu SQL przygotowanym przez metodę prepare()
. Pozwala to na bezpieczne przekazywanie zmiennych do zapytania SQL, pomagając zapobiegać atakom SQL Injection poprzez oddzielenie instrukcji SQL od danych wejściowych użytkownika. Metoda bind_param()
jest częścią rozszerzenia mysqli, które umożliwia interakcję z bazami danych MySQL.
W wywołaniu metody bind_param("i", $id);
w kontekście pliku delete.php
, który dotyczy usuwania rekordu:
"i"
– określa typ danych, który zostanie przekazany. W tym przypadku"i"
oznacza, że przekazany argument będzie traktowany jako liczba całkowita (integer
). Jest to ważne, ponieważ informuje bazę danych o typie danych, które są przekazywane, co może być istotne dla poprawnego przetwarzania zapytania. Dostępne typy to:i
– integer (liczba całkowita),d
– double (liczba zmiennoprzecinkowa),s
– string (łańcuch znaków),b
– blob (dane binarne).
$id
– to zmienna zawierająca wartość, która zostanie przekazana do zapytania w miejscu zastępczym. W kontekście usuwania rekordu jest to identyfikator (ID) rekordu, który ma zostać usunięty.
Przykład użycia metody bind_param("i", $id);
w zapytaniu do usunięcia rekordu gwarantuje, że do zapytania zostanie przekazany bezpiecznie identyfikator rekordu, unikając ryzyka wstrzyknięcia szkodliwego kodu SQL. To sprawia, że przygotowane zapytania są rekomendowanym sposobem na interakcję z bazą danych w aplikacjach PHP.
Czym jest $row = $result->fetch_assoc();?
Metoda $result->fetch_assoc()
w PHP jest używana do pobrania pojedynczego wiersza z zestawu wyników zapytania do bazy danych jako tablicę asocjacyjną. W tablicy asocjacyjnej klucze odpowiadają nazwom kolumn w wynikowym wierszu z bazy danych. Metoda ta jest częścią rozszerzenia MySQLi i PDO, służącego do interakcji z bazami danych MySQL.
Kiedy wykonujesz zapytanie do bazy danych za pomocą metod takich jak mysqli_query()
lub przygotowanego zapytania za pomocą mysqli_prepare()
, wynik zwracany przez bazę danych jest reprezentowany przez obiekt klasy mysqli_result
(w przypadku rozszerzenia MySQLi). Aby przetworzyć wyniki zapytania, możesz użyć różnych metod. Jedną z nich jest właśnie fetch_assoc()
, która pozwala na iteracyjne przetwarzanie każdego wiersza wynikowego.
Przykładowo, jeśli masz zapytanie, które zwraca wyniki z tabeli użytkowników, każde wywołanie metody fetch_assoc()
zwróci następny dostępny wiersz wyników jako tablicę asocjacyjną, gdzie klucze tablicy będą nazwami kolumn, a wartości – odpowiadającymi im danymi użytkownika z tego wiersza. Jeśli w tabeli byłyby kolumny id
, name
, i email
, każde wywołanie fetch_assoc()
zwróciłoby coś w stylu:
[
'id' => '1',
'name' => 'Jan Kowalski',
'email' => '[email protected]'
]
Zastosowanie $row = $result->fetch_assoc();
w pętli pozwala iterować przez wszystkie wiersze zwrócone przez zapytanie. Na przykład:
while($row = $result->fetch_assoc()) {
echo $row["name"] . " - " . $row["email"];
}
W tym kodzie dla każdego wiersza wynikowego tworzona jest tablica asocjacyjna $row
, a następnie dane są wyświetlane. Gdy wszystkie wiersze zostaną przetworzone, fetch_assoc()
zwróci null
, co zakończy pętlę while
.
Czym jest zapis $stmt->bind_param(„ssi”, $title, $content, $id); ?
Metoda $stmt->bind_param()
jest częścią rozszerzenia MySQLi w PHP, używaną w kontekście przygotowanych zapytań (prepared statements) do bezpiecznego przekazywania zmiennych do zapytania SQL. Służy do powiązania zmiennych PHP z odpowiednimi parametrami w zapytaniu SQL, które są oznaczone jako miejsca zastępcze znakami zapytania ?
. Dzięki temu mechanizmowi można uniknąć wielu problemów związanych z bezpieczeństwem, takich jak SQL Injection, ponieważ dane przekazywane do bazy danych są automatycznie sanitowane.
Wywołanie $stmt->bind_param("ssi", $title, $content, $id);
wskazuje na kilka rzeczy:
- „ssi” – Ciąg znaków określający typy danych przekazywanych zmiennych. Każda litera w tym ciągu odpowiada jednej zmiennej, która zostanie przekazana do zapytania, a jej pozycja w ciągu odpowiada kolejności miejsca zastępczego w zapytaniu SQL. W tym przypadku:
s
– oznaczastring
(łańcuch znaków). Pierwszas
odpowiada zmiennej$title
, a drugas
zmiennej$content
.i
– oznaczainteger
(liczba całkowita), co odpowiada zmiennej$id
.
- $title, $content, $id – Są to zmienne PHP, które zostają przekazane do zapytania. Ich wartości zostaną użyte w miejscach zastępczych zapytania SQL w kolejności, w jakiej są podane w
bind_param()
.
Przykład użycia może wyglądać tak:
$sql = "UPDATE articles SET title = ?, content = ? WHERE id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ssi", $title, $content, $id);
W tym przykładzie $title
i $content
są typu string
, natomiast $id
jest typu integer
. Przygotowane zapytanie aktualizuje rekord w tabeli articles
, ustawiając nowe wartości dla kolumn title
i content
dla rekordu o określonym id
.
Użycie przygotowanych zapytań i powiązanie zmiennych z parametrami zapytania w ten sposób jest zalecaną praktyką, ponieważ znacznie zwiększa bezpieczeństwo aplikacji poprzez eliminację ryzyka wstrzyknięcia SQL.