PDO ба өгөгдлийн сан
Вэб програм бараг бүгдэд өгөгдлийг хадгалж, уншдаг. Файлд хадгалах нь жижиг тохиолдолд хангалттай боловч жинхэнэ апп нь өгөгдлийн сан (database) ашигладаг. PHP-д өгөгдлийн сантай ажиллах хамгийн зөв арга бол PDO (PHP Data Objects) юм. PDO нь нэг интерфейсээр MySQL, PostgreSQL, SQLite болон бусад олон өгөгдлийн сантай ажилладаг — нэг аргаар суралцаж, хаана ч хэрэглэж болно.
PDO холболт нээх
PDO объект үүсгэхдээ DSN (Data Source Name) дамжуулна. DSN нь өгөгдлийн сангийн төрөл, хост, нэр зэргийг агуулсан холбооны мэдээлэл:
<?php
// SQLite — файл дээр суурилсан, суулгах шаардлагагүй
try {
$pdo = new PDO('sqlite:myapp.db');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
echo "SQLite холбогдлоо.\n";
} catch (PDOException $e) {
echo "Холболтын алдаа: " . $e->getMessage();
}
// MySQL — жинхэнэ серверт холбогдох
try {
$dsn = "mysql:host=localhost;dbname=myapp;charset=utf8mb4";
$pdo = new PDO($dsn, "root", "нууц_үг");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
echo "MySQL холбогдлоо.\n";
} catch (PDOException $e) {
echo "Холболтын алдаа: " . $e->getMessage();
}
?>
PDO::ERRMODE_EXCEPTION нь алдаа гарахад PDOException шидэхийг тохируулна — try/catch-аар зохицуулж болдог болгоно. PDO::FETCH_ASSOC нь мөр бүрийг нэрлэгдсэн массив болгон буцаана.
Холболтыг тусдаа файлд хадгалах
Холболтын кодыг нэг дор байлгах нь шилдэг дадал. Ашиглах газар бүрт нэг дуудна:
<?php
// файл: lib/database.php
function өгөгдлийн_сан_авах(): PDO {
static $pdo = null;
if ($pdo !== null) {
return $pdo; // Байгаа холболтыг буцаана — шинэ нээхгүй
}
$хост = $_ENV['DB_HOST'] ?? 'localhost';
$нэр = $_ENV['DB_NAME'] ?? 'myapp';
$хэрэглэгч = $_ENV['DB_USER'] ?? 'root';
$нууц = $_ENV['DB_PASS'] ?? '';
$dsn = "mysql:host={$хост};dbname={$нэр};charset=utf8mb4";
$pdo = new PDO($dsn, $хэрэглэгч, $нууц, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
return $pdo;
}
?>
static $pdo = null нь функцийг хэдэн удаа дуудсан ч зөвхөн нэг холболт нээгддэг болгоно — энэ нь Singleton загварын хялбар хэлбэр юм.
Prepared statement — аюулгүй асуулга
PDO-ийн хамгийн чухал хэрэгсэл бол prepared statement юм. Хэрэглэгчийн оруулсан утгыг SQL-д шууд оруулахгүй, тусдаа дамжуулах замаар SQL injection халдлагаас бүрэн хамгаалдаг:
<?php
$pdo = өгөгдлийн_сан_авах();
// ❌ БУРУУ — SQL injection-д эмзэг
$нэр = $_POST['нэр']; // хэрэглэгч "'OR'1'='1" оруулбал бүх мөр задарна
$sql = "SELECT * FROM users WHERE нэр = '{$нэр}'"; // ХЭЗЭЭ ЧИ БҮҮЛ
// ✅ ЗӨВ — Prepared statement
$sql = "SELECT * FROM users WHERE нэр = :нэр";
$stmt = $pdo->prepare($sql);
$stmt->execute([':нэр' => $нэр]); // Утга тусдаа дамжина
$хэрэглэгч = $stmt->fetch();
// Асуулта тэмдэглэгч (?) ашиглан
$stmt = $pdo->prepare("SELECT * FROM users WHERE нэр = ? AND нас > ?");
$stmt->execute([$нэр, 18]);
$үр_дүн = $stmt->fetchAll();
?>
Prepared statement-т хэрэглэгчийн утга ', ;, -- зэрэг аюултай тэмдэгтүүд хамаагүй — PHP нь тэдгээрийг автоматаар аюулгүй болгоно.
Хүснэгт үүсгэх ба анхны өгөгдөл
SQLite ашиглан туршилтын өгөгдлийн сан бүтээх жишээ — MySQL дээр яг адилхан ажилладаг:
<?php
$pdo = new PDO('sqlite:сургалт.db');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Хүснэгт үүсгэх
$pdo->exec("
CREATE TABLE IF NOT EXISTS хэрэглэгчид (
id INTEGER PRIMARY KEY AUTOINCREMENT,
нэр TEXT NOT NULL,
имэйл TEXT NOT NULL UNIQUE,
нас INTEGER,
огноо TEXT DEFAULT CURRENT_TIMESTAMP
)
");
echo "Хүснэгт үүслээ.\n";
// Туршилтын өгөгдөл оруулах
$хэрэглэгчид = [
['Болд', 'bold@example.mn', 25],
['Сарнай', 'sarnai@example.mn', 22],
['Тамир', 'tamir@example.mn', 30],
];
$stmt = $pdo->prepare("INSERT INTO хэрэглэгчид (нэр, имэйл, нас) VALUES (?, ?, ?)");
foreach ($хэрэглэгчид as [$нэр, $имэйл, $нас]) {
$stmt->execute([$нэр, $имэйл, $нас]);
}
echo "Өгөгдөл оруулав.\n";
// Шалгах
$stmt = $pdo->query("SELECT * FROM хэрэглэгчид");
$бүгд = $stmt->fetchAll();
foreach ($бүгд as $хэрэглэгч) {
echo "{$хэрэглэгч['id']}. {$хэрэглэгч['нэр']} — {$хэрэглэгч['имэйл']}\n";
}
?>
exec() нь үр дүн буцаадаггүй SQL (CREATE, DROP) дуудахад, query() нь параметргүй SELECT-д, prepare() нь параметртэй SQL-д ашиглагддаг.
PDO-ийн fetch горимууд
<?php
$stmt = $pdo->query("SELECT * FROM хэрэглэгчид");
// FETCH_ASSOC — нэрлэгдсэн массив (хамгийн түгээмэл)
$мөр = $stmt->fetch(PDO::FETCH_ASSOC);
// ['id' => 1, 'нэр' => 'Болд', 'имэйл' => '...']
// FETCH_OBJ — объект болгон авна
$stmt = $pdo->query("SELECT * FROM хэрэглэгчид");
$объект = $stmt->fetch(PDO::FETCH_OBJ);
echo $объект->нэр; // Болд
// fetchAll() — бүх мөрийг нэг дор авна
$stmt = $pdo->query("SELECT * FROM хэрэглэгчид");
$бүгд = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo count($бүгд) . " хэрэглэгч байна.\n";
// fetchColumn() — нэг баганын утга
$тоо = $pdo->query("SELECT COUNT(*) FROM хэрэглэгчид")->fetchColumn();
echo "Нийт: {$тоо}\n";
?>
Дараагийн хичээлд:
PDO ашиглан бүрэн CRUD (Create, Read, Update, Delete) үйлдлүүдийг хэрэгжүүлнэ. Бодит жишээн дээр хэрэглэгчийн систем бүтээж, transaction хэрхэн ашиглахыг судална.