أساسيات PHP

تنفيذ الاستعلامات مع MySQLi

13 دقيقة الدرس 37 من 45

تنفيذ استعلامات SQL

بمجرد الاتصال بقاعدة البيانات، يمكنك تنفيذ استعلامات SQL لإنشاء البيانات وقراءتها وتحديثها وحذفها. يوفر MySQLi عدة طرق لتنفيذ الاستعلامات.

طريقة query()

أبسط طريقة لتنفيذ استعلام هي استخدام طريقة query():

<?php
require_once 'database.php';

// تنفيذ استعلام بسيط
$sql = "SELECT * FROM users";
$result = $conn->query($sql);

if ($result) {
    echo "تم تنفيذ الاستعلام بنجاح";
} else {
    echo "خطأ: " . $conn->error;
}
?>
تحذير: لا تستخدم أبدًا query() مع مدخلات المستخدم مباشرة. يستخدم هذا المثال SQL مُدمج. سنتعلم عن العبارات المُحضرة في الدرس التالي.

إنشاء الجداول

لنقم بإنشاء جدول مستخدمين لأمثلتنا:

<?php
require_once 'database.php';

$sql = "CREATE TABLE IF NOT EXISTS users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    email VARCHAR(100) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    full_name VARCHAR(100),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)";

if ($conn->query($sql) === TRUE) {
    echo "تم إنشاء الجدول بنجاح";
} else {
    echo "خطأ في إنشاء الجدول: " . $conn->error;
}
?>

إدراج البيانات

إدراج سجلات جديدة في قاعدة البيانات:

<?php
require_once 'database.php';

// تشفير كلمة المرور
$password = password_hash("mypassword123", PASSWORD_DEFAULT);

$sql = "INSERT INTO users (username, email, password, full_name)
        VALUES ('johndoe', 'john@example.com', '$password', 'John Doe')";

if ($conn->query($sql) === TRUE) {
    echo "تم إنشاء السجل بنجاح<br>";
    echo "آخر معرف مُدرج: " . $conn->insert_id;
} else {
    echo "خطأ: " . $conn->error;
}
?>
ملاحظة: خاصية insert_id تُرجع معرّف آخر صف تم إدراجه. مفيد جدًا عندما تحتاج للإشارة إلى السجل الجديد.

تحديد البيانات

استرجاع البيانات من قاعدة البيانات ومعالجة النتائج:

جلب جميع الصفوف

<?php
require_once 'database.php';

$sql = "SELECT id, username, email, full_name, created_at FROM users";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    echo "<h3>قائمة المستخدمين</h3>";
    echo "<table border='1'>";
    echo "<tr><th>المعرف</th><th>اسم المستخدم</th><th>البريد الإلكتروني</th><th>الاسم الكامل</th></tr>";

    // جلب كل صف كمصفوفة ترابطية
    while ($row = $result->fetch_assoc()) {
        echo "<tr>";
        echo "<td>" . $row['id'] . "</td>";
        echo "<td>" . $row['username'] . "</td>";
        echo "<td>" . $row['email'] . "</td>";
        echo "<td>" . $row['full_name'] . "</td>";
        echo "</tr>";
    }

    echo "</table>";
} else {
    echo "لم يتم العثور على مستخدمين";
}

// تحرير مجموعة النتائج
$result->free();
?>

طرق الجلب المختلفة

<?php
// 1. جلب كمصفوفة ترابطية (المفتاح = اسم العمود)
$row = $result->fetch_assoc();
// الاستخدام: $row['username']

// 2. جلب كمصفوفة رقمية (المفتاح = فهرس العمود)
$row = $result->fetch_row();
// الاستخدام: $row[0], $row[1]

// 3. جلب كمصفوفة ترابطية ورقمية معًا
$row = $result->fetch_array(MYSQLI_BOTH);
// الاستخدام: $row['username'] أو $row[0]

// 4. جلب ككائن
$row = $result->fetch_object();
// الاستخدام: $row->username

// 5. جلب جميع الصفوف دفعة واحدة
$rows = $result->fetch_all(MYSQLI_ASSOC);
// يُرجع مصفوفة ثنائية الأبعاد لجميع الصفوف
?>
نصيحة: fetch_assoc() هو الأكثر استخدامًا لأن أسماء الأعمدة أكثر وضوحًا من المؤشرات الرقمية.

تحديث البيانات

تعديل السجلات الموجودة في قاعدة البيانات:

<?php
require_once 'database.php';

$sql = "UPDATE users
        SET full_name = 'John Smith', email = 'johnsmith@example.com'
        WHERE username = 'johndoe'";

if ($conn->query($sql) === TRUE) {
    echo "تم تحديث السجل بنجاح<br>";
    echo "الصفوف المتأثرة: " . $conn->affected_rows;
} else {
    echo "خطأ في تحديث السجل: " . $conn->error;
}
?>
ملاحظة: خاصية affected_rows تخبرك بعدد الصفوف التي تم تعديلها. تُرجع 0 إذا لم تطابق أي صفوف شرط WHERE.

حذف البيانات

إزالة السجلات من قاعدة البيانات:

<?php
require_once 'database.php';

$sql = "DELETE FROM users WHERE username = 'johndoe'";

if ($conn->query($sql) === TRUE) {
    echo "تم حذف السجل بنجاح<br>";
    echo "الصفوف المحذوفة: " . $conn->affected_rows;
} else {
    echo "خطأ في حذف السجل: " . $conn->error;
}
?>
تحذير: قم دائمًا بتضمين جملة WHERE في عبارات UPDATE و DELETE، وإلا ستقوم بتعديل/حذف جميع الصفوف!

عد الصفوف

الحصول على عدد الصفوف التي أرجعها استعلام SELECT:

<?php
require_once 'database.php';

$sql = "SELECT * FROM users WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)";
$result = $conn->query($sql);

echo "المستخدمون المسجلون في آخر 7 أيام: " . $result->num_rows;

$result->free();
?>

استخدام LIMIT و OFFSET

تنفيذ الترقيم بتحديد عدد النتائج:

<?php
require_once 'database.php';

// إعدادات الترقيم
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$per_page = 10;
$offset = ($page - 1) * $per_page;

// الحصول على إجمالي عدد المستخدمين
$count_sql = "SELECT COUNT(*) as total FROM users";
$count_result = $conn->query($count_sql);
$total_users = $count_result->fetch_assoc()['total'];
$total_pages = ceil($total_users / $per_page);

// الحصول على مستخدمي الصفحة الحالية
$sql = "SELECT * FROM users ORDER BY created_at DESC LIMIT $per_page OFFSET $offset";
$result = $conn->query($sql);

echo "<h3>المستخدمون (الصفحة $page من $total_pages)</h3>";

if ($result->num_rows > 0) {
    while ($row = $result->fetch_assoc()) {
        echo "<p>" . $row['username'] . " - " . $row['email'] . "</p>";
    }
}

// روابط الترقيم
for ($i = 1; $i <= $total_pages; $i++) {
    if ($i === $page) {
        echo "<strong>$i</strong> ";
    } else {
        echo "<a href='?page=$i'>$i</a> ";
    }
}
?>

التحقق من وجود السجل

التحقق من وجود سجل قبل تنفيذ العمليات:

<?php
require_once 'database.php';

function userExists($conn, $username) {
    $sql = "SELECT id FROM users WHERE username = '$username'";
    $result = $conn->query($sql);
    return $result->num_rows > 0;
}

if (userExists($conn, 'johndoe')) {
    echo "اسم المستخدم مستخدم بالفعل";
} else {
    echo "اسم المستخدم متاح";
}
?>

تنفيذ استعلامات متعددة

تنفيذ عدة استعلامات في استدعاء واحد:

<?php
require_once 'database.php';

$sql = "
    INSERT INTO users (username, email, password) VALUES ('user1', 'user1@test.com', 'pass1');
    INSERT INTO users (username, email, password) VALUES ('user2', 'user2@test.com', 'pass2');
    INSERT INTO users (username, email, password) VALUES ('user3', 'user3@test.com', 'pass3');
";

if ($conn->multi_query($sql)) {
    do {
        // تخزين أول مجموعة نتائج
        if ($result = $conn->store_result()) {
            $result->free();
        }

        if ($conn->more_results()) {
            echo "المزيد من النتائج قادمة...<br>";
        }
    } while ($conn->next_result());

    echo "تم تنفيذ جميع الاستعلامات بنجاح";
} else {
    echo "خطأ: " . $conn->error;
}
?>
تحذير: multi_query() نادرًا ما يكون مطلوبًا ويمكن أن يكون خطيرًا مع مدخلات المستخدم. يُفضل تنفيذ الاستعلامات بشكل منفصل.

مثال CRUD كامل

إليك مثالًا كاملاً يجمع جميع العمليات:

<?php
require_once 'database.php';

class UserManager {
    private $conn;

    public function __construct($connection) {
        $this->conn = $connection;
    }

    // CREATE
    public function createUser($username, $email, $password, $full_name) {
        $hashed_password = password_hash($password, PASSWORD_DEFAULT);

        $sql = "INSERT INTO users (username, email, password, full_name)
                VALUES ('$username', '$email', '$hashed_password', '$full_name')";

        if ($this->conn->query($sql)) {
            return $this->conn->insert_id;
        }
        return false;
    }

    // READ
    public function getUser($id) {
        $sql = "SELECT * FROM users WHERE id = $id";
        $result = $this->conn->query($sql);

        if ($result->num_rows > 0) {
            return $result->fetch_assoc();
        }
        return null;
    }

    public function getAllUsers() {
        $sql = "SELECT * FROM users ORDER BY created_at DESC";
        $result = $this->conn->query($sql);

        return $result->fetch_all(MYSQLI_ASSOC);
    }

    // UPDATE
    public function updateUser($id, $data) {
        $updates = [];
        foreach ($data as $key => $value) {
            $updates[] = "$key = '$value'";
        }
        $updates_str = implode(', ', $updates);

        $sql = "UPDATE users SET $updates_str WHERE id = $id";

        return $this->conn->query($sql);
    }

    // DELETE
    public function deleteUser($id) {
        $sql = "DELETE FROM users WHERE id = $id";
        return $this->conn->query($sql);
    }

    // COUNT
    public function getUserCount() {
        $sql = "SELECT COUNT(*) as total FROM users";
        $result = $this->conn->query($sql);
        $row = $result->fetch_assoc();
        return $row['total'];
    }
}

// الاستخدام
$userManager = new UserManager($conn);

// إنشاء مستخدم
$user_id = $userManager->createUser('alice', 'alice@example.com', 'password123', 'Alice Smith');
echo "تم إنشاء المستخدم بمعرف: $user_id<br>";

// الحصول على المستخدم
$user = $userManager->getUser($user_id);
echo "اسم المستخدم: " . $user['username'] . "<br>";

// تحديث المستخدم
$userManager->updateUser($user_id, ['full_name' => 'Alice Johnson']);
echo "تم تحديث المستخدم<br>";

// الحصول على جميع المستخدمين
$all_users = $userManager->getAllUsers();
echo "إجمالي المستخدمين: " . count($all_users) . "<br>";

// حذف المستخدم
$userManager->deleteUser($user_id);
echo "تم حذف المستخدم<br>";
?>
تنبيه أمني: يستخدم هذا المثال الدمج المباشر للسلاسل النصية للبساطة. لا تفعل هذا أبدًا مع مدخلات المستخدم! سنتعلم العبارات المحضرة في الدرس التالي.

تمرين: بناء دليل مستخدمين بسيط

  1. أنشئ جدول users بحقول: id, name, email, phone, city
  2. أنشئ نموذج add_user.php لإضافة مستخدمين جدد
  3. أنشئ صفحة list_users.php لعرض جميع المستخدمين في جدول
  4. أضف ميزة بحث لتصفية المستخدمين حسب المدينة
  5. أضف ترقيمًا لعرض 5 مستخدمين في كل صفحة
  6. أنشئ صفحة edit_user.php لتحديث معلومات المستخدم
  7. أنشئ نص برمجي delete_user.php لإزالة المستخدمين
  8. أضف عدادًا يعرض إجمالي عدد المستخدمين

أفضل الممارسات

  • تحقق دائمًا من نتائج الاستعلام: تأكد من تنفيذ الاستعلامات بنجاح
  • حرر مجموعات النتائج: استخدم $result->free() لتحرير الذاكرة
  • استخدم أسماء أعمدة محددة: حدد أعمدة معينة بدلاً من SELECT *
  • قم دائمًا بتضمين WHERE في UPDATE/DELETE: منع التغييرات الجماعية العرضية
  • أغلق الاتصالات: أغلق اتصالات قاعدة البيانات عند الانتهاء
  • لا تثق أبدًا في مدخلات المستخدم: استخدم دائمًا العبارات المحضرة (الدرس التالي)
  • استخدم المعاملات: للعمليات التي يجب أن تنجح أو تفشل معًا

الملخص

  • استخدم طريقة query() لتنفيذ عبارات SQL
  • insert_id تُرجع معرّف آخر صف تم إدراجه
  • affected_rows تُرجع عدد الصفوف المتأثرة بـ UPDATE/DELETE
  • num_rows تُرجع عدد الصفوف في نتيجة SELECT
  • استخدم fetch_assoc() لاسترجاع الصفوف كمصفوفات ترابطية
  • حرر دائمًا مجموعات النتائج بـ free() عند الانتهاء
  • لا تدمج أبدًا مدخلات المستخدم مباشرة في استعلامات SQL