timefrontiers/php-database-object
最新稳定版本:1.0.2
Composer 安装命令:
composer require timefrontiers/php-database-object
包简介
SQL Database Object trait for Active Record pattern
README 文档
README
Database Object trait for Active Record pattern with query builder support.
Installation
composer require timefrontiers/php-database-object
Requirements
- PHP 8.1+
timefrontiers/php-sql-database^1.0timefrontiers/php-has-errors^1.0
Quick Start
use TimeFrontiers\Helper\DatabaseObject; use TimeFrontiers\Helper\HasErrors; class User { use DatabaseObject, HasErrors; protected static string $_db_name = 'myapp'; protected static string $_table_name = 'users'; protected static string $_primary_key = 'id'; protected static array $_db_fields = []; // Auto-loaded from schema public int $id; public string $name; public string $email; public string $status = 'active'; protected ?string $_created = null; protected ?string $_updated = null; protected ?string $_author = null; } // Find by ID $user = User::findById(123); // Query builder $activeUsers = User::query() ->where('status', 'active') ->orderBy('name') ->limit(10) ->get(); // Create $user = new User(); $user->name = 'John Doe'; $user->email = 'john@example.com'; $user->save(); // Update $user->name = 'Jane Doe'; $user->save(); // Delete $user->delete();
Connection Management
Three levels of connection resolution:
// 1. Instance-level (highest priority) $user = new User(); $user->setConnection($conn); // 2. Class-level User::useConnection($conn); // 3. Global fallback (lowest priority) global $database; $database = new SQLDatabase(...);
Required Static Properties
class MyEntity { use DatabaseObject; // Required protected static string $_db_name = 'database_name'; protected static string $_table_name = 'table_name'; protected static string $_primary_key = 'id'; // Optional (auto-loaded from INFORMATION_SCHEMA if empty) protected static array $_db_fields = []; }
Query Builder
Basic Queries
// Find all $users = User::findAll(); // Find by ID $user = User::findById(123); // Count $count = User::countAll(); // Check existence $exists = User::valueExists('email', 'john@example.com');
Fluent Queries
// WHERE conditions User::query() ->where('status', 'active') // status = 'active' ->where('age', '>', 18) // age > 18 ->where('role', '!=', 'admin') // role != 'admin' ->get(); // OR conditions User::query() ->where('status', 'active') ->orWhere('role', 'admin') ->get(); // IN / NOT IN User::query() ->whereIn('status', ['active', 'pending']) ->whereNotIn('role', ['banned', 'suspended']) ->get(); // NULL checks User::query() ->whereNull('deleted_at') ->whereNotNull('verified_at') ->get(); // Ordering User::query() ->orderBy('name') ->orderByDesc('created_at') ->get(); // Pagination User::query() ->limit(10) ->offset(20) ->get(); // Or use take() User::query()->take(10, 20)->get(); // First result $user = User::query() ->where('email', 'john@example.com') ->first(); // Count matching $count = User::query() ->where('status', 'active') ->count(); // Check existence $exists = User::query() ->where('email', 'john@example.com') ->exists();
Custom SQL
Use placeholders for database/table names:
$users = User::findBySql( "SELECT * FROM :database:.:table: WHERE status = ? AND created_at > ? ORDER BY :primary_key: DESC", ['active', '2024-01-01'] );
| Placeholder | Replaced With |
|---|---|
:database: or :db: |
$_db_name |
:table: or :tbl: |
$_table_name |
:primary_key: or :pkey: |
$_primary_key |
CRUD Operations
Create
$user = new User(); $user->name = 'John Doe'; $user->email = 'john@example.com'; if ($user->save()) { echo "Created with ID: {$user->id}"; } else { $errors = $user->getErrors(); }
Update
$user = User::findById(123); $user->name = 'Jane Doe'; if (!$user->save()) { $errors = $user->getErrors(); }
Delete
$user = User::findById(123); if (!$user->delete()) { $errors = $user->getErrors(); }
Timestamps & Author
The trait automatically handles these fields if they exist:
| Property | Behavior |
|---|---|
$_created |
Set to current datetime on insert |
$_updated |
Set to current datetime on insert/update |
$_author |
Set from $session->name on insert |
class Post { use DatabaseObject; protected ?string $_created = null; protected ?string $_updated = null; protected ?string $_author = null; // Accessors public function created():?string { ... } // Built-in public function updated():?string { ... } // Built-in public function author():?string { ... } // Built-in } $post = new Post(); $post->title = 'Hello'; $post->save(); echo $post->created(); // "2024-01-15 10:30:00" echo $post->author(); // "john_doe" (from $session->name)
Empty Properties
By default, empty values are skipped during save. Use $empty_props to allow specific fields to be empty:
class Article { use DatabaseObject; public array $empty_props = ['subtitle', 'meta_description']; public string $title; public string $subtitle = ''; // Can be saved as empty string public ?string $meta_description = null; // Can be saved as NULL }
Schema Caching
Field information is cached from INFORMATION_SCHEMA to avoid repeated queries:
use TimeFrontiers\Database\Schema\TableSchema; // Clear cache for a specific table TableSchema::clearCache('myapp', 'users'); // Clear cache for entire database TableSchema::clearCache('myapp'); // Clear all cached schemas TableSchema::clearCache();
Error Handling
The trait uses HasErrors for error management:
$user = new User(); $user->email = 'invalid'; if (!$user->save()) { // Get all errors $errors = $user->getErrors(); // Check specific context if ($user->hasErrors('_create')) { // Handle creation errors } // Get first error message $message = $user->firstError('_create'); // Use with InstanceError for rank-based filtering $visibleErrors = (new InstanceError($user))->get('_create'); }
Error Contexts
| Context | Triggered By |
|---|---|
_create |
Insert failures |
_update |
Update failures |
_delete |
Delete failures |
Static Accessors
User::primaryKey(); // "id" User::tableName(); // "users" User::databaseName(); // "myapp" User::tableFields(); // ["id", "name", "email", ...]
Complete Example
use TimeFrontiers\Helper\DatabaseObject; use TimeFrontiers\Helper\HasErrors; use TimeFrontiers\SQLDatabase; class Product { use DatabaseObject, HasErrors; protected static string $_db_name = 'store'; protected static string $_table_name = 'products'; protected static string $_primary_key = 'id'; protected static array $_db_fields = []; public int $id; public string $name; public string $sku; public float $price; public int $stock = 0; public string $status = 'draft'; protected ?string $_created = null; protected ?string $_updated = null; protected ?string $_author = null; public array $empty_props = ['description']; public ?string $description = null; /** * Publish the product. */ public function publish():bool { if ($this->stock <= 0) { $this->_userError('publish', 'Cannot publish product with no stock'); return false; } $this->status = 'published'; return $this->save(); } /** * Find products low on stock. */ public static function findLowStock(int $threshold = 10):array { return static::query() ->where('status', 'published') ->where('stock', '<=', $threshold) ->orderBy('stock') ->get(); } } // Usage Product::useConnection($database); // Create $product = new Product(); $product->name = 'Widget'; $product->sku = 'WDG-001'; $product->price = 29.99; $product->stock = 100; if ($product->save()) { $product->publish(); } // Query $lowStock = Product::findLowStock(5); foreach ($lowStock as $item) { echo "{$item->name}: {$item->stock} remaining\n"; }
License
MIT License
统计信息
- 总下载量: 11
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 1
- 依赖项目数: 4
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-04-15