آموزش برنامه نویسی PHP-جلسه بیست و پنجم

آموزش برنامه نویسی PHP-جلسه بیست و پنجم

در این قسمت از آموزش PHP، با نحوه دریافت و نمایش اطلاعات ذخیره‌شده در پایگاه داده MySQL آشنا می‌شویم. دستور SELECT یکی از پرکاربردترین دستورات SQL است که به کمک آن می‌توان اطلاعات موردنیاز را از یک یا چند جدول استخراج کرد و در صفحات وب نمایش داد.

در این آموزش، نحوه اتصال PHP به MySQL، دریافت تمام رکوردها، انتخاب ستون‌های مشخص، فیلتر کردن اطلاعات، مرتب‌سازی نتایج، محدود کردن تعداد رکوردها و نمایش داده‌ها با استفاده از PDO و MySQLi را به‌صورت عملی بررسی می‌کنیم.

Table of contents [Show] [Hide]

۱. دستور SELECT در MySQL چیست؟

دستور SELECT برای خواندن و دریافت اطلاعات از جداول پایگاه داده MySQL استفاده می‌شود. برخلاف دستوراتی مانند INSERT، UPDATE و DELETE که اطلاعات را تغییر می‌دهند، دستور SELECT صرفاً اطلاعات را بازیابی می‌کند.

ساختار کلی این دستور به شکل زیر است:

SELECT column1, column2, ...
FROM table_name;

در این ساختار، نام ستون‌هایی که قصد دریافت آن‌ها را داریم مشخص می‌کنیم و با استفاده از FROM نام جدول موردنظر را تعیین می‌کنیم.

برای مثال، فرض کنید جدولی به نام users داریم که اطلاعات کاربران در آن ذخیره شده است:

SELECT name, email
FROM users;

این دستور فقط ستون‌های name و email را از جدول کاربران دریافت می‌کند.

۲. دریافت تمام اطلاعات جدول با SELECT *

اگر بخواهیم تمام ستون‌های یک جدول را دریافت کنیم، می‌توانیم به‌جای نوشتن نام تک‌تک ستون‌ها از علامت ستاره (*) استفاده کنیم.

SELECT * FROM users;

این دستور تمام ستون‌ها و رکوردهای جدول users را برمی‌گرداند.

برای مثال، جدول کاربران ممکن است اطلاعات زیر را داشته باشد:

idnameemailage
1علی رضاییali@example.com25
2مریم احمدیmaryam@example.com30
3رضا محمدیreza@example.com22

اجرای دستور SELECT * تمام اطلاعات بالا را برمی‌گرداند.

نکته حرفه‌ای: اگرچه استفاده از ستاره ساده است، در پروژه‌های واقعی بهتر است ستون‌های موردنیاز را به‌صورت مشخص انتخاب کنید. این کار باعث خوانایی بیشتر کد و کاهش حجم اطلاعات منتقل‌شده می‌شود.

۳. اتصال PHP به پایگاه داده MySQL با PDO

برای دریافت اطلاعات از MySQL، ابتدا باید PHP به پایگاه داده متصل شود. یکی از روش‌های پیشنهادی برای این کار استفاده از PDO است.

ابتدا یک فایل با نام database.php ایجاد می‌کنیم:

<?php

$host = "localhost";
$dbname = "my_database";
$username = "root";
$password = "";

try {
    $pdo = new PDO(
        "mysql:host=$host;dbname=$dbname;charset=utf8mb4",
        $username,
        $password
    );

    $pdo->setAttribute(
        PDO::ATTR_ERRMODE,
        PDO::ERRMODE_EXCEPTION
    );

} catch (PDOException $e) {
    error_log($e->getMessage());
    exit("خطا در اتصال به پایگاه داده.");
}

?>

در این مثال، اطلاعات اتصال را مشخص کرده‌ایم و با استفاده از کلاس PDO به پایگاه داده متصل شده‌ایم.

  • localhost: آدرس سرور پایگاه داده است.
  • my_database: نام پایگاه داده است.
  • root: نام کاربری پایگاه داده است.
  • utf8mb4: مجموعه کاراکتری مناسب برای پشتیبانی از زبان فارسی و سایر کاراکترهای یونیکد است.
  • PDO::ERRMODE_EXCEPTION: باعث می‌شود خطاهای PDO به‌صورت Exception گزارش شوند.

۴. دریافت تمام رکوردها با PDO

پس از اتصال به پایگاه داده، می‌توانیم با استفاده از متد query() یک دستور SELECT اجرا کنیم.

<?php

require_once "database.php";

$sql = "SELECT * FROM users";

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

$users = $result->fetchAll(PDO::FETCH_ASSOC);

foreach ($users as $user) {
    echo $user["name"] . "<br>";
}

?>

در این مثال چند نکته مهم وجود دارد:

  • query() دستور SQL را اجرا می‌کند.
  • fetchAll() تمام رکوردهای نتیجه را دریافت می‌کند.
  • PDO::FETCH_ASSOC نتیجه را به‌صورت آرایه انجمنی برمی‌گرداند.
  • foreach برای پیمایش رکوردهای دریافت‌شده استفاده می‌شود.

با اجرای این کد، نام تمام کاربران ذخیره‌شده در جدول users نمایش داده می‌شود.

۵. تفاوت fetch() و fetchAll() در PDO

در PDO دو متد پرکاربرد برای دریافت نتایج کوئری وجود دارد: fetch() و fetchAll().

متد fetch()

این متد یک رکورد از نتایج را در هر بار فراخوانی دریافت می‌کند. اگر رکورد دیگری وجود نداشته باشد، مقدار false برمی‌گرداند.

<?php

$stmt = $pdo->query("SELECT * FROM users");

while ($user = $stmt->fetch(PDO::FETCH_ASSOC)) {
    echo $user["name"] . "<br>";
}

?>

متد fetchAll()

این متد تمام رکوردهای باقی‌مانده در نتیجه کوئری را به‌صورت یک آرایه برمی‌گرداند.

<?php

$stmt = $pdo->query("SELECT * FROM users");

$users = $stmt->fetchAll(PDO::FETCH_ASSOC);

foreach ($users as $user) {
    echo $user["name"] . "<br>";
}

?>

تفاوت اصلی این دو متد:

متدعملکردکاربرد
fetch()دریافت یک رکورد در هر فراخوانیپردازش مرحله‌ای نتایج
fetchAll()دریافت تمام رکوردهای باقی‌ماندهنمایش مجموعه‌ای از اطلاعات

اگر تعداد رکوردها بسیار زیاد باشد، استفاده از fetch() در یک حلقه می‌تواند از بارگذاری هم‌زمان تمام نتایج در حافظه جلوگیری کند. البته نوع پردازش و تنظیمات درایور نیز در میزان مصرف حافظه تأثیر دارند.

۶. دریافت ستون‌های مشخص از جدول

همیشه نیازی نیست تمام ستون‌های جدول را دریافت کنیم. برای مثال، اگر فقط نام و ایمیل کاربران را لازم داشته باشیم، بهتر است همان دو ستون را انتخاب کنیم.

<?php

$sql = "SELECT name, email FROM users";

$stmt = $pdo->query($sql);

while ($user = $stmt->fetch(PDO::FETCH_ASSOC)) {
    echo "نام: " . htmlspecialchars($user["name"]) . "<br>";
    echo "ایمیل: " . htmlspecialchars($user["email"]) . "<br>";
    echo "<hr>";
}

?>

در این مثال، فقط ستون‌های name و email دریافت می‌شوند. همچنین برای نمایش مقادیر در HTML از htmlspecialchars() استفاده کرده‌ایم تا کاراکترهای خاص به شکل امن نمایش داده شوند.

۷. فیلتر کردن اطلاعات با WHERE

گاهی لازم است فقط رکوردهایی را دریافت کنیم که شرایط مشخصی دارند. برای این کار از عبارت WHERE استفاده می‌کنیم.

برای مثال، دریافت کاربری با شناسه ۱:

SELECT * FROM users
WHERE id = 1;

این دستور فقط رکوردی را برمی‌گرداند که مقدار ستون id آن برابر با ۱ باشد.

استفاده از WHERE در PHP با PDO

<?php

require_once "database.php";

$userId = 1;

$sql = "SELECT * FROM users WHERE id = :id";

$stmt = $pdo->prepare($sql);

$stmt->execute([
    "id" => $userId
]);

$user = $stmt->fetch(PDO::FETCH_ASSOC);

if ($user) {
    echo htmlspecialchars($user["name"]);
} else {
    echo "کاربری پیدا نشد.";
}

?>

در این مثال از Prepared Statement استفاده کرده‌ایم. مقدار شناسه از طریق پارامتر :id به کوئری منتقل می‌شود. این روش برای مقادیری که از ورودی کاربر دریافت می‌شوند، بسیار مهم است و از تزریق SQL جلوگیری می‌کند.

۸. استفاده از شرط‌های AND و OR در SELECT

برای ترکیب چند شرط در یک کوئری می‌توانیم از عملگرهای منطقی AND و OR استفاده کنیم.

استفاده از AND

عملگر AND زمانی نتیجه را انتخاب می‌کند که تمام شرط‌های مشخص‌شده برقرار باشند.

SELECT * FROM users
WHERE age >= 18 AND status = 'active';

این دستور کاربرانی را دریافت می‌کند که حداقل ۱۸ سال سن دارند و وضعیت حساب آن‌ها active است.

استفاده از OR

عملگر OR رکوردهایی را انتخاب می‌کند که حداقل یکی از شرط‌ها درباره آن‌ها برقرار باشد.

SELECT * FROM users
WHERE city = 'Tehran' OR city = 'Shiraz';

این کوئری کاربران ساکن تهران یا شیراز را دریافت می‌کند.

ترکیب AND و OR

SELECT * FROM users
WHERE status = 'active'
AND (city = 'Tehran' OR city = 'Shiraz');

استفاده از پرانتز در شرط‌های ترکیبی به مشخص شدن ترتیب منطقی ارزیابی کمک می‌کند.

۹. جستجوی اطلاعات با LIKE

اگر بخواهیم اطلاعاتی را بر اساس بخشی از یک متن جستجو کنیم، می‌توانیم از عملگر LIKE استفاده کنیم.

برای مثال، جستجوی کاربرانی که نام آن‌ها با Ali شروع می‌شود:

SELECT * FROM users
WHERE name LIKE 'Ali%';

در عملگر LIKE دو کاراکتر پرکاربرد وجود دارد:

  • %: نشان‌دهنده صفر یا چند کاراکتر است.
  • _: نشان‌دهنده دقیقاً یک کاراکتر است.

نمونه‌های استفاده از LIKE

-- نام‌هایی که با Ali شروع می‌شوند
SELECT * FROM users
WHERE name LIKE 'Ali%';

-- نام‌هایی که به Ali ختم می‌شوند
SELECT * FROM users
WHERE name LIKE '%Ali';

-- نام‌هایی که عبارت Ali در هر قسمت آن‌ها وجود دارد
SELECT * FROM users
WHERE name LIKE '%Ali%';

-- نام‌هایی که کاراکتر دوم آن‌ها a است
SELECT * FROM users
WHERE name LIKE '_a%';

جستجوی امن با LIKE در PHP

<?php

$search = "Ali";

$sql = "SELECT * FROM users
        WHERE name LIKE :search";

$stmt = $pdo->prepare($sql);

$stmt->execute([
    "search" => "%" . $search . "%"
]);

$users = $stmt->fetchAll(PDO::FETCH_ASSOC);

foreach ($users as $user) {
    echo htmlspecialchars($user["name"]) . "<br>";
}

?>

در این مثال، عبارت جستجو با دو علامت درصد ترکیب شده است تا نام‌هایی که عبارت موردنظر در هر قسمت آن‌ها وجود دارد پیدا شوند.

۱۰. مرتب‌سازی اطلاعات با ORDER BY

برای مرتب‌سازی نتایج یک کوئری از عبارت ORDER BY استفاده می‌کنیم.

برای مثال، مرتب‌سازی کاربران بر اساس سن:

SELECT * FROM users
ORDER BY age ASC;

عبارت ASC نتایج را به‌صورت صعودی مرتب می‌کند؛ یعنی مقادیر کوچک‌تر ابتدا نمایش داده می‌شوند.

برای مرتب‌سازی نزولی از DESC استفاده می‌کنیم:

SELECT * FROM users
ORDER BY age DESC;

در این حالت کاربران با سن بیشتر در ابتدای نتایج قرار می‌گیرند.

مرتب‌سازی بر اساس چند ستون

امکان مرتب‌سازی بر اساس چند ستون نیز وجود دارد.

SELECT * FROM users
ORDER BY city ASC, age DESC;

در این مثال ابتدا کاربران بر اساس شهر به‌صورت صعودی مرتب می‌شوند و در میان کاربران هر شهر، افراد بر اساس سن به‌صورت نزولی قرار می‌گیرند.

۱۱. محدود کردن تعداد نتایج با LIMIT

اگر جدول تعداد زیادی رکورد داشته باشد، ممکن است بخواهیم فقط تعداد مشخصی از آن‌ها را دریافت کنیم. برای این کار از عبارت LIMIT استفاده می‌کنیم.

SELECT * FROM users
LIMIT 5;

این دستور حداکثر پنج رکورد از جدول کاربران دریافت می‌کند.

استفاده از LIMIT همراه با OFFSET

برای مشخص کردن نقطه شروع دریافت رکوردها می‌توانیم از OFFSET استفاده کنیم.

SELECT * FROM users
LIMIT 5 OFFSET 10;

این کوئری از رکورد یازدهم شروع می‌کند و حداکثر پنج رکورد برمی‌گرداند.

شکل کوتاه‌تر همین دستور به‌صورت زیر است:

SELECT * FROM users
LIMIT 10, 5;

در این ساختار، عدد اول تعداد رکوردهایی است که باید نادیده گرفته شوند و عدد دوم تعداد رکوردهای موردنیاز است.

نکته مهم: برای صفحه‌بندی دقیق، بهتر است از یک مرتب‌سازی پایدار و معمولاً یک ستون یکتا مانند id استفاده کنید.

۱۲. دریافت اطلاعات یکتا با DISTINCT

اگر بخواهیم مقادیر تکراری یک ستون را حذف کنیم و فقط مقادیر یکتا را دریافت کنیم، از DISTINCT استفاده می‌کنیم.

SELECT DISTINCT city
FROM users;

این کوئری فهرست شهرهایی را برمی‌گرداند که حداقل یک کاربر در آن‌ها ثبت شده است. اگر چند کاربر در یک شهر باشند، نام آن شهر فقط یک بار در نتیجه ظاهر می‌شود.

می‌توان DISTINCT را با چند ستون نیز استفاده کرد:

SELECT DISTINCT city, status
FROM users;

در این حالت ترکیب‌های تکراری شهر و وضعیت حذف می‌شوند؛ نه اینکه هر ستون به‌صورت مستقل یکتا شود.

۱۳. شمارش رکوردها با COUNT

تابع COUNT() برای شمارش رکوردها یا مقادیر غیر NULL یک ستون استفاده می‌شود.

SELECT COUNT(*) AS total_users
FROM users;

این کوئری تعداد کل رکوردهای جدول کاربران را محاسبه می‌کند.

برای دریافت نتیجه در PHP با PDO:

<?php

$sql = "SELECT COUNT(*) FROM users";

$totalUsers = $pdo->query($sql)->fetchColumn();

echo "تعداد کاربران: " . $totalUsers;

?>

متد fetchColumn() مقدار یک ستون از اولین رکورد نتیجه را برمی‌گرداند.

توجه داشته باشید که COUNT(*) تعداد رکوردها را می‌شمارد، اما COUNT(email) فقط مقادیر غیر NULL ستون ایمیل را محاسبه می‌کند.

۱۴. دریافت اطلاعات با GROUP BY

عبارت GROUP BY برای گروه‌بندی رکوردها بر اساس یک یا چند ستون استفاده می‌شود. این عبارت معمولاً همراه با توابعی مانند COUNT، SUM، AVG، MIN و MAX کاربرد دارد.

برای مثال، تعداد کاربران هر شهر:

SELECT city, COUNT(*) AS total
FROM users
GROUP BY city;

نتیجه این کوئری تعداد کاربران را به تفکیک شهر نمایش می‌دهد.

محاسبه میانگین سن کاربران هر شهر

SELECT city, AVG(age) AS average_age
FROM users
GROUP BY city;

تابع AVG() میانگین مقادیر عددی ستون مشخص‌شده را محاسبه می‌کند.

۱۵. فیلتر کردن گروه‌ها با HAVING

برای فیلتر کردن نتایج گروه‌بندی‌شده از عبارت HAVING استفاده می‌کنیم. تفاوت اصلی WHERE و HAVING این است که WHERE رکوردها را قبل از گروه‌بندی فیلتر می‌کند، اما HAVING روی گروه‌های ایجادشده اعمال می‌شود.

SELECT city, COUNT(*) AS total
FROM users
GROUP BY city
HAVING COUNT(*) > 5;

این دستور فقط شهرهایی را نمایش می‌دهد که بیش از پنج کاربر دارند.

می‌توان WHERE و HAVING را در یک کوئری ترکیب کرد:

SELECT city, COUNT(*) AS total
FROM users
WHERE status = 'active'
GROUP BY city
HAVING COUNT(*) > 2;

ابتدا کاربران فعال انتخاب می‌شوند، سپس بر اساس شهر گروه‌بندی شده و در نهایت فقط گروه‌هایی نمایش داده می‌شوند که بیش از دو کاربر دارند.

۱۶. دریافت اطلاعات با BETWEEN

عملگر BETWEEN برای انتخاب مقادیری استفاده می‌شود که در یک محدوده مشخص قرار دارند. این محدوده شامل مقدار ابتدا و انتها نیز می‌شود.

SELECT * FROM users
WHERE age BETWEEN 18 AND 30;

این کوئری کاربران ۱۸ تا ۳۰ ساله را دریافت می‌کند.

برای دریافت اطلاعاتی که خارج از محدوده هستند، می‌توان از NOT BETWEEN استفاده کرد:

SELECT * FROM users
WHERE age NOT BETWEEN 18 AND 30;

۱۷. دریافت اطلاعات با IN و NOT IN

اگر بخواهیم یک ستون را با چند مقدار مشخص مقایسه کنیم، استفاده از IN می‌تواند کد را خواناتر کند.

SELECT * FROM users
WHERE city IN ('Tehran', 'Shiraz', 'Tabriz');

این دستور کاربران سه شهر مشخص‌شده را دریافت می‌کند.

برای انتخاب مقادیری که در فهرست وجود ندارند از NOT IN استفاده می‌کنیم:

SELECT * FROM users
WHERE city NOT IN ('Tehran', 'Shiraz');

نکته: اگر فهرست مورد استفاده در NOT IN شامل مقدار NULL باشد، رفتار منطق SQL ممکن است باعث شود نتیجه مورد انتظار را دریافت نکنید. در چنین شرایطی باید مقادیر NULL را مدیریت کنید یا از NOT EXISTS استفاده کنید.

۱۸. بررسی مقادیر NULL با IS NULL و IS NOT NULL

در MySQL مقدار NULL به معنای نبودن یا نامشخص بودن مقدار است. برای بررسی NULL نباید از عملگر مساوی استفاده کنیم.

برای دریافت کاربرانی که شماره تلفن آن‌ها ثبت نشده است:

SELECT * FROM users
WHERE phone IS NULL;

برای دریافت کاربرانی که شماره تلفن دارند:

SELECT * FROM users
WHERE phone IS NOT NULL;

استفاده از phone = NULL روش صحیح بررسی NULL نیست. برای این کار همیشه از IS NULL یا IS NOT NULL استفاده کنید.

۱۹. دریافت اطلاعات از چند جدول با JOIN

در پروژه‌های واقعی، اطلاعات معمولاً در چند جدول مرتبط ذخیره می‌شوند. برای دریافت اطلاعات از چند جدول می‌توانیم از دستورهای JOIN استفاده کنیم.

فرض کنید دو جدول زیر را داریم:

جدول users:

idname
1علی
2مریم

جدول orders:

iduser_idtotal_price
1011500000
1021300000
1032700000

INNER JOIN

INNER JOIN فقط رکوردهایی را برمی‌گرداند که در هر دو جدول تطابق داشته باشند.

SELECT
    users.name,
    orders.total_price
FROM users
INNER JOIN orders
ON users.id = orders.user_id;

در این مثال نام کاربران همراه با مبلغ سفارش‌های آن‌ها نمایش داده می‌شود.

LEFT JOIN

LEFT JOIN تمام رکوردهای جدول سمت چپ را برمی‌گرداند؛ حتی اگر رکورد مرتبطی در جدول سمت راست وجود نداشته باشد.

SELECT
    users.name,
    orders.total_price
FROM users
LEFT JOIN orders
ON users.id = orders.user_id;

اگر کاربری هیچ سفارشی نداشته باشد، اطلاعات او همچنان در نتیجه ظاهر می‌شود و ستون‌های مربوط به سفارش مقدار NULL خواهند داشت.

۲۰. استفاده از Prepared Statement برای SELECT

زمانی که مقدارهای یک کوئری از کاربر دریافت می‌شوند، باید از Prepared Statement استفاده کنیم. این روش از قرار دادن مستقیم ورودی کاربر داخل متن SQL جلوگیری می‌کند و نقش مهمی در جلوگیری از SQL Injection دارد.

نمونه ناامن:

<?php

$id = $_GET["id"];

$sql = "SELECT * FROM users WHERE id = $id";

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

?>

در این روش، مقدار ورودی بدون پارامترگذاری وارد کوئری شده است. این کار می‌تواند زمینه‌ساز آسیب‌پذیری SQL Injection شود.

نمونه امن با PDO:

<?php

$id = filter_input(INPUT_GET, "id", FILTER_VALIDATE_INT);

if ($id === false || $id === null || $id < 1) {
    exit("شناسه نامعتبر است.");
}

$sql = "SELECT * FROM users WHERE id = :id";

$stmt = $pdo->prepare($sql);

$stmt->execute([
    "id" => $id
]);

$user = $stmt->fetch(PDO::FETCH_ASSOC);

if ($user) {
    echo htmlspecialchars($user["name"]);
} else {
    echo "کاربر موردنظر پیدا نشد.";
}

?>

در این مثال، ابتدا شناسه ورودی اعتبارسنجی شده و سپس از طریق پارامتر :id به کوئری منتقل می‌شود.

توجه داشته باشید که Prepared Statement برای مقدارها استفاده می‌شود، نه برای نام جدول یا نام ستون. اگر نام ستون یا ترتیب مرتب‌سازی از ورودی کاربر انتخاب می‌شود، باید آن را با یک فهرست مجاز کنترل کنید.

۲۱. دریافت اطلاعات از MySQL با MySQLi

علاوه بر PDO، می‌توانیم با استفاده از افزونه MySQLi نیز اطلاعات را از پایگاه داده دریافت کنیم.

ابتدا اتصال به پایگاه داده را ایجاد می‌کنیم:

<?php

$mysqli = new mysqli(
    "localhost",
    "root",
    "",
    "my_database"
);

if ($mysqli->connect_error) {
    error_log($mysqli->connect_error);
    exit("خطا در اتصال به پایگاه داده.");
}

$mysqli->set_charset("utf8mb4");

?>

دریافت تمام رکوردها با query()

<?php

$sql = "SELECT * FROM users";

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

while ($user = $result->fetch_assoc()) {
    echo htmlspecialchars($user["name"]) . "<br>";
}

$result->free();

?>

در این مثال، متد query() دستور SQL را اجرا می‌کند و متد fetch_assoc() هر بار یک رکورد را به‌صورت آرایه انجمنی برمی‌گرداند.

متد free() حافظه مربوط به نتیجه کوئری را آزاد می‌کند.

استفاده از Prepared Statement در MySQLi

<?php

$id = 1;

$stmt = $mysqli->prepare(
    "SELECT * FROM users WHERE id = ?"
);

$stmt->bind_param("i", $id);

$stmt->execute();

$result = $stmt->get_result();

$user = $result->fetch_assoc();

if ($user) {
    echo htmlspecialchars($user["name"]);
} else {
    echo "کاربر پیدا نشد.";
}

$stmt->close();

?>

در این مثال علامت سؤال به‌عنوان جایگاه پارامتر استفاده شده است. متد bind_param() مقدار را به کوئری متصل می‌کند.

حرف i نشان‌دهنده عدد صحیح است. در MySQLi انواع اصلی پارامترها عبارت‌اند از:

  • i: عدد صحیح (Integer)
  • d: عدد اعشاری (Double)
  • s: رشته (String)
  • b: داده باینری (Blob)

متد get_result() در همه محیط‌های PHP در دسترس نیست و به پشتیبانی درایور mysqlnd نیاز دارد. در صورت نبود این قابلیت می‌توان از bind_result() و fetch() استفاده کرد.

۲۲. نمایش اطلاعات MySQL در قالب جدول HTML

یکی از کاربردهای اصلی SELECT در PHP، نمایش اطلاعات پایگاه داده در صفحات وب است. در مثال زیر تمام کاربران را از پایگاه داده دریافت کرده و در یک جدول HTML نمایش می‌دهیم.

<?php

require_once "database.php";

$sql = "SELECT id, name, email, age
        FROM users
        ORDER BY id DESC";

$stmt = $pdo->query($sql);

$users = $stmt->fetchAll(PDO::FETCH_ASSOC);

?>

<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
    <meta charset=UTF-8>
    <title>لیست کاربران</title>
</head>
<body>

<h1>لیست کاربران</h1>

<table border=1 cellpadding="10">
    <thead>
        <tr>
            <th>شناسه</th>
            <th>نام</th>
            <th>ایمیل</th>
            <th>سن</th>
        </tr>
    </thead>

    <tbody>

    <?php if (count($users) > 0): ?>

        <?php foreach ($users as $user): ?>

            <tr>
                <td>
                    <?= htmlspecialchars((string) $user["id"]) ?>
                </td>

                <td>
                    <?= htmlspecialchars($user["name"]) ?>
                </td>

                <td>
                    <?= htmlspecialchars($user["email"]) ?>
                </td>

                <td>
                    <?= htmlspecialchars((string) $user["age"]) ?>
                </td>
            </tr>

        <?php endforeach; ?>

    <?php else: ?>

        <tr>
            <td colspan="4">
                هیچ کاربری ثبت نشده است.
            </td>
        </tr>

    <?php endif; ?>

    </tbody>
</table>

</body>
</html>

در این مثال ابتدا اطلاعات کاربران دریافت می‌شود و سپس با استفاده از حلقه foreach هر رکورد در یک ردیف جدول قرار می‌گیرد.

همچنین برای جلوگیری از اجرای ناخواسته کدهای HTML و JavaScript در مرورگر، مقادیر خروجی با htmlspecialchars() پاک‌سازی می‌شوند.

۲۳. ساخت سیستم جستجوی کاربران با PHP و MySQL

در این بخش یک مثال کاربردی ایجاد می‌کنیم که کاربر بتواند نام افراد را جستجو کند. برای این کار از فرم HTML، دستور SELECT، عبارت LIKE و Prepared Statement استفاده می‌کنیم.

<?php

require_once "database.php";

$search = trim($_GET["search"] ?? "");

$users = [];

if ($search !== "") {

    $sql = "SELECT id, name, email
            FROM users
            WHERE name LIKE :search
            ORDER BY id DESC";

    $stmt = $pdo->prepare($sql);

    $stmt->execute([
        "search" => "%" . $search . "%"
    ]);

    $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
}

?>

<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
    <meta charset=UTF-8>
    <title>جستجوی کاربران</title>
</head>
<body>

<h1>جستجوی کاربران</h1>

<form method="GET">

    <input
        type=text
        name=search
        placeholder="نام کاربر را وارد کنید"
        value="<?= htmlspecialchars($search) ?>"
    >

    <button type=submit>جستجو</button>

</form>

<hr>

<?php if ($search !== ""): ?>

    <?php if (count($users) > 0): ?>

        <h2>نتایج جستجو</h2>

        <ul>

            <?php foreach ($users as $user): ?>

                <li>
                    <?= htmlspecialchars($user["name"]) ?>
                    -
                    <?= htmlspecialchars($user["email"]) ?>
                </li>

            <?php endforeach; ?>

        </ul>

    <?php else: ?>

        <p>کاربری با این نام پیدا نشد.</p>

    <?php endif; ?>

<?php endif; ?>

</body>
</html>

در این پروژه، مقدار جستجو از طریق متد GET دریافت می‌شود. سپس با استفاده از Prepared Statement اطلاعات مرتبط از پایگاه داده استخراج می‌شوند.

برای پروژه‌های واقعی بهتر است محدودیت طول ورودی، اعتبارسنجی مناسب و صفحه‌بندی نتایج نیز در نظر گرفته شود.

۲۴. پیاده‌سازی صفحه‌بندی اطلاعات با PHP و MySQL

وقتی تعداد کاربران زیاد باشد، نمایش تمام اطلاعات در یک صفحه مناسب نیست. صفحه‌بندی یا Pagination کمک می‌کند اطلاعات را در چند صفحه تقسیم کنیم.

برای شروع، تعداد رکوردهای هر صفحه و شماره صفحه فعلی را مشخص می‌کنیم:

<?php

require_once "database.php";

$limit = 10;

$page = filter_input(
    INPUT_GET,
    "page",
    FILTER_VALIDATE_INT
);

if ($page === false || $page === null || $page < 1) {
    $page = 1;
}

$stmt = $pdo->query(
    "SELECT COUNT(*) FROM users"
);

$totalUsers = (int) $stmt->fetchColumn();

$totalPages = (int) ceil($totalUsers / $limit);

$offset = ($page - 1) * $limit;

$sql = "SELECT id, name, email
        FROM users
        ORDER BY id DESC
        LIMIT :limit OFFSET :offset";

$stmt = $pdo->prepare($sql);

$stmt->bindValue(":limit", $limit, PDO::PARAM_INT);
$stmt->bindValue(":offset", $offset, PDO::PARAM_INT);

$stmt->execute();

$users = $stmt->fetchAll(PDO::FETCH_ASSOC);

?>

در این کد ابتدا تعداد کل کاربران محاسبه می‌شود. سپس با تقسیم تعداد کاربران بر تعداد رکوردهای هر صفحه، تعداد صفحات به دست می‌آید.

مقدار $offset مشخص می‌کند که دریافت اطلاعات از کدام رکورد شروع شود. از آنجا که LIMIT و OFFSET در این مثال پارامترهای عددی هستند، آن‌ها را با PDO::PARAM_INT متصل کرده‌ایم.

نمایش کاربران و لینک صفحات

<?php foreach ($users as $user): ?>

    <div>
        <strong>
            <?= htmlspecialchars($user["name"]) ?>
        </strong>

        <span>
            <?= htmlspecialchars($user["email"]) ?>
        </span>
    </div>

<?php endforeach; ?>

<hr>

<?php if ($page > 1): ?>

    <a href="?page=<?= $page - 1 ?>">
        صفحه قبلی
    </a>

<?php endif; ?>

<span>
    صفحه <?= $page ?> از <?= $totalPages ?>
</span>

<?php if ($page < $totalPages): ?>

    <a href="?page=<?= $page + 1 ?>">
        صفحه بعدی
    </a>

<?php endif; ?>

در این مثال لینک صفحه قبلی و بعدی فقط در صورت وجود صفحه مربوطه نمایش داده می‌شود.

در پروژه‌های بزرگ، برای صفحه‌بندی باید به هزینه اجرای COUNT و محدودیت‌های OFFSET نیز توجه کرد. برای مجموعه‌داده‌های بسیار بزرگ، روش‌هایی مانند Keyset Pagination می‌توانند عملکرد بهتری داشته باشند.

۲۵. تفاوت query() و prepare() در PDO

دو روش رایج برای اجرای دستورهای SELECT در PDO، متدهای query() و prepare() هستند.

ویژگیquery()prepare()
اجرای مستقیم SQLبلهابتدا دستور آماده می‌شود و سپس اجرا می‌شود
پارامترگذاریبرای ورودی‌های متغیر مناسب نیستاز پارامترهای نام‌دار یا علامت سؤال پشتیبانی می‌کند
استفاده برای ورودی کاربرخیر، مگر با روش‌های امن و مناسبروش پیشنهادی
کاربرد معمولکوئری ثابت و بدون ورودیکوئری دارای پارامتر

نمونه استفاده از query():

<?php

$stmt = $pdo->query(
    "SELECT * FROM users ORDER BY id DESC"
);

$users = $stmt->fetchAll(PDO::FETCH_ASSOC);

?>

نمونه استفاده از prepare():

<?php

$stmt = $pdo->prepare(
    "SELECT * FROM users WHERE age > :age"
);

$stmt->execute([
    "age" => 18
]);

$users = $stmt->fetchAll(PDO::FETCH_ASSOC);

?>

۲۶. نکات مهم و حرفه‌ای هنگام استفاده از SELECT

  • انتخاب ستون‌های موردنیاز: به‌جای استفاده همیشگی از SELECT * فقط ستون‌هایی را دریافت کنید که واقعاً به آن‌ها نیاز دارید.
  • استفاده از Prepared Statement: هر زمان مقدار کوئری از ورودی کاربر دریافت می‌شود، از پارامترگذاری امن استفاده کنید.
  • استفاده از LIMIT: در جدول‌های بزرگ، برای جلوگیری از دریافت حجم بسیار زیادی از اطلاعات، نتایج را محدود کنید.
  • مرتب‌سازی نتایج: اگر ترتیب نمایش اهمیت دارد، همیشه از ORDER BY استفاده کنید؛ ترتیب رکوردها بدون آن تضمین‌شده نیست.
  • مدیریت خطاها: پیام‌های فنی و جزئیات خطاهای پایگاه داده را در محیط عملیاتی مستقیماً به کاربران نمایش ندهید. آن‌ها را در گزارش خطا ثبت کنید.
  • پاک‌سازی خروجی HTML: داده‌های دریافت‌شده از پایگاه داده را هنگام نمایش در HTML با htmlspecialchars() مناسب بافت خروجی کنید.
  • استفاده از ایندکس: برای ستون‌هایی که زیاد در شرط‌های WHERE یا JOIN استفاده می‌شوند، بررسی ایندکس مناسب می‌تواند عملکرد کوئری را بهبود دهد.
  • بررسی عملکرد کوئری: برای تحلیل اجرای کوئری‌های سنگین می‌توانید از دستور EXPLAIN در MySQL استفاده کنید.
  • بستن منابع: پس از اتمام کار با نتایج، در صورت نیاز منابع مربوط به آن‌ها را آزاد کنید؛ به‌خصوص هنگام استفاده از MySQLi.

۲۷. خطاهای رایج هنگام استفاده از SELECT در PHP

خطای اول: اشتباه در نام جدول یا ستون

اگر نام جدول یا ستون در دستور SQL اشتباه نوشته شود، MySQL خطا برمی‌گرداند. نام‌ها را با ساختار واقعی پایگاه داده بررسی کنید.

خطای دوم: استفاده نادرست از fetchAll()

متد fetchAll() یک آرایه از رکوردها برمی‌گرداند. بنابراین برای نمایش آن باید از حلقه‌هایی مانند foreach استفاده کنید.

خطای سوم: استفاده از fetch() برای تمام رکوردها بدون حلقه

متد fetch() فقط یک رکورد را در هر بار فراخوانی برمی‌گرداند. اگر قصد دریافت تمام رکوردها را دارید، باید آن را در یک حلقه اجرا کنید یا از fetchAll() استفاده کنید.

خطای چهارم: استفاده مستقیم از ورودی کاربر در SQL

قرار دادن مستقیم ورودی کاربر در دستور SQL می‌تواند باعث آسیب‌پذیری SQL Injection شود. همیشه از Prepared Statement استفاده کنید.

خطای پنجم: فراموش کردن charset

اگر مجموعه کاراکتری اتصال به پایگاه داده به‌درستی تنظیم نشود، ممکن است حروف فارسی به شکل نامناسب نمایش داده شوند. استفاده از utf8mb4 در اتصال و تنظیمات مناسب جدول توصیه می‌شود.

خطای ششم: استفاده از ORDER BY نامعتبر

نام ستون و جهت مرتب‌سازی را نباید بدون اعتبارسنجی از ورودی کاربر دریافت کرد. برای چنین مواردی از فهرست مقادیر مجاز استفاده کنید.

۲۸. تمرین عملی

برای تسلط بیشتر بر دستور SELECT، تمرین زیر را انجام دهید.

فرض کنید جدولی با نام products دارید که شامل ستون‌های زیر است:

  • id: شناسه محصول
  • name: نام محصول
  • price: قیمت محصول
  • category: دسته‌بندی محصول
  • stock: تعداد موجودی

وظایف زیر را با استفاده از PHP و MySQL انجام دهید:

  1. تمام محصولات را از جدول دریافت و نمایش دهید.
  2. فقط نام و قیمت محصولات را دریافت کنید.
  3. محصولاتی را نمایش دهید که قیمت آن‌ها بیشتر از ۵۰۰ هزار تومان است.
  4. محصولات را بر اساس قیمت از بیشترین به کمترین مرتب کنید.
  5. فقط پنج محصول اول را دریافت کنید.
  6. محصولاتی را پیدا کنید که موجودی آن‌ها بیشتر از صفر است.
  7. محصولات یک دسته‌بندی مشخص را با Prepared Statement دریافت کنید.
  8. تعداد کل محصولات را با COUNT محاسبه کنید.
  9. تعداد محصولات هر دسته‌بندی را با GROUP BY نمایش دهید.
  10. یک فرم جستجوی محصول با استفاده از LIKE ایجاد کنید.

۲۹. سوالات متداول درباره MySQL SELECT

۱. دستور SELECT در MySQL چه کاربردی دارد؟

دستور SELECT برای دریافت و خواندن اطلاعات از یک یا چند جدول در پایگاه داده MySQL استفاده می‌شود.

۲. تفاوت SELECT * با SELECT name چیست؟

دستور SELECT * تمام ستون‌های جدول را دریافت می‌کند، اما SELECT name فقط ستون name را برمی‌گرداند.

۳. تفاوت fetch() و fetchAll() در PHP چیست؟

fetch() در هر فراخوانی یک رکورد را برمی‌گرداند، اما fetchAll() تمام رکوردهای باقی‌مانده را در قالب یک آرایه دریافت می‌کند.

۴. چگونه فقط یک رکورد را از MySQL دریافت کنیم؟

می‌توانیم از شرط WHERE برای انتخاب رکورد موردنظر استفاده کنیم و نتیجه را با fetch() دریافت کنیم. همچنین برای محدود کردن نتیجه می‌توان از LIMIT 1 استفاده کرد.

۵. دستور ORDER BY چه کاربردی دارد؟

ORDER BY برای مرتب‌سازی نتایج بر اساس یک یا چند ستون استفاده می‌شود.

۶. دستور LIMIT چه کاری انجام می‌دهد؟

LIMIT تعداد رکوردهای برگشتی از یک کوئری را محدود می‌کند و معمولاً برای صفحه‌بندی کاربرد دارد.

۷. تفاوت WHERE و HAVING چیست؟

WHERE رکوردها را قبل از گروه‌بندی فیلتر می‌کند، اما HAVING برای فیلتر کردن گروه‌های ایجادشده با GROUP BY استفاده می‌شود.

۸. آیا استفاده از SELECT * مناسب است؟

برای آزمایش و مثال‌های ساده مناسب است، اما در پروژه‌های واقعی بهتر است ستون‌های موردنیاز را مشخص کنید تا اطلاعات غیرضروری دریافت نشوند.

۹. چگونه از SQL Injection در دستور SELECT جلوگیری کنیم؟

با استفاده از Prepared Statement در PDO یا MySQLi و اعتبارسنجی ورودی‌های کاربر می‌توان خطر SQL Injection را کاهش داد.

۱۰. آیا SELECT اطلاعات پایگاه داده را تغییر می‌دهد؟

خیر. دستور SELECT برای خواندن اطلاعات است و به‌خودی‌خود رکوردها را اضافه، ویرایش یا حذف نمی‌کند.

جمع‌بندی

در این قسمت از آموزش PHP یاد گرفتیم چگونه با استفاده از دستور SELECT اطلاعات را از پایگاه داده MySQL دریافت کنیم. ابتدا با ساختار اصلی SELECT و اتصال PHP به MySQL آشنا شدیم و سپس روش دریافت تمام رکوردها، انتخاب ستون‌های مشخص، فیلتر کردن اطلاعات با WHERE، مرتب‌سازی با ORDER BY و محدود کردن نتایج با LIMIT را بررسی کردیم.

همچنین با دستورهای DISTINCT، COUNT، GROUP BY، HAVING، BETWEEN، IN، LIKE و JOIN آشنا شدیم و نحوه اجرای کوئری‌های امن با Prepared Statement در PDO و MySQLi را یاد گرفتیم.

دستور SELECT یکی از مهم‌ترین ابزارهای کار با پایگاه داده است و در تقریباً تمام پروژه‌های PHP مانند سیستم مدیریت کاربران، فروشگاه اینترنتی، وبلاگ، پنل مدیریت و سیستم‌های جستجو استفاده می‌شود. تسلط بر این دستور و ترکیب آن با PHP، پایه‌ای مهم برای ساخت برنامه‌های کاربردی و حرفه‌ای خواهد بود.

پیشنهاد ویژه ما:آموزش رایگان PHP

اشتراک گذاری :
ملیکا اسمعیلی
نویسنده

ملیکا اسمعیلی

من یک توسعه‌دهنده وردپرس و متخصص سئو هستم که به کسب‌وکارها کمک می‌کنم وب‌سایت‌های خود را به پلتفرم‌هایی بهینه برای موتورهای جستجو و با نرخ تبدیل بالا تبدیل کنند. با تجربه عملی در Python، HTML و CSS، مهارت‌های فنی توسعه وب را با درک عمیق از سئوی داخلی (On-Page SEO) و سئوی محتوا ترکیب می‌کنم؛

TEST_VALUE_123
https://t.me/artarasaneh
tel:09193426251
https://wa.me/+989193426251
https://instagram.com/artarasaneh_com