Kết nối MySQL

Sau khi nắm vững làm việc với file, bước tiếp theo là học cách kết nối MySQL trong PHP để làm việc với database. MySQL là hệ quản trị cơ sở dữ liệu phổ biến nhất cho PHP – từ website cá nhân đến hệ thống doanh nghiệp lớn. Bạn sẽ học cách kết nối, thực hiện truy vấn SELECT, INSERT, UPDATE, DELETE và bảo mật ứng dụng với Prepared Statements.

1. MySQL là gì?

MySQL là hệ quản trị cơ sở dữ liệu quan hệ (RDBMS) miễn phí, lưu trữ dữ liệu dạng bảng (tables) với hàng (rows) và cột (columns).

Tại sao dùng MySQL thay vì File?

  • Hiệu suất: Truy vấn nhanh với hàng triệu bản ghi
  • Bảo mật: Phân quyền người dùng, mã hóa dữ liệu
  • Quan hệ: Liên kết bảng (foreign key), JOIN
  • Đồng thời: Nhiều người dùng cùng lúc
  • Backup: Sao lưu, phục hồi dễ dàng
  • Tìm kiếm: WHERE, LIKE, ORDER BY, LIMIT…
LUỒNG KẾT NỐI PHP VÀ MYSQL PHP SERVER $conn = new PDO(); 1. Tạo kết nối 2. Gửi SQL query 3. Nhận kết quả 4. Hiển thị HTML SQL Query Result MYSQL DATABASE Database: test_db Tables: 📋 users 📋 products 📋 orders 💡 Host: localhost | Port: 3306 | User: root
Hình 1: Luồng kết nối PHP và MySQL

2. Cài đặt MySQL

2.1. XAMPP (Windows/Mac/Linux)

# XAMPP đã bao gồm MySQL
# Mở XAMPP Control Panel → Start Apache + MySQL

# Truy cập phpMyAdmin:
http://localhost/phpmyadmin

# Thông tin kết nối mặc định:
Host: localhost
Username: root
Password: (để trống)
Port: 3306

2.2. Tạo Database và Table

-- Tạo database
CREATE DATABASE test_db;

-- Sử dụng database
USE test_db;

-- Tạo bảng users
CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  email VARCHAR(100) UNIQUE NOT NULL,
  password VARCHAR(255) NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Insert dữ liệu mẫu
INSERT INTO users (name, email, password) VALUES
('Nguyễn Văn A', 'a@email.com', 'password123'),
('Trần Thị B', 'b@email.com', 'password456');

3. Hai cách kết nối: MySQLi vs PDO

MYSQLI vs PDO MYSQLI Kết nối: new mysqli($h,$u,$p,$db) Đặc điểm: ✅ Chỉ MySQL ✅ OOP + Procedural ✅ Prepared Statements ❌ Chỉ ? parameters ⚡ Nhanh hơn PDO 1 chút Dùng khi: 🔹 Dự án chỉ dùng MySQL 🔹 Dự án nhỏ/vừa 🔹 Cần tốc độ tối đa PDO Kết nối: new PDO($dsn,$u,$p) Đặc điểm: ✅ 12+ databases ✅ Chỉ OOP ✅ Prepared Statements ✅ ? và :named parameters ✅ Linh hoạt hơn Dùng khi: 🔹 Dự án có thể đổi DB 🔹 Dự án lớn/phức tạp 🔹 Khuyến nghị cho mới 💡 Khuyến nghị: Dùng PDO cho dự án mới vì linh hoạt và chuẩn hóa
Hình 2: So sánh MySQLi và PDO
Tiêu chíMySQLiPDO
DatabaseChỉ MySQL12+ databases
OOP/Procedural✅ Cả haiChỉ OOP
Prepared Statements✅ Có✅ Có
Named Parameters❌ Không✅ Có
Khuyến nghịDự án nhỏ/vừaDự án lớn/đa DB

💡 Khuyến nghị: Dùng PDO cho dự án mới vì linh hoạt hơn và hỗ trợ nhiều database.

4. Kết nối với MySQLi

4.1. Kết nối OOP (khuyến nghị)

<?php
// Thông tin kết nối
$host = "localhost";
$username = "root";
$password = "";
$database = "test_db";

// Tạo kết nối
$conn = new mysqli($host, $username, $password, $database);

// Kiểm tra kết nối
if ($conn->connect_error) {
  die("Kết nối thất bại: " . $conn->connect_error);
}

echo "Kết nối thành công!";

// Đóng kết nối
$conn->close();
?>

4.2. Kết nối Procedural

<?php
$conn = mysqli_connect("localhost", "root", "", "test_db");

if (!$conn) {
  die("Kết nối thất bại: " . mysqli_connect_error());
}

echo "Kết nối thành công!";

mysqli_close($conn);
?>

4.3. File cấu hình kết nối

<?php
// config.php
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_NAME', 'test_db');

$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);

if ($conn->connect_error) {
  die("Kết nối thất bại: " . $conn->connect_error);
}

// Đặt charset UTF-8 (quan trọng cho tiếng Việt)
$conn->set_charset("utf8mb4");
?>

5. Kết nối với PDO

<?php
$host = "localhost";
$dbname = "test_db";
$username = "root";
$password = "";

try {
  // Tạo kết nối PDO
  $pdo = new PDO(
    "mysql:host=$host;dbname=$dbname;charset=utf8mb4",
    $username,
    $password
  );
  
  // Đặt chế độ lỗi
  $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  
  // Đặt fetch mode mặc định
  $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
  
  echo "Kết nối PDO thành công!";
  
} catch (PDOException $e) {
  die("Kết nối thất bại: " . $e->getMessage());
}
?>

6. Truy vấn SELECT – Đọc dữ liệu

CÁC THAO TÁC SQL CƠ BẢN (CRUD) SELECT - ĐỌC DỮ LIỆU SELECT * FROM users WHERE id = ? → Lấy dữ liệu từ database INSERT - THÊM DỮ LIỆU INSERT INTO users (name, email) VALUES (?,?) → Thêm bản ghi mới UPDATE - SỬA DỮ LIỆU UPDATE users SET name = ? WHERE id = ? → Cập nhật bản ghi DELETE - XÓA DỮ LIỆU DELETE FROM users WHERE id = ? → Xóa bản ghi ⚠️ LUÔN DÙNG PREPARED STATEMENTS ĐỂ CHỐNG SQL INJECTION! $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
Hình 3: Các thao tác SQL cơ bản

6.1. SELECT với MySQLi

<?php
require 'config.php';

// Truy vấn SQL
$sql = "SELECT * FROM users";
$result = $conn->query($sql);

// Kiểm tra có dữ liệu không
if ($result->num_rows > 0) {
  // Duyệt từng dòng
  while ($row = $result->fetch_assoc()) {
    echo "ID: " . $row['id'] . "<br>";
    echo "Tên: " . $row['name'] . "<br>";
    echo "Email: " . $row['email'] . "<br><hr>";
  }
} else {
  echo "Không có dữ liệu";
}

$conn->close();
?>

6.2. SELECT với PDO

<?php
require 'config.php';

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

// Fetch tất cả
$users = $stmt->fetchAll();

foreach ($users as $user) {
  echo "Tên: " . $user['name'] . "<br>";
  echo "Email: " . $user['email'] . "<br><hr>";
}

// Hoặc fetch từng dòng
$stmt = $pdo->query($sql);
while ($user = $stmt->fetch()) {
  echo $user['name'] . "<br>";
}
?>

6.3. SELECT với điều kiện WHERE

<?php
// MySQLi
$sql = "SELECT * FROM users WHERE id = 1";
$result = $conn->query($sql);
$user = $result->fetch_assoc();

echo "Tên: " . $user['name'];

// PDO
$sql = "SELECT * FROM users WHERE id = 1";
$stmt = $pdo->query($sql);
$user = $stmt->fetch();

echo "Email: " . $user['email'];
?>

7. INSERT – Thêm dữ liệu

7.1. INSERT với MySQLi

<?php
require 'config.php';

$name = "Lê Văn C";
$email = "c@email.com";
$password = password_hash("password789", PASSWORD_DEFAULT);

$sql = "INSERT INTO users (name, email, password) 
        VALUES ('$name', '$email', '$password')";

if ($conn->query($sql) === TRUE) {
  echo "Thêm thành công! ID mới: " . $conn->insert_id;
} else {
  echo "Lỗi: " . $conn->error;
}

$conn->close();
?>

7.2. INSERT với PDO

<?php
$name = "Phạm Thị D";
$email = "d@email.com";
$password = password_hash("password000", PASSWORD_DEFAULT);

$sql = "INSERT INTO users (name, email, password) 
        VALUES ('$name', '$email', '$password')";

try {
  $pdo->exec($sql);
  echo "Thêm thành công! ID mới: " . $pdo->lastInsertId();
} catch (PDOException $e) {
  echo "Lỗi: " . $e->getMessage();
}
?>

⚠️ CẢNH BÁO: Code trên có lỗ hổng SQL Injection! Xem phần Prepared Statements bên dưới.

8. UPDATE – Cập nhật dữ liệu

<?php
// MySQLi
$sql = "UPDATE users SET name = 'Nguyễn Văn An' WHERE id = 1";

if ($conn->query($sql) === TRUE) {
  echo "Cập nhật thành công! Số dòng: " . $conn->affected_rows;
} else {
  echo "Lỗi: " . $conn->error;
}

// PDO
$sql = "UPDATE users SET email = 'newemail@email.com' WHERE id = 2";

try {
  $affected = $pdo->exec($sql);
  echo "Cập nhật $affected dòng";
} catch (PDOException $e) {
  echo "Lỗi: " . $e->getMessage();
}
?>

9. DELETE – Xóa dữ liệu

<?php
// MySQLi
$sql = "DELETE FROM users WHERE id = 3";

if ($conn->query($sql) === TRUE) {
  echo "Xóa thành công! Số dòng: " . $conn->affected_rows;
} else {
  echo "Lỗi: " . $conn->error;
}

// PDO
$sql = "DELETE FROM users WHERE id = 4";

try {
  $affected = $pdo->exec($sql);
  echo "Đã xóa $affected dòng";
} catch (PDOException $e) {
  echo "Lỗi: " . $e->getMessage();
}
?>

10. Prepared Statements – Bảo mật SQL Injection

SQL Injection là lỗ hổng bảo mật nghiêm trọng nhất trong web:

<?php
// ❌ NGUY HIỂM - SQL Injection
$id = $_GET['id'];  // Hacker nhập: 1 OR 1=1
$sql = "SELECT * FROM users WHERE id = $id";
// → SELECT * FROM users WHERE id = 1 OR 1=1 (lấy toàn bộ!)

// ❌ NGUY HIỂM
$email = $_POST['email'];  // Hacker nhập: ' OR '1'='1
$password = $_POST['password'];
$sql = "SELECT * FROM users WHERE email = '$email' AND password = '$password'";
// → Đăng nhập mà không cần mật khẩu!
?>

10.1. Prepared Statements với MySQLi

<?php
// SELECT
$id = $_GET['id'];

$stmt = $conn->prepare("SELECT * FROM users WHERE id = ?");
$stmt->bind_param("i", $id);  // i = integer, s = string
$stmt->execute();

$result = $stmt->get_result();
$user = $result->fetch_assoc();

echo "Tên: " . $user['name'];

$stmt->close();

// INSERT
$name = $_POST['name'];
$email = $_POST['email'];
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);

$stmt = $conn->prepare("INSERT INTO users (name, email, password) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $name, $email, $password);  // s = string

if ($stmt->execute()) {
  echo "Thêm thành công!";
} else {
  echo "Lỗi: " . $stmt->error;
}

$stmt->close();

// UPDATE
$new_name = $_POST['name'];
$id = $_POST['id'];

$stmt = $conn->prepare("UPDATE users SET name = ? WHERE id = ?");
$stmt->bind_param("si", $new_name, $id);
$stmt->execute();

echo "Cập nhật " . $stmt->affected_rows . " dòng";
$stmt->close();
?>

Các loại bind_param:

  • i – Integer (số nguyên)
  • d – Double (số thực)
  • s – String (chuỗi)
  • b – Blob (dữ liệu nhị phân)

10.2. Prepared Statements với PDO

<?php
// Cách 1: Positional parameters (?)
$id = $_GET['id'];

$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
$user = $stmt->fetch();

echo "Email: " . $user['email'];

// Cách 2: Named parameters (:name) - Khuyến nghị
$email = $_POST['email'];
$password = $_POST['password'];

$stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email AND password = :password");
$stmt->execute([
  ':email' => $email,
  ':password' => $password
]);

$user = $stmt->fetch();

if ($user) {
  echo "Đăng nhập thành công";
} else {
  echo "Sai email hoặc mật khẩu";
}

// INSERT
$stmt = $pdo->prepare("INSERT INTO users (name, email, password) VALUES (:name, :email, :password)");
$stmt->execute([
  ':name' => $_POST['name'],
  ':email' => $_POST['email'],
  ':password' => password_hash($_POST['password'], PASSWORD_DEFAULT)
]);

echo "Thêm thành công! ID: " . $pdo->lastInsertId();

// UPDATE
$stmt = $pdo->prepare("UPDATE users SET name = :name WHERE id = :id");
$stmt->execute([
  ':name' => $_POST['name'],
  ':id' => $_POST['id']
]);

echo "Cập nhật " . $stmt->rowCount() . " dòng";
?>

11. Ví dụ hoàn chỉnh: CRUD với PDO

11.1. Database.php – Class kết nối

<?php
class Database {
  private $host = "localhost";
  private $dbname = "test_db";
  private $username = "root";
  private $password = "";
  private $conn;
  
  public function connect() {
    $this->conn = null;
    
    try {
      $this->conn = new PDO(
        "mysql:host=$this->host;dbname=$this->dbname;charset=utf8mb4",
        $this->username,
        $this->password
      );
      
      $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
      $this->conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
      
    } catch (PDOException $e) {
      echo "Lỗi kết nối: " . $e->getMessage();
    }
    
    return $this->conn;
  }
}
?>

11.2. User.php – Class CRUD

<?php
require_once 'Database.php';

class User {
  private $conn;
  private $table = "users";
  
  public function __construct() {
    $db = new Database();
    $this->conn = $db->connect();
  }
  
  // Lấy tất cả users
  public function getAll() {
    $sql = "SELECT id, name, email, created_at FROM $this->table ORDER BY id DESC";
    $stmt = $this->conn->query($sql);
    return $stmt->fetchAll();
  }
  
  // Lấy 1 user theo ID
  public function getById($id) {
    $stmt = $this->conn->prepare("SELECT * FROM $this->table WHERE id = ?");
    $stmt->execute([$id]);
    return $stmt->fetch();
  }
  
  // Thêm user
  public function create($name, $email, $password) {
    $stmt = $this->conn->prepare(
      "INSERT INTO $this->table (name, email, password) VALUES (:name, :email, :password)"
    );
    
    return $stmt->execute([
      ':name' => $name,
      ':email' => $email,
      ':password' => password_hash($password, PASSWORD_DEFAULT)
    ]);
  }
  
  // Cập nhật user
  public function update($id, $name, $email) {
    $stmt = $this->conn->prepare(
      "UPDATE $this->table SET name = :name, email = :email WHERE id = :id"
    );
    
    return $stmt->execute([
      ':name' => $name,
      ':email' => $email,
      ':id' => $id
    ]);
  }
  
  // Xóa user
  public function delete($id) {
    $stmt = $this->conn->prepare("DELETE FROM $this->table WHERE id = ?");
    return $stmt->execute([$id]);
  }
  
  // Đăng nhập
  public function login($email, $password) {
    $stmt = $this->conn->prepare("SELECT * FROM $this->table WHERE email = ?");
    $stmt->execute([$email]);
    $user = $stmt->fetch();
    
    if ($user && password_verify($password, $user['password'])) {
      return $user;
    }
    
    return false;
  }
}
?>

11.3. index.php – Sử dụng

<?php
require_once 'User.php';

$userObj = new User();

// Lấy danh sách users
$users = $userObj->getAll();

echo "<h2>Danh sách Users</h2>";
echo "<table border='1'>";
echo "<tr><th>ID</th><th>Tên</th><th>Email</th><th>Ngày tạo</th></tr>";

foreach ($users as $user) {
  echo "<tr>";
  echo "<td>" . $user['id'] . "</td>";
  echo "<td>" . htmlspecialchars($user['name']) . "</td>";
  echo "<td>" . htmlspecialchars($user['email']) . "</td>";
  echo "<td>" . $user['created_at'] . "</td>";
  echo "</tr>";
}

echo "</table>";

// Thêm user mới
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['add'])) {
  $userObj->create($_POST['name'], $_POST['email'], $_POST['password']);
  header("Location: index.php");
  exit;
}

// Xóa user
if (isset($_GET['delete'])) {
  $userObj->delete($_GET['delete']);
  header("Location: index.php");
  exit;
}
?>

<h3>Thêm User</h3>
<form method="POST">
  <input type="text" name="name" placeholder="Tên" required><br>
  <input type="email" name="email" placeholder="Email" required><br>
  <input type="password" name="password" placeholder="Mật khẩu" required><br>
  <button type="submit" name="add">Thêm</button>
</form>

12. Xử lý lỗi và Transaction

12.1. Try-Catch với PDO

<?php
try {
  $pdo->beginTransaction();
  
  // Truy vấn 1
  $pdo->exec("INSERT INTO users (name, email, password) VALUES ('User 1', 'user1@test.com', 'pass')");
  
  // Truy vấn 2
  $pdo->exec("INSERT INTO orders (user_id, total) VALUES (1, 1000000)");
  
  // Commit nếu thành công
  $pdo->commit();
  echo "Transaction thành công";
  
} catch (PDOException $e) {
  // Rollback nếu có lỗi
  $pdo->rollBack();
  echo "Lỗi: " . $e->getMessage();
}
?>

Tóm tắt

Qua bài này bạn đã nắm được:

  • MySQL: Hệ quản trị CSDL quan hệ, lưu trữ dữ liệu dạng bảng
  • MySQLi: Extension chỉ cho MySQL, có OOP và Procedural
  • PDO: Hỗ trợ 12+ databases, chỉ OOP, linh hoạt hơn (khuyến nghị)
  • Kết nối: new mysqli() hoặc new PDO()
  • SELECT: query() hoặc prepare() + execute() + fetch()
  • INSERT: INSERT INTO table VALUES (...)
  • UPDATE: UPDATE table SET ... WHERE ...
  • DELETE: DELETE FROM table WHERE ...
  • Prepared Statements: Bảo vệ khỏi SQL Injection – BẮT BUỘC dùng!
  • Transaction: beginTransaction() + commit()/rollBack()

Bài tiếp theo, bạn sẽ học về Mini Project CRUD xây dựng ứng dụng quản lý hoàn chỉnh.

🎯 Bài tập thực hành

  1. Bài 1: Tạo database library với bảng books (id, title, author, year, created_at). Viết code hiển thị danh sách sách, thêm sách mới.
  2. Bài 2: Tạo form đăng ký/đăng nhập. Lưu user vào database với mật khẩu đã hash. Kiểm tra email trùng khi đăng ký.
  3. Bài 3: Tạo class Product với CRUD đầy đủ (getAll, getById, create, update, delete). Sử dụng PDO và Prepared Statements.

Gợi ý Bài 1:

<?php
// Kết nối
$pdo = new PDO("mysql:host=localhost;dbname=library;charset=utf8mb4", "root", "");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// Thêm sách
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
  $stmt = $pdo->prepare("INSERT INTO books (title, author, year) VALUES (?, ?, ?)");
  $stmt->execute([$_POST['title'], $_POST['author'], $_POST['year']]);
  header("Location: index.php");
  exit;
}

// Hiển thị
$books = $pdo->query("SELECT * FROM books ORDER BY id DESC")->fetchAll();
?>

<h2>Danh sách sách</h2>
<table border="1">
  <tr><th>ID</th><th>Tiêu đề</th><th>Tác giả</th><th>Năm</th></tr>
  <?php foreach ($books as $book): ?>
    <tr>
      <td><?= $book['id'] ?></td>
      <td><?= htmlspecialchars($book['title']) ?></td>
      <td><?= htmlspecialchars($book['author']) ?></td>
      <td><?= $book['year'] ?></td>
    </tr>
  <?php endforeach; ?>
</table>

<form method="POST">
  <input type="text" name="title" placeholder="Tiêu đề" required>
  <input type="text" name="author" placeholder="Tác giả" required>
  <input type="number" name="year" placeholder="Năm" required>
  <button type="submit">Thêm</button>
</form>

Gợi ý Bài 2:

<?php
session_start();
$pdo = new PDO("mysql:host=localhost;dbname=test_db;charset=utf8mb4", "root", "");

// Đăng ký
if (isset($_POST['register'])) {
  $email = $_POST['email'];
  
  // Kiểm tra email tồn tại
  $stmt = $pdo->prepare("SELECT id FROM users WHERE email = ?");
  $stmt->execute([$email]);
  
  if ($stmt->rowCount() > 0) {
    echo "Email đã tồn tại!";
  } else {
    $stmt = $pdo->prepare("INSERT INTO users (name, email, password) VALUES (?, ?, ?)");
    $stmt->execute([
      $_POST['name'],
      $email,
      password_hash($_POST['password'], PASSWORD_DEFAULT)
    ]);
    echo "Đăng ký thành công!";
  }
}

// Đăng nhập
if (isset($_POST['login'])) {
  $stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
  $stmt->execute([$_POST['email']]);
  $user = $stmt->fetch();
  
  if ($user && password_verify($_POST['password'], $user['password'])) {
    $_SESSION['user_id'] = $user['id'];
    echo "Đăng nhập thành công!";
  } else {
    echo "Sai email hoặc mật khẩu!";
  }
}
?>

Để lại bình luận

Email của bạn sẽ không được hiển thị.