codesaur/dataobject
PDO суурьтай өгөгдлийн модель ба хүснэгтүүдийг удирдагч компонент (MySQL / PostgreSQL / SQLite, PHP 8.2.1+)
codesaur/dataobject нь codesaur-php экосистемийн өгөгдлийн давхаргын үндсэн компонент.
Энгийн PDO-г шууд ашиглахын оронд:
- хүснэгтүүдийн бүтцийг PHP класс дотор Column-оор тодорхойлж,
- хүснэгтийг анх удаа ажиллах үедээ автоматаар үүсгэж,
- MySQL / PostgreSQL / SQLite бүгд дээр адилхан кодоор ажиллах боломжийг олгодог.
Гол санаа нь:
"Schema + CRUD логикоороо давтагдсан бүх кодуудыг дахин ашиглагдах Model / LocalizedModel дотор нуух"
Шаардлага
- PHP 8.2.1+ (
ext-pdoөргөтгөл шаардлагатай) - Composer
- MySQL эсвэл PostgreSQL эсвэл SQLite
Суурилуулалт
composer require codesaur/dataobject
Тест ажиллуулах
Composer Test Command-ууд
# Бүх тест ажиллуулах (Unit + Integration тестүүд)
composer test
# HTML coverage report үүсгэх (coverage/ directory дотор)
composer test-coverage
Command-уудын тайлбар
composer test- Бүх тестүүдийг (Unit болон Integration) ажиллуулна
- PHPUnit ашиглан тестлэх
- Тестүүдийн үр дүнг terminal дээр харуулна
- Амжилттай/Амжилтгүй статусыг буцаана
composer test-coverage- Бүх тестүүдийг ажиллуулна + code coverage report үүсгэнэ
- HTML форматтай coverage report үүсгэнэ (
coverage/directory) - Браузер дээр
coverage/index.htmlфайлыг нээж харж болно - Мөр, функц, класс тус бүрийн coverage хувийг харуулна
Тестүүдийн мэдээлэл
- Unit Tests: Column, PDOTrait, TableTrait, Model классуудын тест
- Integration Tests: LocalizedModel-ийн бүрэн тест
- Нийт: 106 тест, 277 assertion
PHPUnit шууд ашиглах
Composer command-уудын оронд PHPUnit-ийг шууд ажиллуулж болно:
# Бүх тест ажиллуулах
vendor/bin/phpunit
# Зөвхөн Unit тестүүд
vendor/bin/phpunit tests/Unit
# Зөвхөн Integration тестүүд
vendor/bin/phpunit tests/Integration
# Coverage report (Clover XML формат)
vendor/bin/phpunit --coverage-clover coverage/clover.xml
# Coverage report (HTML формат)
vendor/bin/phpunit --coverage-html coverage
Windows хэрэглэгчид: vendor/bin/phpunit-ийг vendor\bin\phpunit.bat-аар солино уу
Гол классууд
Constants
Кодбааз даяар ашиглагдах бүх тогтмол утгуудыг төвлөрүүлсэн класс:
- Driver нэрс:
DRIVER_MYSQL,DRIVER_PGSQL,DRIVER_SQLITE - Error кодууд:
ERR_TABLE_NAME_MISSING,ERR_COLUMNS_NOT_DEFINED,ERR_COLUMN_NOT_FOUND - Баганын нэрс:
COL_ID,COL_IS_ACTIVE,COL_PARENT_ID,COL_CODE - Localized model:
CONTENT_TABLE_SUFFIX,CONTENT_KEY_COLUMNS,LOCALIZED_KEY,PRIMARY_ALIAS_PREFIX,CONTENT_ALIAS_PREFIX,DEFAULT_CODE_LENGTH - Тохиргоо:
TABLE_NAME_PATTERN,MYSQL_ENGINE
use codesaur\DataObject\Constants;
// Driver шалгалт
if ($driver == Constants::DRIVER_MYSQL) { ... }
// Баганын нэр ашиглах
$this->hasColumn(Constants::COL_ID);
// Content хүснэгтийн нэр
$contentTable = $tableName . Constants::CONTENT_TABLE_SUFFIX;
Column
Нэг баганын мета мэдээлэл:
- нэр (
name) - төрөл (
type- int, varchar, datetime, ...) - урт (
length) - NULL / NOT NULL
- PRIMARY / UNIQUE / AUTO_INCREMENT
- анхны утга (
default)
use codesaur\DataObject\Column;
$columns = [
(new Column('id', 'bigint'))->primary(),
(new Column('username', 'varchar', 65))->unique(),
new Column('password', 'varchar', 255),
(new Column('is_active', 'tinyint'))->default(1),
];
Model
Энгийн (non-localized) хүснэгтийн суурь класс.
-Хүснэгтийн нэр, баганыг setTable() / setColumns()
-CRUD: insert(), updateById(), getRow(), getRows(), getRowWhere()
-getById(), existsById(), countRows()
-deleteById(), deactivateById()
-MySQL / PostgreSQL / SQLite ялгааг автоматаар зохицуулна
use codesaur\DataObject\Model;
use codesaur\DataObject\Column;
class UserModel extends Model
{
public function __construct(\PDO $pdo)
{
$this->setInstance($pdo);
$this->setColumns([
(new Column('id', 'bigint'))->primary(),
(new Column('username', 'varchar', 64))->unique(),
new Column('password', 'varchar', 255),
(new Column('is_active', 'tinyint'))->default(1),
new Column('created_at', 'datetime'),
]);
$this->setTable('users');
}
protected function __initial()
{
// Хүснэгт анх удаа үүсгэгдэх үед ганц удаа ажиллана
}
// Жишээ: хэрэглэгч нэмэх
public function createUser(string $username, string $hashedPassword): array
{
return $this->insert([
'username' => $username,
'password' => $hashedPassword,
'created_at' => date('Y-m-d H:i:s'),
]);
}
// Жишээ: хэрэглэгчийн идэвхжил шинэчлэх
public function setActive(int $id, bool $active): array
{
return $this->updateById($id, [
'is_active' => $active ? 1 : 0,
]);
}
}
LocalizedModel
Олон хэл дээр контент хадгалах шаардлагатай хүснэгтэд зориулагдсан суурь класс.
Архитектур:
-PRIMARY хүснэгт: tablename
-CONTENT хүснэгт: tablename_content
CONTENT хүснэгт дотор:
parent_id-> FK -> primary.id (CASCADE шинэчлэлт)code-> хэлний код (mn, en, jp ...)- бусад талбарууд (
title,description, ...)
Үндсэн функцүүд:
-CRUD: insert($record, $content), updateById($id, $record, $content)
-Унших: getById($id), existsById($id), countRows($condition), getRow($condition), getRows($condition), getRowWhere($values), getRowsByCode($code, $condition)
-MySQL / PostgreSQL / SQLite ялгааг автоматаар зохицуулна
Буцаах утгын бүтэц:
getRow(), getRows(), insert(), updateById() функцүүд дараах бүтэцтэй массив буцаана:
[
'id' => 1,
'slug' => 'article-slug',
'is_active' => 1,
'localized' => [
'en' => [
'title' => 'English Title',
'body' => 'English content...'
],
'mn' => [
'title' => 'Монгол гарчиг',
'body' => 'Монгол агуулга...'
]
]
]
getRowsByCode('en', $condition) функц олон мөрийг зөвхөн тухайн хэлний кодоор буцаана (хэлний кодын түвшин байхгүй):
[
1 => [
'id' => 1,
'slug' => 'article-slug-1',
'is_active' => 1,
'localized' => [
'title' => 'English Title 1',
'body' => 'English content 1...'
]
],
2 => [
'id' => 2,
'slug' => 'article-slug-2',
'is_active' => 1,
'localized' => [
'title' => 'English Title 2',
'body' => 'English content 2...'
]
]
]
use codesaur\DataObject\LocalizedModel;
use codesaur\DataObject\Column;
class ArticleModel extends LocalizedModel
{
public function __construct(\PDO $pdo)
{
$this->setInstance($pdo);
$this->setColumns([
(new Column('id', 'bigint'))->primary(),
(new Column('slug', 'varchar', 128))->unique(),
(new Column('is_active', 'tinyint'))->default(1),
new Column('created_at', 'datetime'),
]);
$this->setContentColumns([
new Column('title', 'varchar', 255),
new Column('body', 'text'),
]);
$this->setTable('article');
}
// Жишээ: нийтлэл нэмэх (primary + localized)
public function createArticle(string $slug, array $content): array
{
return $this->insert(
[
'slug' => $slug,
'created_at' => date('Y-m-d H:i:s'),
],
$content // ['en' => ['title' => '...', 'body' => '...'], 'mn' => [...]]
);
}
// Жишээ: нийтлэлийн контентыг шинэчлэх
public function updateArticle(int $id, array $content, array $record = []): array
{
return $this->updateById($id, $record, $content);
}
// Жишээ: тодорхой хэлээр нийтлэл авах
public function getArticleByLang(int $id, string $lang): array|null
{
$rows = $this->getRowsByCode($lang, ['WHERE' => "p.id=$id"]);
return $rows[$id] ?? null;
}
// Жишээ: тодорхой хэлээр бүх нийтлэлүүд авах
public function getAllArticlesByLang(string $lang, bool $activeOnly = true): array
{
$condition = ['ORDER BY' => 'p.created_at DESC'];
if ($activeOnly) {
$condition['WHERE'] = 'p.is_active=1';
}
return $this->getRowsByCode($lang, $condition);
}
// Жишээ: slug болон хэлээр нийтлэл авах (WHERE + PARAM ашиглах)
public function getArticleBySlugAndLang(string $slug, string $lang): array|null
{
$rows = $this->getRowsByCode($lang, [
'WHERE' => 'p.slug=:slug AND p.is_active=1',
'PARAM' => [':slug' => $slug]
]);
return !empty($rows) ? \reset($rows) : null;
}
}
PDOTrait
PDOTrait нь бодит PDO instance-тэй ажиллах бүх бага түвшний логикийг нэг дор төвлөрүүлсэн trait юм.
Үндсэн боломжууд:
setInstance(PDO $pdo)- гаднаас PDO-г суулгаж өгнөgetDriverName()-mysql,pgsql,sqliteгэх мэт драйверийн нэрийг кештэйгээр буцаанаquote(),prepare(),exec(),query()- PDO-гийн үндсэн функцуудын safe wrapperprepare()/query()ньfalseбуцсан үед алдааны Exception шиднэ
hasTable($name)- хүснэгт байгаа эсэхийг MySQL / PostgreSQL / SQLite дээр тус бүр өөр SQL-аар шалгана
Ингэснээр дээр нь суугаа Model / LocalizedModel нь PDO код биш, зөвхөн бизнесс логик-оо мэддэг болдог.
TableTrait
TableTrait нь PDOTrait-ыг ашиглан schema-level ажиллагааг хариуцдаг:
- хүснэгтийн нэр (
$name) - багануудын тодорхойлолт (
$columns) - хүснэгт үүсгэх / шалгах / анхны өгөгдлөөр populate хийх
Гол үүргүүд:
setColumns(array $columns)-Columnмассивыг нэрээр нь индексжүүлж хадгалнаsetTable(string $name)- хүснэгтийн нэрийг цэвэрлэнэ (
A-z 0-9 _-ашиглана) - баганууд зөв тодорхойлогдсон эсэхийг шалгана
- хүснэгт байхгүй бол ->
createTable()дуудаж автоматаар үүсгэнэ - дараа нь моделийн
__initial()-ийг ганц удаа ажиллуулна
- хүснэгтийн нэрийг цэвэрлэнэ (
getColumns()/getColumn($name)/hasColumn($name)- schema introspectiondeleteById($id)- primary key ашиглан мөр устганаdeactivateById($id, array $record = [])is_activeбаганад0онооно- UNIQUE баганууд өөрчлөгдөхгүй
selectStatement($fromTable, $selection='*', array $condition=[])- JOIN / WHERE / GROUP BY / ORDER / LIMIT / OFFSET бүхнийг
['INNER JOIN' => '...', 'WHERE' => '...', 'PARAM' => [...]]хэлбэрээр өгч, динамик SELECT үүсгэх боломж олгоно
- JOIN / WHERE / GROUP BY / ORDER / LIMIT / OFFSET бүхнийг
createTable($table, array $columns)/getSyntax(Column $column)- MySQL / PostgreSQL / SQLite type mapping
serial,bigserial,timestamptz,tinyintvssmallint, ...
- PRIMARY, UNIQUE, AUTO_INCREMENT, DEFAULT, NULL/NOT NULL бүгдийг цэвэр SQL болгон автоматаар угсарна
- MySQL / PostgreSQL / SQLite type mapping
Эцэст нь, Model / LocalizedModel нь "зөвхөн баганаа зарлаад, setTable() дуудахад" хүснэгт нь өөрөө үүсдэг.
Example Runner UI
Жишээ код моделиуд Example хавтсан дотор бүрэн эхээрээ орсон.
example/index.php- MySQL/PostgreSQL/SQLite сонгох UI
Тест ба CI/CD
Project нь бүрэн тестжүүлсэн:
-PHPUnit - Unit болон Integration тестүүд -GitHub Actions - Автомат CI/CD pipeline
- Push болон Pull Request үед автоматаар ажиллана
main,master,developbranch-ууд дээр trigger болно -Code Coverage - HTML coverage report:coverage/directory -Multi-version - PHP 8.2, 8.3 дээр тестлэгдсэн -Multi-OS - Ubuntu, Windows дээр тестлэгдсэн -Database Extensions - PDO, PDO_SQLite, PDO_MySQL, PDO_PgSQL суурилуулна
CI/CD workflow файл: .github/workflows/ci.yml
Нэмэлт бичиг баримтууд
- API - Бүрэн API баримт бичиг (PHPDoc-уудаас Cursor AI ашиглан автоматаар үүсгэсэн)
- REVIEW - Код шалгалтын үр дүн, олсон асуудлууд, сайжруулалтын саналууд (Cursor AI ашиглан үүсгэсэн)
- CHANGELOG - Пакетийн бүх хувилбаруудын өөрчлөлтийн түүх
Лиценз
Энэ төсөл MIT лицензтэй.
Зохиогч
Narankhuu
https://github.com/codesaur
Дүгнэлт
codesaur/dataobject бол:
- Хүснэгт, баганыг PHP кодоор тодорхойлдог
- MySQL / PostgreSQL / SQLite бүгд дээр адилхан кодоор ажилладаг
- CRUD болон schema initialization-ийг автоматаар шийдсэн
- Өгөгдлийн давхаргыг цэвэр, загварлаг болгох
- Хөнгөн, уян хатан, өргөтгөхөд хялбар компонент
PHP төсөлдөө стандартчилсан, дахин ашиглагдах, цэвэр өгөгдлийн модель ашиглахыг хүсвэл хамгийн зөв сонголт юм!