內容目录
上一个主题下一个主题 |
数据库抽象层(Database Abstraction Layer)¶Phalcon\Db 是 Phalcon\Mvc\Model 背后的一个组件,它为框架提供了强大的model层。它是一个完全由C语言写的独立的高级抽象层的数据库系统。 这个组件提供了比传统模式的更容易上手的数据库操作。
数据库适配器(Database Adapters)¶这个组件利用了这些适配器去封装特定的数据库的详细操作。Phalcon使用 PDO 去连接这些数据库。下面这些是我们支持的数据库引擎:
自定义适配器(Implementing your own adapters)¶如果你想创建自己的适配器或者扩展现有的适配器,这个 Phalcon\Db\AdapterInterface 接口必须被实现。 数据库“方言”¶Phalcon把每个数据库引擎的具体操作封装成“方言”,这些“方言”提供了提供通用的功能和SQL生成的适配器。 (译者注:这里的“方言”是指Phalcon把一些常用的数据库操作封装成类的方法,例如检查数据库中表是否存在,不再需要麻烦的手动写SQL,可以把调用tableExists方法去查询)
自定义“方言”(Implementing your own dialects)¶如果你想创建自己的“方言”或者扩展现有的“方言”,你需要实现这个接口: Phalcon\Db\DialectInterface 连接数据库(Connecting to Databases)¶为了建立连接,实例化适配器类是必须的。它只接收一个包含连接参数的数组。 下面的例子展示了,传递必要参数和可选项的参数去连接数据库: <?php
// 必要参数
$config = [
"host" => "127.0.0.1",
"username" => "mike",
"password" => "sigma",
"dbname" => "test_db",
];
// 可选参数
$config["persistent"] = false;
// 创建连接
$connection = new \Phalcon\Db\Adapter\Pdo\Mysql($config);
<?php
// 必要参数
$config = [
"host" => "localhost",
"username" => "postgres",
"password" => "secret1",
"dbname" => "template",
];
// 可选参数
$config["schema"] = "public";
// 创建连接
$connection = new \Phalcon\Db\Adapter\Pdo\Postgresql($config);
<?php
// 必要参数
$config = [
"dbname" => "/path/to/database.db",
];
// 创建连接
$connection = new \Phalcon\Db\Adapter\Pdo\Sqlite($config);
设置额外的 PDO 选项(Setting up additional PDO options)¶你可以在连接的时候,通过传递’options’参数,设置PDO选项: <?php
// 带PDO options参数的创建连接
$connection = new \Phalcon\Db\Adapter\Pdo\Mysql(
[
"host" => "localhost",
"username" => "root",
"password" => "sigma",
"dbname" => "test_db",
"options" => [
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'",
PDO::ATTR_CASE => PDO::CASE_LOWER,
]
]
);
查找行(Finding Rows)¶文档 Phalcon\Db 提供了几种方法去查询行。在这个例子中,SQL语句是必须符合数据库的SQL语法的: <?php
$sql = "SELECT id, name FROM robots ORDER BY name";
// 发送SQL语句到数据库
$result = $connection->query($sql);
// 打印每个robot名称
while ($robot = $result->fetch()) {
echo $robot["name"];
}
// 返回一个包含返回结果的数组
$robots = $connection->fetchAll($sql);
foreach ($robots as $robot) {
echo $robot["name"];
}
// 只返回查询结果的第一条数据
$robot = $connection->fetchOne($sql);
默认情况下,这些调用会建立一个数组,数组中包含以字段名和以数字下标为key的值。你可以改变这种行为通过使用
<?php
$sql = "SELECT id, name FROM robots ORDER BY name";
$result = $connection->query($sql);
$result->setFetchMode(Phalcon\Db::FETCH_NUM);
while ($robot = $result->fetch()) {
echo $robot[0];
}
这个 <?php
$sql = "SELECT id, name FROM robots";
$result = $connection->query($sql);
// 遍历结果集
while ($robot = $result->fetch()) {
echo $robot["name"];
}
// 获取第三条记录
$result->seek(2);
$robot = $result->fetch();
// 计算结果集的记录数
echo $result->numRows();
绑定参数(Binding Parameters)¶在 Phalcon\Db 中支持绑定参数。虽然使用绑定参数会有很少性能的损失,但是我们鼓励你使用这个方法 去消除(译者注:是消除,不是减少,因为使用参数绑定可以彻底解决SQL注入问题)SQL注入攻击的可能性。 字符串和占位符都支持,就像下面展示的那样,绑定参数可以简单地实现: <?php
// 用数字占位符绑定参数
$sql = "SELECT * FROM robots WHERE name = ? ORDER BY name";
$result = $connection->query(
$sql,
[
"Wall-E",
]
);
// 用指定的占位符绑定参数
$sql = "INSERT INTO `robots`(name`, year) VALUES (:name, :year)";
$success = $connection->query(
$sql,
[
"name" => "Astro Boy",
"year" => 1952,
]
);
When using numeric placeholders, you will need to define them as integers i.e. 1 or 2. In this case “1” or “2” are considered strings and not numbers, so the placeholder could not be successfully replaced. With any adapter data are automatically escaped using PDO Quote. This function takes into account the connection charset, so its recommended to define the correct charset in the connection parameters or in your database server configuration, as a wrong charset will produce undesired effects when storing or retrieving data. Also, you can pass your parameters directly to the execute/query methods. In this case bound parameters are directly passed to PDO: <?php
// Binding with PDO placeholders
$sql = "SELECT * FROM robots WHERE name = ? ORDER BY name";
$result = $connection->query(
$sql,
[
1 => "Wall-E",
]
);
插入、更新、删除行(Inserting/Updating/Deleting Rows)¶去插入,更新或者删除行,你可以使用原生SQL操作,或者使用类中预设的方法 <?php
// 使用原生SQL插入行
$sql = "INSERT INTO `robots`(`name`, `year`) VALUES ('Astro Boy', 1952)";
$success = $connection->execute($sql);
// 使用带占位符的SQL插入行
$sql = "INSERT INTO `robots`(`name`, `year`) VALUES (?, ?)";
$success = $connection->execute(
$sql,
[
"Astro Boy",
1952,
]
);
// 使用类中预设的方法插入行
$success = $connection->insert(
"robots",
[
"Astro Boy",
1952,
],
[
"name",
"year",
],
);
// 插入数据的另外一种方法
$success = $connection->insertAsDict(
"robots",
[
"name" => "Astro Boy",
"year" => 1952,
]
);
// 使用原生SQL更新行
$sql = "UPDATE `robots` SET `name` = 'Astro boy' WHERE `id` = 101";
$success = $connection->execute($sql);
// 使用带占位符的SQL更新行
$sql = "UPDATE `robots` SET `name` = ? WHERE `id` = ?";
$success = $connection->execute(
$sql,
[
"Astro Boy",
101,
]
);
// 使用类中预设的方法更新行
$success = $connection->update(
"robots",
[
"name",
],
[
"New Astro Boy",
],
"id = 101" // Warning! In this case values are not escaped
);
// 更新数据的另外一种方法
$success = $connection->updateAsDict(
"robots",
[
"name" => "New Astro Boy",
],
"id = 101" // Warning! In this case values are not escaped
);
// With escaping conditions
$success = $connection->update(
"robots",
[
"name",
],
[
"New Astro Boy",
],
[
"conditions" => "id = ?",
"bind" => [101],
"bindTypes" => [PDO::PARAM_INT], // Optional parameter
]
);
$success = $connection->updateAsDict(
"robots",
[
"name" => "New Astro Boy",
],
[
"conditions" => "id = ?",
"bind" => [101],
"bindTypes" => [PDO::PARAM_INT], // Optional parameter
]
);
// 使用原生SQL删除数据
$sql = "DELETE `robots` WHERE `id` = 101";
$success = $connection->execute($sql);
// 使用带占位符的SQL删除行
$sql = "DELETE `robots` WHERE `id` = ?";
$success = $connection->execute($sql, [101]);
// 使用类中预设的方法删除行
$success = $connection->delete(
"robots",
"id = ?",
[
101,
]
);
事务与嵌套事务(Transactions and Nested Transactions)¶PDO支持事务工作。在事务里面执行数据操作, 在大多数数据库系统上, 往往可以提高数据库的性能: <?php
try {
// 开始一个事务
$connection->begin();
// 执行一些操作
$connection->execute("DELETE `robots` WHERE `id` = 101");
$connection->execute("DELETE `robots` WHERE `id` = 102");
$connection->execute("DELETE `robots` WHERE `id` = 103");
// 提交操作,如果一切正常
$connection->commit();
} catch (Exception $e) {
// 如果发现异常,回滚操作
$connection->rollback();
}
除了标准的事务, Phalcon\Db 提供了内置支持`嵌套事务`_(如果数据库系统支持的话)。 当你第二次调用begin()方法,一个嵌套的事务就被创建了: <?php
try {
// 开始一个事务
$connection->begin();
// 执行某些SQL操作
$connection->execute("DELETE `robots` WHERE `id` = 101");
try {
// 开始一个嵌套事务
$connection->begin();
// 在嵌套事务中执行这些SQL
$connection->execute("DELETE `robots` WHERE `id` = 102");
$connection->execute("DELETE `robots` WHERE `id` = 103");
// 创建一个保存的点
$connection->commit();
} catch (Exception $e) {
// 发生错误,释放嵌套的事务
$connection->rollback();
}
// 继续,执行更多SQL操作
$connection->execute("DELETE `robots` WHERE `id` = 104");
// 如果一切正常,提交
$connection->commit();
} catch (Exception $e) {
// 发生错误,回滚操作
$connection->rollback();
}
数据库事件(Database Events)¶Phalcon\Db 可以发送事件到一个 EventsManager 中,如果它存在的话。 一些事件当返回布尔值false可以停止操作。我们支持下面这些事件:
绑定一个EventsManager给一个连接是很简单的, Phalcon\Db 将触发这些类型为“db”的事件: <?php
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Db\Adapter\Pdo\Mysql as Connection;
$eventsManager = new EventsManager();
// 监听所有数据库事件
$eventsManager->attach('db', $dbListener);
$connection = new Connection(
[
"host" => "localhost",
"username" => "root",
"password" => "secret",
"dbname" => "invo",
]
);
// 把eventsManager分配给适配器实例
$connection->setEventsManager($eventsManager);
数据库事件中,停止操作是非常有用的,例如:如果你想要实现一个注入检查器,在发送SQL到数据库前触发: <?php
use Phalcon\Events\Event;
$eventsManager->attach(
"db:beforeQuery",
function (Event $event, $connection) {
$sql = $connection->getSQLStatement();
// 检查是否有恶意关键词
if (preg_match("/DROP|ALTER/i", $sql)) {
// DROP/ALTER 操作是不允许的, 这肯定是一个注入!
// 返回false中断此操作
return false;
}
// 一切正常
return true;
}
);
分析 SQL 语句(Profiling SQL Statements)¶Phalcon\Db 包含了一个性能分析组件,叫 Phalcon\Db\Profiler ,它被用于分析数据库的操作性能以便诊断性能问题,并发现瓶颈。 使用 Phalcon\Db\Profiler 来分析数据库真的很简单: <?php
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Db\Profiler as DbProfiler;
$eventsManager = new EventsManager();
$profiler = new DbProfiler();
// 监听所有数据库的事件
$eventsManager->attach(
"db",
function (Event $event, $connection) use ($profiler) {
if ($event->getType() === "beforeQuery") {
$sql = $connection->getSQLStatement();
// 操作前启动分析
$profiler->startProfile($sql);
}
if ($event->getType() === "afterQuery") {
// 操作后停止分析
$profiler->stopProfile();
}
}
);
// 设置事件管理器
$connection->setEventsManager($eventsManager);
$sql = "SELECT buyer_name, quantity, product_name "
. "FROM buyers "
. "LEFT JOIN products ON buyers.pid = products.id";
// 执行SQL
$connection->query($sql);
// 获取最后一个分析结果
$profile = $profiler->getLastProfile();
echo "SQL Statement: ", $profile->getSQLStatement(), "\n";
echo "Start Time: ", $profile->getInitialTime(), "\n";
echo "Final Time: ", $profile->getFinalTime(), "\n";
echo "Total Elapsed Time: ", $profile->getTotalElapsedSeconds(), "\n";
你也可以基于 Phalcon\Db\Profiler 建立你自己的分析器类,以记录SQL语句发送到数据库的实时统计: <?php
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Db\Profiler as Profiler;
use Phalcon\Db\Profiler\Item as Item;
class DbProfiler extends Profiler
{
/**
* 在SQL语句将要发送给数据库前执行
*/
public function beforeStartProfile(Item $profile)
{
echo $profile->getSQLStatement();
}
/**
* 在SQL语句已经被发送到数据库后执行
*/
public function afterEndProfile(Item $profile)
{
echo $profile->getTotalElapsedSeconds();
}
}
// 创建一个事件管理器
$eventsManager = new EventsManager();
// 创建一个监听器
$dbProfiler = new DbProfiler();
// 设置监听器监听所有的数据库事件
$eventsManager->attach("db", $dbProfiler);
记录 SQL 语句(Logging SQL Statements)¶使用例如 Phalcon\Db 的高级抽象组件操作数据库,被发送到数据库中执行的原生SQL语句是难以获知的。使用 Phalcon\Logger 和 Phalcon\Db 来配合使用,可以在数据库抽象层上提供记录的功能。 <?php
use Phalcon\Logger;
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Logger\Adapter\File as FileLogger;
$eventsManager = new EventsManager();
$logger = new FileLogger("app/logs/db.log");
$eventsManager->attach(
"db:beforeQuery",
function (Event $event, $connection) use ($logger) {
$sql = $connection->getSQLStatement();
$logger->log($sql, Logger::INFO);
}
);
// 设置事件管理器
$connection->setEventsManager($eventsManager);
// 执行一些SQL
$connection->insert(
"products",
[
"Hot pepper",
3.50,
],
[
"name",
"price",
]
);
如上操作,文件 app/logs/db.log 将包含像下面这样的信息: [Sun, 29 Apr 12 22:35:26 -0500][DEBUG][Resource Id #77] INSERT INTO products
(name, price) VALUES ('Hot pepper', 3.50)
自定义日志记录器(Implementing your own Logger)¶你可以实现你自己的日志类来记录数据库的所有操作,通过创建一个实现了”log”方法的类。
这个方法需要接受一个字符串作为第一个参数。你可以把日志类的对象传递给 获取数据库表与视图信息(Describing Tables/Views)¶Phalcon\Db 也提供了方法去检索详细的表和视图信息: <?php
// 获取test_db数据库的所有表
$tables = $connection->listTables("test_db");
// 在数据库中是否存在'robots'这个表
$exists = $connection->tableExists("robots");
// 获取'robots'字段名称,数据类型,特殊特征
$fields = $connection->describeColumns("robots");
foreach ($fields as $field) {
echo "Column Type: ", $field["Type"];
}
// 获取'robots'表的所有索引
$indexes = $connection->describeIndexes("robots");
foreach ($indexes as $index) {
print_r(
$index->getColumns()
);
}
// 获取'robots'表的所有外键
$references = $connection->describeReferences("robots");
foreach ($references as $reference) {
// 打印引用的列
print_r(
$reference->getReferencedColumns()
);
}
一个表的详细描述信息和MYSQL的describe命令返回的信息非常相似,它包含以下信息:
对于被支持的数据库系统,获取视图的信息的方法也被实现了: <?php
// 获取test_db数据库的视图
$tables = $connection->listViews("test_db");
// 'robots'视图是否存在数据库中
$exists = $connection->viewExists("robots");
创建/修改/删除表¶不同的数据库系统(MySQL,Postgresql等)通过了CREATE, ALTER 或 DROP命令提供了用于创建,修改或删除表的功能。但是不同的数据库语法不同。 Phalcon\Db 提供了统一的接口来改变表,而不需要区分基于目标存储系统上的SQL语法。 创建数据库表(Creating Tables)¶下面这个例子展示了怎么建立一个表: <?php
use \Phalcon\Db\Column as Column;
$connection->createTable(
"robots",
null,
[
"columns" => [
new Column(
"id",
[
"type" => Column::TYPE_INTEGER,
"size" => 10,
"notNull" => true,
"autoIncrement" => true,
"primary" => true,
]
),
new Column(
"name",
[
"type" => Column::TYPE_VARCHAR,
"size" => 70,
"notNull" => true,
]
),
new Column(
"year",
[
"type" => Column::TYPE_INTEGER,
"size" => 11,
"notNull" => true,
]
),
]
]
);
Phalcon\Db 支持下面的数据库字段类型:
传入
修改数据库表(Altering Tables)¶随着你的应用的增长,作为一个重构的一部分,或者增加新功能,你也许需要修改你的数据库。 因为不是所有的数据库允许你修改已存在的字段或者添加字段在2个已存在的字段之间。所以 Phalcon\Db 会受到数据库系统的这些限制。 <?php
use Phalcon\Db\Column as Column;
// 添加一个新的字段
$connection->addColumn(
"robots",
null,
new Column(
"robot_type",
[
"type" => Column::TYPE_VARCHAR,
"size" => 32,
"notNull" => true,
"after" => "name",
]
)
);
// 修改一个已存在的字段
$connection->modifyColumn(
"robots",
null,
new Column(
"name",
[
"type" => Column::TYPE_VARCHAR,
"size" => 40,
"notNull" => true,
]
)
);
// 删除名为"name"的字段
$connection->dropColumn(
"robots",
null,
"name"
);
删除数据库表(Dropping Tables)¶删除数据库表的例子: <?php
// 删除'robots'表
$connection->dropTable("robots");
// 删除数据库'machines'中的'robots'表
$connection->dropTable("robots", "machines");
|