Transakcje są grupami instrukcji języka SQL, z punktu widzenia przetwarzania traktowane są jako jedna całość – Czyli jeśli coś po drodze zawiedzie – nic nam się nie zapisze do bazy.
Thank you for reading this post, don't forget to subscribe!
Dzisiejsze nasze zadanie będzie następujące.
Napiszemy skrypt, który będzie składał się z dwóch parametryzowanych zapytań. Pierwsze z nich będzie wstawiało nowe dane do tabeli ‘tabela_1’, (gdzie mamy id auto_increment), natomiast drugie również będzie dodawało nowy rekord tylko, że już do tabeli ‘tabela_2’ a w rekordzie będzie zapisywało miedzy innymi id z nowo wstawionego rekordu tabeli ‘tabela_1’. Dane to zapytań będą pochodziły z formularza (którego strukturę już sobie w tym kodzie pominąłem).
Trochę zamotałem – ale myślę, że każdy po analizie poniższego kodu zrozumie o co chodzi:).
Na początku należy pamiętać o połączeniu z bazą danych:
$mysqli = new mysqli("localhost", "my_user", "my_password", "world"); /* check connection */ if (mysqli_connect_errno()) { printf("Connect failed: %s\n", mysqli_connect_error()); exit(); }
następnie odbieramy dane z formularza i otwieramy blok try catch w celu przeprowadzenia naszej transakcji:
//zapisujemy dane z formularza w przykładowych zmiennych. $name = htmlentities($_POST['name'],ENT_QUOTES); $slug = htmlentities($_POST['slug'],ENT_QUOTES); $parent_id = htmlentities($_POST['parent_id'],ENT_QUOTES); $description = htmlentities($_POST['description'],ENT_QUOTES); if($name=='' || $slug==''||$parent_id==''){ $error="Pola : nazwa, uproszczona nazwa, Kategoria nadrzędna - muszą być uzupełnione!
"; }else{ try{ /* ustawiamy autocommit na wylączony */ $mysqli->autocommit(FALSE); $stmt1 = $mysqli->prepare("INSERT INTO tabela_1 (name, slug) VALUES ( ?,?)"); $stmt1->bind_param('ss', $name, $slug); if ($stmt1->execute() == false) { throw new Exception('Zapytanie 1 się nie powiodło'); } //pobranie id nowo stworzonego rekordu tabeli_1 $last_id = $stmt1->insert_id; if($last_id=='' or !is_numeric($last_id)){ throw new Exception('Wynikł problem z id tabeli 1'); }else{ $stmt2 = $mysqli->prepare("INSERT INTO tabela_2 (term_id, taxonomy, description, parent) VALUES ( ?,?,?,?)"); $taxonomy='category'; $stmt2->bind_param('issi', $last_id, $taxonomy, $description,$parent_id); if ($stmt2->execute() == false) { throw new Exception('Zapytanie 2 się nie powiodło'); } $stmt1->close(); $stmt2->close(); $mysqli->commit(); } }catch(Exception $e){ $mysqli->rollback(); echo $e->getMessage(); } }
I w ten oto prosty sposób zapewniamy sobie mechanizm transakcji dla wprowadzania danych z formularza do dwóch różnych tabel.
Dodam jeszcze kilka linijek wyjaśnienia:)
htmlentities($_POST['name'],ENT_QUOTES);
Funkcja htmlentities()
konwertuje znaki specjalne na znaczniki HTML
$mysqli->autocommit(FALSE);
Musimy pamiętać, iż każda wykonywana operacja w MySQL jest wykonywana w transakcji, to znaczy mniej więcej tyle, że każda pojedyncza operacja (SELECT, UPDATE, INSERT itp) jest wykonywana i automatycznie zakańczana. Wiec wpisując funkcję autocommit()
z wartością FALSE
wyłączamy na chwilę zatwierdzanie każdej operacji.
$stmt1 = $mysqli->prepare("INSERT INTO tabela_1 (name, slug) VALUES ( ?,?)"); $stmt1->bind_param('ss', $name, $slug); if ($stmt1->execute() == false) { throw new Exception('Zapytanie 1 się nie powiodło'); }
Przygotowujemy zapytanie sql (funkcja prepare()
), za pomocą bind_param()
przekazujemy parametry do zapytania ( ‘ss’ – oznacza, że przekazujemy dwie zmienne typu string) następnie warunek z execute()
wywołuje zapytanie i jeśli jest błąd to wyrzucamy wyjątek.
Oznacza to w skrócie tyle, że wykonanie ataku typu SQL INJECTION, nie jest już możliwe.
$mysqli->commit();
Jeśli nie było żadnych błędów po drodze – zatwierdzamy transakcję poprzez funkcję commit();
$mysqli->rollback();
i na końcu w sekcji catch
wstawiamy funkcję rollback()
która w przypadku wyrzucenia wyjątku / błędu – cofa wprowadzone dane.
Jeśli były by jeszcze jakieś niejasności, zapraszam do komentowania.
Pozdrawiam.