為何選擇 Medoo?
- 極輕量:單檔 < 100KB,零依賴,Composer 安裝即用。
- 直觀語法:陣列式查詢建構器,SQL 結構一目瞭然。
- 多資料庫支援:MySQL、PostgreSQL、SQLite、MSSQL、Oracle、MariaDB 等。
- 安全預設:內建參數綁定,防止 SQL 注入。
- 效能優秀:無 ORM 開銷,直接生成優化 SQL。
安裝與初始化
composer require catfan/medoo
<?php
require 'vendor/autoload.php';
use Medoo\Medoo;
$database = new Medoo([
'database_type' => 'mysql',
'database_name' => 'my_app',
'server' => '127.0.0.1',
'username' => 'db_user',
'password' => 'db_pass',
'charset' => 'utf8mb4',
'port' => 3306,
// 選項
'option' => [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false, // 真正的參數化查詢
],
// 除錯模式 (開發環境)
'logging' => true,
'logger' => function($sql, $params) {
error_log('[Medoo] ' . $sql . ' | Params: ' . json_encode($params));
},
]);
CRUD 核心操作範例
🟢 Create - 新增資料
// 單筆新增,回傳自增 ID
$userId = $database->insert('account', [
'user_name' => 'john_doe',
'email' => 'john@example.com',
'age' => 28,
'lang' => ['en', 'zh-TW', 'ja'], // JSON 欄位自動編碼
'created_at' => Medoo::raw('NOW()'), // 原生 SQL 函式
]);
echo "新增使用者 ID: $userId\n";
// 批次新增 (單一 INSERT 多 VALUES)
$database->insert('account', [
['user_name' => 'alice', 'email' => 'alice@example.com', 'age' => 24],
['user_name' => 'bob', 'email' => 'bob@example.com', 'age' => 30],
['user_name' => 'charlie','email' => 'charlie@example.com','age' => 27],
]);
echo "批次新增完成\n";
🔵 Read - 查詢資料
// 1. 單筆查詢 (依主鍵)
$user = $database->get('account', '*', ['user_id' => 100]);
// SELECT * FROM account WHERE user_id = 100 LIMIT 1
// 2. 條件查詢 (多條件、運算子、IN、BETWEEN、LIKE)
$users = $database->select('account', [
'user_id', 'user_name', 'email', 'age'
], [
'age[>]' => 18,
'age[<]' => 65,
'status' => 'active',
'role[IN]' => ['admin', 'editor', 'author'],
'email[~]' => '%@company.com', // LIKE
'created_at[<>]' => ['2023-01-01', '2023-12-31'], // BETWEEN
'LIMIT' => 20,
'ORDER' => ['age' => 'DESC', 'user_id' => 'ASC'],
'GROUP' => 'role',
'HAVING' => ['COUNT(user_id)[>]' => 5],
]);
// 產生 SQL 含參數綁定,完全安全
// 3. 關聯查詢 (JOIN)
$posts = $database->select('post', [
'[>]account' => ['author_id' => 'user_id'], // LEFT JOIN
], [
'post.post_id',
'post.title',
'post.content',
'account.user_name',
'account.email',
], [
'post.status' => 'published',
'ORDER' => 'post.created_at DESC',
'LIMIT' => 10,
]);
// 4. 計數/聚合
$totalUsers = $database->count('account', ['status' => 'active']);
$avgAge = $database->avg('account', 'age', ['status' => 'active']);
$maxAge = $database->max('account', 'age');
$minAge = $database->min('account', 'age');
$sumAge = $database->sum('account', 'age');
🟡 Update - 更新資料
// 1. 基本更新
$database->update('account', [
'last_login' => Medoo::raw('NOW()'),
'login_count[+]' => 1, // 原子遞增
], [
'user_id' => 100,
]);
// 2. 複雜條件更新
$database->update('account', [
'status' => 'inactive',
'level[-]' => 5, // 原子遞減
], [
'AND' => [
'last_login[<]' => '2023-01-01',
'status' => 'active',
'role[!]' => 'admin', // 不等於
],
]);
// 3. JSON 欄位部分更新 (MySQL 5.7+/PostgreSQL)
$database->update('account', [
'preferences[theme]' => 'dark',
'preferences[notifications][email]' => false,
], ['user_id' => 100]);
🔴 Delete - 刪除資料
// 1. 單筆刪除
$database->delete('account', ['user_id' => 999]);
// 2. 條件刪除
$database->delete('account', [
'AND' => [
'status' => 'banned',
'created_at[<]' => '2020-01-01',
],
]);
// 3. 清空資料表 (TRUNCATE)
$database->delete('account', []); // 會產生 TRUNCATE TABLE account
進階查詢技巧
子查詢與原生 SQL
// WHERE IN 子查詢
$activeAdmins = $database->select('account', 'user_id', ['role' => 'admin', 'status' => 'active']);
$database->select('post', '*', [
'author_id' => $activeAdmins, // 自動轉為 IN (...)
]);
// 原生 SQL 片段 (需極度小心注入)
$database->select('account', '*', [
'Medoo::raw("age > (SELECT AVG(age) FROM account)")' => null,
]);
交易控制
$database->action(function($db) {
$db->insert('order', ['user_id' => 1, 'total' => 100]);
$orderId = $db->id();
$db->insert('order_item', [
['order_id' => $orderId, 'product_id' => 1, 'qty' => 2],
['order_id' => $orderId, 'product_id' => 3, 'qty' => 1],
]);
$db->update('product', ['stock[-]' => 2], ['product_id' => 1]);
$db->update('product', ['stock[-]' => 1], ['product_id' => 3]);
// 任何例外自動 Rollback
});
除錯與效能分析
// 取得最後執行的 SQL (含參數)
echo $database->last_query(); // 參數已綁定,非完整 SQL
// 取得 PDOStatement 物件進一步檢查
$stmt = $database->pdo()->query('EXPLAIN SELECT * FROM account WHERE email = ?', ['test@example.com']);
print_r($stmt->fetchAll());
常見操作對照表
| 需求 | Medoo 方法 | 關鍵語法 |
| 單筆新增 | insert() | 回傳 ID,支援 Medoo::raw() |
| 批次新增 | insert() | 二維陣列,單一 SQL |
| 單筆查詢 | get() | 自動 LIMIT 1 |
| 多筆查詢 | select() | 支援 JOIN、GROUP、HAVING |
| 計數 | count() | 回傳整數 |
| 聚合 | sum/avg/min/max() | 欄位名 + 條件 |
| 更新 | update() | [+] [-] 原子運算 |
| 刪除 | delete() | 空條件 = TRUNCATE |
| 交易 | action() | Closure 自動 Commit/Rollback |
遷移建議
- 從 PDO 原生:逐步替換,Medoo 可共用既有 PDO 實例
new Medoo(['pdo' => $existingPdo])。
- 從 Eloquent/Doctrine:Medoo 非 ORM,無實體映射、關聯載入、事件系統;適合高效能、低複雜度查詢場景。
- 大型專案:建議核心業務用 ORM,報表/統計/高頻 API 用 Medoo 混合架構。