Bỏ qua để đến nội dung

Lớp cơ sở dữ liệu

2.5.x ✅ 4.0.x ✅

Tìm hiểu về tính trừu tượng hóa cơ sở dữ liệu XOOPS, tính bền vững của đối tượng và xây dựng truy vấn.


Lớp cơ sở dữ liệu XOOPS cung cấp khả năng trừu tượng hóa mạnh mẽ trên MySQL/MariaDB, bao gồm:

  • Mẫu nhà máy - Quản lý kết nối cơ sở dữ liệu tập trung
  • Ánh xạ quan hệ đối tượng - XoopsObject và các trình xử lý
  • Xây dựng truy vấn - Hệ thống tiêu chí cho các truy vấn phức tạp
  • Tái sử dụng kết nối - Kết nối đơn thông qua nhà máy đơn lẻ (không gộp)

flowchart TB
subgraph App["📱 Application Code"]
AppCode["Your Module Code"]
end
subgraph Handler["🔧 XoopsPersistableObjectHandler"]
HandlerMethods["create() | get() | insert() | delete()<br/>getObjects() | getCount() | deleteAll()"]
end
subgraph Object["📦 XoopsObject"]
ObjectMethods["initVar() | getVar() | setVar() | toArray()"]
end
subgraph Criteria["🔍 Criteria System"]
CriteriaMethods["Criteria | CriteriaCompo | CriteriaElement"]
end
subgraph Database["🗄️ XoopsDatabase"]
DatabaseMethods["query() | queryF() | fetchArray() | insert()"]
end
subgraph Storage["💾 MySQL / MariaDB"]
DB[(Database)]
end
App --> Handler
Handler --> Object
Object --> Criteria
Criteria --> Database
Database --> Storage
style App fill:#e3f2fd,stroke:#1976d2
style Handler fill:#e8f5e9,stroke:#388e3c
style Object fill:#fff3e0,stroke:#f57c00
style Criteria fill:#f3e5f5,stroke:#7b1fa2
style Database fill:#fce4ec,stroke:#c2185b
style Storage fill:#eceff1,stroke:#546e7a

// Recommended: Use the global database instance
$db = \XoopsDatabaseFactory::getDatabaseConnection();
// Legacy: Global variable (still works)
global $xoopsDB;

Mẫu nhà máy đảm bảo một kết nối cơ sở dữ liệu duy nhất được sử dụng lại:

<?php
class XoopsDatabaseFactory
{
private static ?XoopsDatabase $instance = null;
public static function getDatabaseConnection(): XoopsDatabase
{
if (self::$instance === null) {
self::$instance = new XoopsMySQLDatabase();
}
return self::$instance;
}
}

class cơ sở cho tất cả các đối tượng dữ liệu trong XOOPS.

<?php
namespace XoopsModules\MyModule;
class Article extends \XoopsObject
{
public function __construct()
{
$this->initVar('article_id', \XOBJ_DTYPE_INT, null, false);
$this->initVar('category_id', \XOBJ_DTYPE_INT, 0, true);
$this->initVar('title', \XOBJ_DTYPE_TXTBOX, '', true, 255);
$this->initVar('content', \XOBJ_DTYPE_TXTAREA, '', false);
$this->initVar('author_id', \XOBJ_DTYPE_INT, 0, true);
$this->initVar('status', \XOBJ_DTYPE_TXTBOX, 'draft', true, 20);
$this->initVar('views', \XOBJ_DTYPE_INT, 0, false);
$this->initVar('created', \XOBJ_DTYPE_INT, time(), false);
$this->initVar('updated', \XOBJ_DTYPE_INT, 0, false);
}
}
Hằng sốLoạiMô tả
XOBJ_DTYPE_INTSố nguyênGiá trị số
XOBJ_DTYPE_TXTBOXChuỗiVăn bản ngắn (< 255 ký tự)
XOBJ_DTYPE_TXTAREAvăn bảnNội dung văn bản dài
XOBJ_DTYPE_EMAILEmailĐịa chỉ email
XOBJ_DTYPE_URLURLĐịa chỉ web
XOBJ_DTYPE_FLOATPhaoSố thập phân
XOBJ_DTYPE_ARRAYMảngMảng nối tiếp
XOBJ_DTYPE_OTHERHỗn hợpDữ liệu thô
// Create new object
$article = new Article();
// Set values
$article->setVar('title', 'My Article');
$article->setVar('content', 'Article content here...');
$article->setVar('category_id', 5);
$article->setVar('author_id', $xoopsUser->getVar('uid'));
// Get values
$title = $article->getVar('title'); // Raw value
$titleDisplay = $article->getVar('title', 'e'); // For editing (HTML entities)
$titleShow = $article->getVar('title', 's'); // For display (sanitized)
// Bulk assign from array
$article->assignVars([
'title' => 'New Title',
'status' => 'published'
]);
// Convert to array
$data = $article->toArray();

Trình xử lý class quản lý các hoạt động CRUD cho các phiên bản XoopsObject.

<?php
namespace XoopsModules\MyModule;
class ArticleHandler extends \XoopsPersistableObjectHandler
{
public function __construct(\XoopsDatabase $db = null)
{
parent::__construct(
$db,
'mymodule_articles', // Table name
Article::class, // Object class
'article_id', // Primary key
'title' // Identifier field
);
}
}
// Get handler instance
$articleHandler = xoops_getModuleHandler('article', 'mymodule');
// Create new object
$article = $articleHandler->create();
// Get by ID
$article = $articleHandler->get(123);
// Insert (create or update)
$success = $articleHandler->insert($article);
// Delete
$success = $articleHandler->delete($article);
// Get multiple objects
$articles = $articleHandler->getObjects($criteria);
// Get count
$count = $articleHandler->getCount($criteria);
// Get as array (key => value)
$list = $articleHandler->getList($criteria);
// Delete multiple
$deleted = $articleHandler->deleteAll($criteria);
<?php
namespace XoopsModules\MyModule;
class ArticleHandler extends \XoopsPersistableObjectHandler
{
// ... constructor
/**
* Get published articles
*/
public function getPublished(int $limit = 10, int $start = 0): array
{
$criteria = new \CriteriaCompo();
$criteria->add(new \Criteria('status', 'published'));
$criteria->setSort('created');
$criteria->setOrder('DESC');
$criteria->setLimit($limit);
$criteria->setStart($start);
return $this->getObjects($criteria);
}
/**
* Get articles by category
*/
public function getByCategory(int $categoryId, int $limit = 10): array
{
$criteria = new \CriteriaCompo();
$criteria->add(new \Criteria('category_id', $categoryId));
$criteria->add(new \Criteria('status', 'published'));
$criteria->setSort('created');
$criteria->setOrder('DESC');
$criteria->setLimit($limit);
return $this->getObjects($criteria);
}
/**
* Get articles by author
*/
public function getByAuthor(int $authorId): array
{
$criteria = new \Criteria('author_id', $authorId);
return $this->getObjects($criteria);
}
/**
* Increment view count
*/
public function incrementViews(int $articleId): bool
{
$sql = sprintf(
'UPDATE %s SET views = views + 1 WHERE article_id = %d',
$this->table,
$articleId
);
return $this->db->queryF($sql) !== false;
}
/**
* Get popular articles
*/
public function getPopular(int $limit = 5): array
{
$criteria = new \CriteriaCompo();
$criteria->add(new \Criteria('status', 'published'));
$criteria->setSort('views');
$criteria->setOrder('DESC');
$criteria->setLimit($limit);
return $this->getObjects($criteria);
}
}

Hệ thống Tiêu chí cung cấp một cách mạnh mẽ, hướng đối tượng để xây dựng các mệnh đề SQL WHERE.

// Simple equality
$criteria = new \Criteria('status', 'published');
// With operator
$criteria = new \Criteria('views', 100, '>=');
// Column comparison
$criteria = new \Criteria('updated', 'created', '>');
$criteria = new \CriteriaCompo();
// AND conditions (default)
$criteria->add(new \Criteria('status', 'published'));
$criteria->add(new \Criteria('category_id', 5));
// OR conditions
$criteria->add(new \Criteria('featured', 1), 'OR');
// Nested conditions
$subCriteria = new \CriteriaCompo();
$subCriteria->add(new \Criteria('author_id', 1));
$subCriteria->add(new \Criteria('author_id', 2), 'OR');
$criteria->add($subCriteria);
$criteria = new \CriteriaCompo();
$criteria->add(new \Criteria('status', 'published'));
// Sorting
$criteria->setSort('created');
$criteria->setOrder('DESC');
// Multiple sort fields
$criteria->setSort('category_id, created');
$criteria->setOrder('ASC, DESC');
// Pagination
$criteria->setLimit(10); // Items per page
$criteria->setStart(20); // Offset
// Group by
$criteria->setGroupby('category_id');
Nhà điều hànhVí dụĐầu ra SQL
=new Criteria('status', 'published')status = 'published'
!=new Criteria('status', 'draft', '!=')status != 'draft'
>new Criteria('views', 100, '>')views > 100
>=new Criteria('views', 100, '>=')views >= 100
<new Criteria('views', 100, '<')views < 100
<=new Criteria('views', 100, '<=')views <= 100
LIKEnew Criteria('title', '%php%', 'LIKE')title LIKE '%php%'
NOT LIKEnew Criteria('title', '%test%', 'NOT LIKE')title NOT LIKE '%test%'
INnew Criteria('id', '(1,2,3)', 'IN')id IN (1,2,3)
NOT INnew Criteria('id', '(1,2,3)', 'NOT IN')id NOT IN (1,2,3)
// Find published articles in specific categories,
// with search term in title, sorted by views
$criteria = new \CriteriaCompo();
// Status must be published
$criteria->add(new \Criteria('status', 'published'));
// In categories 1, 2, or 3
$criteria->add(new \Criteria('category_id', '(1, 2, 3)', 'IN'));
// Title contains search term
$searchTerm = '%' . $db->escape($searchQuery) . '%';
$criteria->add(new \Criteria('title', $searchTerm, 'LIKE'));
// Created in last 30 days
$thirtyDaysAgo = time() - (30 * 24 * 60 * 60);
$criteria->add(new \Criteria('created', $thirtyDaysAgo, '>='));
// Sort by views descending
$criteria->setSort('views');
$criteria->setOrder('DESC');
// Paginate
$criteria->setLimit(10);
$criteria->setStart($page * 10);
$articles = $articleHandler->getObjects($criteria);
$totalCount = $articleHandler->getCount($criteria);

Đối với các truy vấn phức tạp không thể thực hiện được bằng Tiêu chí, hãy sử dụng SQL trực tiếp.### Truy vấn an toàn (Đọc)

$db = \XoopsDatabaseFactory::getDatabaseConnection();
$sql = sprintf(
'SELECT a.*, c.category_name
FROM %s a
LEFT JOIN %s c ON a.category_id = c.category_id
WHERE a.status = %s
ORDER BY a.created DESC
LIMIT %d',
$db->prefix('mymodule_articles'),
$db->prefix('mymodule_categories'),
$db->quoteString('published'),
10
);
$result = $db->query($sql);
while ($row = $db->fetchArray($result)) {
// Process row
echo $row['title'];
}
// Insert
$sql = sprintf(
"INSERT INTO %s (title, content, created) VALUES (%s, %s, %d)",
$db->prefix('mymodule_articles'),
$db->quoteString($title),
$db->quoteString($content),
time()
);
$db->queryF($sql);
$newId = $db->getInsertId();
// Update
$sql = sprintf(
"UPDATE %s SET views = views + 1 WHERE article_id = %d",
$db->prefix('mymodule_articles'),
$articleId
);
$db->queryF($sql);
$affectedRows = $db->getAffectedRows();
// Delete
$sql = sprintf(
"DELETE FROM %s WHERE article_id = %d",
$db->prefix('mymodule_articles'),
$articleId
);
$db->queryF($sql);
// String escaping
$safeString = $db->quoteString($userInput);
// or
$safeString = $db->escape($userInput);
// Integer (no escaping needed, just cast)
$safeInt = (int) $userInput;

⚠️ Các phương pháp bảo mật tốt nhất

Phần tiêu đề “⚠️ Các phương pháp bảo mật tốt nhất”

Luôn thoát khỏi thao tác nhập của người dùng

Phần tiêu đề “Luôn thoát khỏi thao tác nhập của người dùng”
// NEVER do this
$sql = "SELECT * FROM articles WHERE title = '$_GET[title]'"; // SQL Injection!
// DO this
$title = $db->escape($_GET['title']);
$sql = "SELECT * FROM articles WHERE title = '$title'";
// Or better, use Criteria
$criteria = new \Criteria('title', $db->escape($_GET['title']));

Sử dụng truy vấn được tham số hóa (XMF)

Phần tiêu đề “Sử dụng truy vấn được tham số hóa (XMF)”
use Xmf\Database\TableLoad;
// Safe bulk insert
$tableLoad = new TableLoad('mymodule_articles');
$tableLoad->insert([
['title' => 'Article 1', 'content' => 'Content 1'],
['title' => 'Article 2', 'content' => 'Content 2'],
]);
use Xmf\Request;
$id = Request::getInt('id', 0, 'GET');
$title = Request::getString('title', '', 'POST');

📊 Ví dụ về lược đồ cơ sở dữ liệu

Phần tiêu đề “📊 Ví dụ về lược đồ cơ sở dữ liệu”
-- sql/mysql.sql
CREATE TABLE `{PREFIX}_mymodule_articles` (
`article_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`category_id` INT(11) UNSIGNED NOT NULL DEFAULT 0,
`title` VARCHAR(255) NOT NULL DEFAULT '',
`content` TEXT,
`author_id` INT(11) UNSIGNED NOT NULL DEFAULT 0,
`status` VARCHAR(20) NOT NULL DEFAULT 'draft',
`views` INT(11) UNSIGNED NOT NULL DEFAULT 0,
`created` INT(11) UNSIGNED NOT NULL DEFAULT 0,
`updated` INT(11) UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`article_id`),
KEY `category_id` (`category_id`),
KEY `author_id` (`author_id`),
KEY `status` (`status`),
KEY `created` (`created`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


#xoops #database #orm #criteria #handlers #mysql