1. דף הבית
  2. קורסים אונליין
  3. קורס PHP אונליין
  4. מסדי נתונים ו-MySQLi ב-PHP

מסדי נתונים ו-MySQLi ב-PHP

בפרק זה, נכיר מסדי נתונים, ונכסה את כל ההיבטים החיוניים של עבודה עם MySQLi, כולל התחברות למסד נתונים, ביצוע שאילתות, טיפול בתוצאות, הצהרות מוכנות, טיפול בשגיאות ועסקאות.
בסיסי נתונים ב-PHP

עבודה עם מסדי נתונים ב-PHP

שפת PHP מספקת מספר פתרונות לעבודה עם מסדי נתונים. שני הפתרונות הנפוצים ביותר הם ההרחבות MySQLi ו-PDO.

הרחבת MySQLi (MySQL Improved)

  • מתאימה עבור מסדי נתונים של MySQL בלבד.
  • תומכת גם בתכנות פרוצדורלי וגם בתכנות מונחה עצמים.
  • מספקת מנגנון "הצהרות מוכנות" (Prepared Statements) לאבטחה מחמירה יותר.

דוגמה:

<?php
$conn = new mysqli('localhost', 'username', 'password', 'database_name');
if ($conn->connect_error) {
    die('Connection failed: ' . $conn->connect_error);
}
?>

הרחבת PDO (PHP Data Objects)

  • מתאימה למספר סוגי מסדי נתונים, לרבות MySQL, PostgreSQL, SQLite.
  • תומכת רק בתכנות מונחה עצמים.
  • מספקת מנגנון הצהרות מוכנות עם מצייני מיקום עם שם (Named Placeholders).
  • כוללת מנגנון מובנה לטיפול בשגיאות (Exception Handling).

דוגמה:

<?php
$conn = new PDO('mysql:host=localhost;dbname=database_name', 'username', 'password');
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
?>

אם אתם עובדים עם מסד הנתונים MySQL, נמליץ על עבודה עם ההרחבה MySQLi שהיא האופטימלית והמתאימה יותר. אם אתם עובדים עם מסד נתונים אחר, או רוצים לשמור על הגמישות שאולי תעברו בעתיד למסד נתונים אחר, נמליץ על עבודה עם ההרחבה PDO.

נסקור להלן עבודה עם MySQLi בהרחבה.

התחברות למסד נתונים עם MySQLi

MySQLi, בשמה המלא MySQL Improved, היא הרחבה (extension) של PHP המאפשרת אינטראקציה עם מסדי נתונים של MySQL. היא מספקת אבטחה טובה יותר, ביצועים משופרים ותמיכה בהצהרות מוכנות (prepared statement) בהשוואה להרחבה MySQL הישנה יותר שכבר אינה נתמכת.

ישנן שתי דרכים להשתמש ב-MySQLi ב-PHP: תכנות מונחה עצמים (OO) או תכנות פרוצדורלי. שתי הדרכים הן רק מסיבות של נוחיות הכתיבה, אין יתרונות מהותיים מעבר לכך. גם אם הקוד שלכם אינו מבוסס תכנות מונחה עצמים, עדין נמליץ לשקול עבודה עם הגישה מונחית העצמים של MySQLi, כיוון שהקוד קצר יותר וקריא יותר.

כאשר מתחברים למסד נתונים, בכל אחת משתי הדרכים, נדרשים לספק 4 פרמטרים: שרת (host), שם משתמש (username), סיסמה (password), שם מסד הנתונים (dbname).

 

התחברות למסד נתונים באמצעות תכנות מונחה עצמים נעשית באמצעות יצירת אובייקט של המחלקה mysqli. באמצעות האובייקט הזה נוכל בהמשך לגשת למסד הנתונים ולעבוד איתו. במקרה של שגיאת התחברות, נוכל להעזר בתכונה connect_error ששומרת את הודעת השגיאה. בסיום העבודה עם מסד הנתונים, נתנתק באמצעות המתודה close.

דוגמה:

<?php
$servername = 'localhost';
$username = 'root';
$password = '';
$database = 'test_db';

// Create connection
$conn = new mysqli($servername, $username, $password, $database);

// Check connection
if ($conn->connect_error) {
    die('Connection failed: ' . $conn->connect_error);
}
echo 'Connected successfully!';

$conn->close();
?>

בדוגמה זו, יצרנו אובייקט מטיפוס mysqli באמצעות 4 הפרמטרים שמגדירים את מסד הנתונים. את האובייקט שנוצר הכנסנו למשתנה conn. בהמשך, נוכל לבצע פעולות שונות על מסד הנתונים באמצעות משתנה זה.

 

התחברות למסד נתונים באמצעות תכנות פרוצדורלי נעשית באמצעות קריאה לפונקציה mysqli_connect. הפונקציה מחזירה אובייקט של המחלקה mysqli שאיתו נעבוד בהמשך. במקרה של שגיאת התחברות, נוכל להעזר בפונקציה mysqli_connect_error שמחזירה את הודעת השגיאה. בסיום העבודה עם מסד הנתונים, נתנתק באמצעות הפונקציה mysqli_close.

דוגמה:

<?php
$servername = 'localhost';
$username = 'root';
$password = '';
$database = 'test_db';

// Create connection
$conn = mysqli_connect($servername, $username, $password, $database);

// Check connection
if (!$conn) {
    die('Connection failed: ' . mysqli_connect_error());
}
echo 'Connected successfully!';

mysqli_close($conn);
?>

בדוגמה זו, קראנו לפונקציה mysqli_connect באמצעות 4 הפרמטרים שמגדירים את מסד הנתונים. הפונקציה החזירה לנו אובייקט שהכנסנו למשתנה conn שבאמצעותו נבצע בהמשך פעולות במסד הנתונים.

 

נשים לב: מאחורי הקלעים, הפונקציה mysqli_connect קוראת ל-mysql. באופן דומה, לכל מתודה של האובייקט mysqli קיימת פונקציה שמפעילה את המתודה, ובכך מתאפשרת העבודה בגישה הפרוצדורלית.

אחזור נתונים ממסד הנתונים

אחרי שהתחברנו למסד הנתונים ויצרנו אובייקט של המחלקה mysqli, נשתמש בו לביצוע הפעולות השונות במסד הנתונים.

ביצוע שאילתה נעשה באמצעות המתודה query (תכנות מונחה עצמים) או הפונקציה mysqli_query (תכנות פרוצדורלי). תוצאת השאילתה היא אובייקט של המחלקה mysqli_result שיכולה לכלול יותר משורת תוצאה אחת. אחזור שורה אחת מהתוצאה נעשה באמצעות המתודה fetch_assoc (תכנות מונחה עצמים) או הפונקציה mysqli_fetch_assoc (תכנות פרוצדורלי), על האובייקט mysqli_result שהוחזר מביצוע השאילתה.

דוגמה:

<?php
$sql = 'SELECT * FROM users WHERE id=14';

// Object-Oriented
$result = $conn->query($sql);
$row = $result->fetch_assoc();

// Procedural
$result = mysqli_query($conn, $sql);
$row = mysqli_fetch_assoc($result);
?>

בדוגמה זו, אנו מאחזרים את נתוני המשתמש עם מזהה 14 מהטבלה users באמצעות שאילתת SELECT. המשתנה sql כולל מחרוזת עם שאילתת SQL. המשתנה result הוא אובייקט של המחלקה mysqli_result וכולל את תוצאת השאילתה, שיכולה לכלול יותר משורה אחת. המשתנה row כולל שורה אחת של תוצאה.

 

דפוס פעולה נפוץ, הוא אחזור של יותר משורה אחת, באמצעות לולאה שרצה על שורות התוצאה של השאילתה.

דוגמה:

<?php
$sql = 'SELECT * FROM users';

// Object-Oriented
$row = $result->fetch_assoc();
while ($row = $result->fetch_assoc()) {
    echo $row['username'] . '<br />';
}

// Procedural
$row = mysqli_fetch_assoc($result);
while ($row = mysqli_fetch_assoc($result)) {
    echo $row['username'] . '<br />';
}
?>

בדוגמה זו, אנו מאחזרים את נתוני כל המשתמשים מהטבלה users באמצעות שאילתת SELECT. באמצעות לולאת while אנו עוברים על כל השורות בתוצאת השאילתה, ומדפיסים את השדה username שבכל שורה.

הוספה, עדכון והסרת שורות במסד הנתונים

באמצעות המתודה query (תכנות מונחה עצמים) או הפונקציה mysqli_query (תכנות פרוצדורלי) ניתן לבצע פעולות נוספות במסד הנתונים מעבר לאחזור. במקרה של הצלחה, מוחזר הערך TRUE. במקרה של כשלון, מוחזר הערך FALSE. במקרה של שגיאה, נוכל לבחון את התכונה error (תכנות מונחה עצמים) או את תוצאת הפונקציה mysqli_error (תכנות פרוצדורלי).

נראה להלן דוגמה לשאילתות להוספה (insert), עדכון (update) ומחיקה (delete).

דוגמה בתכנות מונחה עצמים:

<?php
// Database connection (Object-Oriented)
$conn = new mysqli('localhost', 'username', 'password', 'database_name');

// Check connection
if ($conn->connect_error) {
    die('Connection failed: ' . $conn->connect_error);
}

// INSERT example
$sql = "INSERT INTO users (name, email, age) VALUES ('John Doe', 'john@example.com', 25)";
if ($conn->query($sql) === TRUE) {
    echo 'New record inserted successfully';
} else {
    echo 'Error: ' . $sql . '<br />' . $conn->error;
}

// UPDATE example
$sql = "UPDATE users SET age = 26 WHERE email = 'john@example.com'";
if ($conn->query($sql) === TRUE) {
    echo 'Record updated successfully';
} else {
    echo 'Error updating record: ' . $conn->error;
}

// DELETE example
$sql = "DELETE FROM users WHERE email = 'john@example.com'";
if ($conn->query($sql) === TRUE) {
    echo 'Record deleted successfully';
} else {
    echo 'Error deleting record: ' . $conn->error;
}

// Close connection
$conn->close();
?>

 

דוגמה בתכנות פרוצדורלי:

<?php
// Database connection (Procedural)
$conn = mysqli_connect('localhost', 'username', 'password', 'database_name');

// Check connection
if (!$conn) {
    die('Connection failed: ' . mysqli_connect_error());
}

// INSERT example
$sql = "INSERT INTO users (name, email, age) VALUES ('John Doe', 'john@example.com', 25)";
if (mysqli_query($conn, $sql)) {
    echo 'New record inserted successfully';
} else {
    echo 'Error: ' . $sql . '<br />' . mysqli_error($conn);
}

// UPDATE example
$sql = "UPDATE users SET age = 26 WHERE email = 'john@example.com'";
if (mysqli_query($conn, $sql)) {
    echo 'Record updated successfully';
} else {
    echo 'Error updating record: ' . mysqli_error($conn);
}

// DELETE example
$sql = "DELETE FROM users WHERE email = 'john@example.com'";
if (mysqli_query($conn, $sql)) {
    echo 'Record deleted successfully';
} else {
    echo 'Error deleting record: ' . mysqli_error($conn);
}

// Close connection
mysqli_close($conn);
?>

 

נשווה בין 2 הגישות: תכנות מונחה עצמים מול תכנות פרוצדורלי.

תכונה תכנות מונחה עצמים תכנות פרוצדורלי
התחברות למסד הנתונים $conn = new mysqli(...) $conn = mysqli_connect(...)
בדיקת שגיאת התחברות $conn->connect_error mysqli_connect_error($conn)
הרצת שאילתה $conn->query($sql) mysqli_query($conn, $sql)
בדיקת שגיאת שאילתה $conn->error mysqli_error($conn)
סגירת מסד הנתונים $conn->close() mysqli_close($conn)

עבודה עם הצהרות מוכנות (Prepared Statements)

שאילתות SQL נתונות לסיכון אבטחתי מסוג SQL Injection. זהו סיכון אבטחה שבו התוקף מבצע מניפולציות בשאילתות SQL ומחדיר קוד SQL זדוני. בכך, יכול התוקף לגנוב, לשנות או למחוק נתונים ממסד נתונים.

הדבר מתרחש בדרך כלל בשאילתת SQL דינמית, אשר מבוססת על ערכי קלט חיצוניים.

 

נדגים את הסיכון האבטחתי. נניח לדוגמה, שכתבנו את קטע הקוד הבא:

<?php
// Database connection
$conn = new mysqli('localhost', 'username', 'password', 'database_name');

// Get user input (simulating a login form)
$username = $_GET['username'];

// Vulnerable SQL Query (User input is directly concatenated)
$sql = "SELECT * FROM users WHERE username = '$username'";

$result = $conn->query($sql);

if ($result->num_rows > 0) {
    echo 'Login successful!';
} else {
    echo 'Invalid username.';
}

$conn->close();
?>

בקוד זה, אנו מקבלים כקלט חיצוני שם משתמש (username) ומאחזרים את כל השדות של אותו משתמש ממסד הנתונים.

תוקף יכול לנצל את העובדה שאין לנו שליטה על ערך הקלט username ולהעביר את הערך הבא:

' OR '1'='1

שאילתת ה-SQL תהיה במקרה כזה:

SELECT * FROM users WHERE username = '' OR '1'='1'

מכיוון שהתנאי בשאילתה מתקיים תמיד (תמיד 1=1), יאוחזרו פרטי כל המשתמשים ממסד הנתונים, וזוהי כמובן פרצת אבטחה חמורה.

 

הפתרון לסיכון האבטחתי הוא שימוש בהצהרות מוכנות (Prepared Statements). הרעיון שעומד מאחורי ההצהרות המוכנות הוא להגדיר את השאילתה, כאשר את הערכים הדינמיים מסמנים בסימן שאלה (?). באופן הזה, מבצעים הפרדה בין השאילתה עצמה לבין הערכים המוזנים לתוכה.

את הדוגמה האחרונה ניתן לכתוב באמצעות הצהרות מוכנות באופן הבא:

<?php
// Secure Database connection
$conn = new mysqli('localhost', 'username', 'password', 'database_name');

// Get user input safely
$username = $_GET['username'];

// Use Prepared Statement
$stmt = $conn->prepare('SELECT * FROM users WHERE username = ?');
$stmt->bind_param('s', $username);
$stmt->execute();

$result = $stmt->get_result();

if ($result->num_rows > 0) {
    echo 'Login successful!';
    // Fetch user data
    while ($row = $result->fetch_assoc()) {
        echo 'User ID: ' . $row['id'] . '<br />';
        echo 'Username: ' . $row['username'] . '<br />';
        echo 'Email: ' . $row['email'] . '<br />';
    }
} else {
    echo 'Invalid username.';
}

// Close connections
$stmt->close();
$conn->close();
?>

המתודה prepare מכינה את השאילתה ומחזירה אובייקט של המחלקה mysqli_stmt שאותו אנו מכניסים למשתנה stmt. סימן שאלה (?) מייצג את ערך הקלט שמושווה לשדה username. המתודה bind_param מבצעת קשירה של כל הערכים בשאילתה למשתנים שמחזיקים בהם. הפרמטר הראשון של bind_param הוא מחרוזת המציינת את טיפוס הערכים: האות s עבור מחרוזת (String) או האות i עבור מספר שלם (Integer). הפרמטרים השני ואילך הם המשתנים עם הערכים שמוזנים לשאילתה במקום סימני השאלה. המתודה execute מפעילה את השאילתה והמתודה get_result מחזירה את התוצאה. כל קריאה למתודה fetch_assoc מחזירה שורה נוספת מהתוצאה שהתקבלה. בסיום, מבצעים סגירה לשאילתה stmt, וכמובן סגירה למסד הנתונים.

 

נראה דוגמה נוספת להצהרה מוכנה:

<?php
$conn = new mysqli('localhost', 'username', 'password', 'database_name');

$stmt = $conn->prepare('INSERT INTO users (name, email, age) VALUES (?, ?, ?)');
$stmt->bind_param('ssi', $name, $email, $age);

// Insert first user
$name = 'Charlie';
$email = 'charlie@example.com';
$age = 28;
$stmt->execute();

// Insert second user
$name = 'David';
$email = 'david@example.com';
$age = 32;
$stmt->execute();

echo 'Records inserted successfully.';

$stmt->close();
$conn->close();
?>

בדוגמה זו, אנו מבצעים שאילתת INSERT המוסיפה שורה בטבלה users במסד הנתונים. אנו מגדירים 3 ערכי קלט: name, email, age. המתודה bind_param מקשרת את הערכים החסרים בשאילתה (שמיוצגים באמצעות סימני שאלה) למשתנים הרלוונטיים, וקובעת כי המשתנה name הוא מסוג מחרוזת (s), המשתנה email הוא מסוג מחרוזת (s) והמשתנה age הוא מסוג מספר (i). ניתן לראות כי אנו קובעים את הערכים האמיתיים של המשתנים האלו לאחר המתודה bind_param ולא לפניה, אבל זה לא באמת משנה, ובלבד שהם ייקבעו לפני ביצוע המתודה execute. אנו מבצעים execute פעמיים, בכל פעם עם ערכים אחרים, על בסיס אותה שאילתה שהוכנה מראש. בסיום, אנו סוגרים את השאילתה ואת מסד הנתונים.

 

גם להצהרות המוכנות יש גירסה פרוצדורלית. את הדוגמה האחרונה נוכל לכתוב בצורה הבאה:

<?php
$conn = mysqli_connect('localhost', 'username', 'password', 'database_name');

$stmt = mysqli_prepare($conn, 'INSERT INTO users (name, email, age) VALUES (?, ?, ?)');
mysqli_stmt_bind_param($stmt, 'ssi', $name, $email, $age);

// Insert first user
$name = 'Charlie';
$email = 'charlie@example.com';
$age = 28;
mysqli_stmt_execute($stmt);

// Insert second user
$name = 'David';
$email = 'david@example.com';
$age = 32;
mysqli_stmt_execute($stmt);

echo 'Records inserted successfully.';

mysqli_stmt_close($stmt);
mysqli_close($conn);
?>

 

גם בעבודה עם הצהרות מוכנות, נמליץ על שימוש בגישה מונחית העצמים ולא בגישה הפרוצדורלית, מטעמי קריאות.

 

נסקור את יתרונות השימוש בהצהרות מוכנות לעומת שאילתות בתחביר הרגיל.

תכונה שאילתות רגילות הצהרות מוכנות
אבטחה פגיעות מפני SQL Injection חסינות מפני SQL Injection
ביצועים מוגדר בכל פעם שמריצים את השאילתה מוגדר פעם אחת, וניתן להרצה מספר פעמים
תחזוקה קשה לתחזוקה ולניהול קלט מהמשתמשים קוד קריא ומובנה

 

המלצתנו היא להשתמש בהצהרות מוכנות במידת האפשר. רק אם אין ברירה, להשתמש בשאילתות רגילות. בכל מקרה, נמליץ תמיד לבדוק היטב כל קלט שמגיע מהמשתמש שהוא תקין: לוודא שקלט שצריך להיות מספרי (נניח גיל) הוא אכן מספר, שקלט מחרוזת אינו כולל תווים מסוכנים (כגון גרש או גירשיים) וכן הלאה. הבדיקה הזו תמיד חשובה, במטרה להקטין סיכונים אבטחתיים ובכלל תוצאות לא צפויות בביצוע הקוד.

ביצוע טרנזקציות

תארו מצב בו הנתונים במסד הנתונים פרוסים על פני מספר טבלאות שונות עם קשרים ביניהן. נניח לדוגמה מסד נתונים שכולל שתי טבלאות:

  1. טבלה cities - הטבלה מכילה שמות של ערים בארץ. השדה id מחזיק את מזהה העיר (מספר סידורי רץ) והשדה name מחזיק את שם העיר.
  2. טבלה users - הטבלה מכילה שמות של משתמשים. השדה name מחזיק את שם המשתמש, השדה phone מחזיק את הטלפון של המשתמש והשדה city מחזיק את המזהה של העיר בה גר המשתמש מהטבלה cities.

בכל הוספה של משתמש חדש למסד הנתונים, אנו תחילה מוודאים שהעיר בה המשתמש גר קיימת בטבלה cities. אם אינה קיימת בטבלה, אנו מוסיפים אותה לשם. לאחר מכן, אנו מוסיפים את המשתמש לטבלה users. אנו למעשה מבצעים שאילתת INSERT אחת לטבלה cities (אם העיר אינה קיימת בטבלה) ושאילתת INSERT נוספת לטבלה users.

על מנת לשמור על מסד נתונים מצומצם ומהיר, נשים לעצמנו תנאי שלא יתווספו שמות ערים שאין בהם צורך לטבלה cities. כלומר, אם קיימת עיר בטבלה cities אז מובטח לנו שיש לפחות משתמש אחד בטבלה users שגר בעיר הזו.

במקרה שאנו מוסיפים משתמש חדש והכל תקין, מסד הנתונים ישמור על התנאי ששמנו לעצמנו, שאין שם של עיר שלא לצורך. עם זאת, במקרה שבו תהליך ההוספה אינו מצליח מסיבה כלשהי (הפסקת חשמל בדיוק באמצע התהליך, תקלה במחשב או באינטרנט או כל סיבה אחרת), יתכן מצב ביניים שבו התווספה עיר לטבלת ה-cities, אבל המשתמש לא התווסף לטבלה users. מצב זה מפר את התנאי שקבענו לעצמנו.

כדי שמקרה כזה לא יקרה, נרצה שכל התהליך של הוספת המשתמש החדש, שכולל בתוכו לעיתים 2 שאילתות INSERT, יוגדר כטרנזקציה אחת: או שכולו מצליח, או שכולו נכשל. אם התהליך משתבש, יובטח לנו שנוכל להחזיר את כל מה שבוצע חלקית אחורה, וכך לא יתרחש מצב ביניים ומסד הנתונים ימשיך לשמור על עקביות הנתונים ועל התנאי שהגדרנו.

תכונות הטרנזקציה:

  • אטומיות - הבטחה שכל הפעולות יצליחו או יכשלו, כיחידה אחת. התהליך כולו הוא יחידה "אטומית" אחת, שאי אפשר לחלק אותה.
  • עקביות - שמירה של מסד הנתונים במצב חוקי, על ידי הבטחה ששינויים קשורים מתרחשים יחד.
  • בידוד - מניעה של הפרעות מפעולות אחרות של מסד נתונים במהלך הביצוע. כלומר, במהלך ביצוע הטרנזקציה לא יכולה להתרחש במקביל פעולה אחרת במסד הנתונים שעלולה לשבש את הטרנזקציה.
  • עמידות - הבטחה שטרנזקציות "שיצאו לדרך" (שנעשה להן commit) ישארו רלוונטיות גם לאחר קריסת מערכת. כלומר, גם אם המערכת קורסת מסיבה כלשהי, כשהיא תחזור לעצמה הטרנזקציה תתרחש ולא תתבטל.

 

דוגמה בקוד מונחה עצמים:

<?php
// Database connection (Object-Oriented)
$conn = new mysqli('localhost', 'username', 'password', 'database_name');

// Check connection
if ($conn->connect_error) {
    die('Connection failed: ' . $conn->connect_error);
}

// Start transaction
$conn->begin_transaction();

try {
    // First query - Insert a new user
    $conn->query("INSERT INTO users (name, email, balance) VALUES ('Alice', 'alice@example.com', 1000)");

    // Second query - Deduct balance from another user
    $conn->query("UPDATE users SET balance = balance - 200 WHERE email = 'bob@example.com'");

    // Third query - Add balance to Alice
    $conn->query("UPDATE users SET balance = balance + 200 WHERE email = 'alice@example.com'");

    // If all queries execute successfully, commit the transaction
    $conn->commit();
    echo 'Transaction successful!';
} catch (Exception $e) {
    // If any query fails, rollback all changes
    $conn->rollback();
    echo 'Transaction failed: ' . $e->getMessage();
}

// Close connection
$conn->close();
?>

בדוגמה זו, באמצעות המתודה autocommit (שיכולה לקבל ערך true או ערך false) אנו מגדירים תחילה שלא יבוצע commit בצורה אוטומטית, אלא רק באמצעות ביצוע מפורש של commit. אנו מכריזים על טרנזקציה באמצעות המתודה begin_transaction. הטרנזציה כוללת סדרה של שאילתות, כל אחת מבוצעת באמצעות המתודה query. בסיום הטרנזקציה, אנו סוגרים אותה על ידי קריאה למתודה commit. כל ביצוע השאילתות של הטרנזקציה נעשות בתוך בלוק try. במקרה של כשלון כלשהו, ביצוע הטרנזקציה נעצר והקוד מועבר לבלוק ה-catch, שם אנו קוראים למתודה rollback שמחזירה את המצב לקדמותו, ממש כאילו השאילתות בחלק של ה-try לא התבצעו כלל.

 

אותו הקוד בגישה פרוצדורלית:

<?php
// Database connection (Procedural)
$conn = mysqli_connect('localhost', 'username', 'password', 'database_name');

// Check connection
if (!$conn) {
    die('Connection failed: ' . mysqli_connect_error());
}

// Disable autocommit to manually handle transactions
mysqli_autocommit($conn, false);

try {
    // Start transaction
    mysqli_begin_transaction($conn);

    // First query - Insert a new user
    mysqli_query($conn, "INSERT INTO users (name, email, balance) VALUES ('Alice', 'alice@example.com', 1000)");

    // Second query - Deduct balance from another user
    mysqli_query($conn, "UPDATE users SET balance = balance - 200 WHERE email = 'bob@example.com'");

    // Third query - Add balance to Alice
    mysqli_query($conn, "UPDATE users SET balance = balance + 200 WHERE email = 'alice@example.com'");

    // If all queries execute successfully, commit the transaction
    mysqli_commit($conn);
    echo 'Transaction successful!';
} catch (Exception $e) {
    // If any query fails, rollback all changes
    mysqli_rollback($conn);
    echo 'Transaction failed: ' . $e->getMessage();
}

// Re-enable autocommit to avoid affecting future queries
mysqli_autocommit($conn, true);

// Close connection
mysqli_close($conn);
?>

 

אותו הקוד באמצעות הצהרות מוכנות (קוד מונחה עצמים):

<?php
// Database connection (Object-Oriented)
$conn = new mysqli('localhost', 'username', 'password', 'database_name');

// Check connection
if ($conn->connect_error) {
    die('Connection failed: ' . $conn->connect_error);
}

// Disable autocommit to manually handle transactions
$conn->autocommit(false);

try {
    // Start transaction
    $conn->begin_transaction();

    // First query - Insert a new user
    $stmt1 = $conn->prepare('INSERT INTO users (name, email, balance) VALUES (?, ?, ?)');
    $stmt1->bind_param('ssi', $name, $email, $balance);
    $name = 'Alice';
    $email = 'alice@example.com';
    $balance = 1000;
    $stmt1->execute();

    // Second query - Deduct balance from another user
    $stmt2 = $conn->prepare('UPDATE users SET balance = balance - ? WHERE email = ?');
    $stmt2->bind_param('is', $amount, $email);
    $amount = 200;
    $email = 'bob@example.com';
    $stmt2->execute();

    // Third query - Add balance to Alice
    $stmt3 = $conn->prepare('UPDATE users SET balance = balance + ? WHERE email = ?');
    $stmt3->bind_param('is', $amount, $email);
    $amount = 200;
    $email = 'alice@example.com';
    $stmt3->execute();

    // If all queries execute successfully, commit the transaction
    $conn->commit();
    echo 'Transaction successful!';

    // Close statements
    $stmt1->close();
    $stmt2->close();
    $stmt3->close();
} catch (Exception $e) {
    // If any query fails, rollback all changes
    $conn->rollback();
    echo 'Transaction failed: ' . $e->getMessage();
}

// Re-enable autocommit to avoid affecting future queries
$conn->autocommit(true);

// Close connection
$conn->close();
?>
הוספת תגובה
אנו משתמשים בעוגיות על מנת לשפר את חווית המשתמש באתר. מדיניות הפרטיותאני מסכים