发布于 2015-08-21 15:24:58 | 752 次阅读 | 评论: 0 | 来源: 网络整理
PhalconDb 是 PhalconMvcModel 背后的一个组件,它为框架提供了强大的model层。它是一个完全由C语言写的独立的高级抽象层的数据库系统。
这个组件提供了比传统模式的更容易上手的数据库操作。
这个指引不是一个完整的包含所有方法和它们的参数的文档。 查看完整的文档参考,请访问 API
这个组件利用了这些适配器去封装特定的数据库的详细操作。Phalcon使用 PDO 去连接这些数据库。下面这些是我们支持的数据库引擎:
名称 | 描述 | API |
---|---|---|
MySQL | MySQL是这个世界上最多人使用的关系数据库,它作为服务器运行为多用户提供了访问多个数据库的功能。 | PhalconDbAdapterPdoMysql |
PostgreSQL | PostgreSQL是一个强大,开源的关系数据库。它拥有超过15年的积极发展和经过验证的架构,这些已经为它赢得了可靠性、数据完整性、正确性的良好的声誉 | PhalconDbAdapterPdoPostgresql |
SQLite | SQLite是一个实现一个自包含的,无服务器,零配置,支持事务的SQL数据库引擎的软件库 | PhalconDbAdapterPdoSqlite |
Oracle | Oracle是一个对象-关系数据库,由甲骨文公司生产和销售。 | PhalconDbAdapterPdoOracle |
如果你想创建自己的适配器或者扩展现有的适配器,这个 PhalconDbAdapterInterface 接口必须被实现。
Phalcon把每个数据库引擎的具体操作封装成“方言”,这些“方言”提供了提供通用的功能和SQL生成的适配器。 (译者注:这里的“方言”是指Phalcon把一些常用的数据库操作封装成类的方法,例如检查数据库中表是否存在,不再需要麻烦的手动写SQL,可以把调用tableExists方法去查询)
名称 | 描述 | API |
---|---|---|
MySQL | MySQL的具体“方言” | PhalconDbDialectMysql |
PostgreSQL | PostgreSQL的具体“方言” | PhalconDbDialectPostgresql |
SQLite | SQLite的具体“方言” | PhalconDbDialectSqlite |
Oracle | Oracle的具体“方言” | PhalconDbDialectOracle |
如果你想创建自己的“方言”或者扩展现有的“方言”,你需要实现这个接口: PhalconDbDialectInterface
为了建立连接,实例化适配器类是必须的。它只接收一个包含连接参数的数组。 下面的例子展示了,传递必要参数和可选项的参数去连接数据库:
<?php
// 必要参数
$config = array(
"host" => "127.0.0.1",
"username" => "mike",
"password" => "sigma",
"dbname" => "test_db"
);
// 可选参数
$config["persistent"] = false;
// 创建连接
$connection = new PhalconDbAdapterPdoMysql($config);
<?php
// 必要参数
$config = array(
"host" => "localhost",
"username" => "postgres",
"password" => "secret1",
"dbname" => "template"
);
// 可选参数
$config["schema"] = "public";
// 创建连接
$connection = new PhalconDbAdapterPdoPostgresql($config);
<?php
// 必要参数
$config = array(
"dbname" => "/path/to/database.db"
);
// 创建连接
$connection = new PhalconDbAdapterPdoSqlite($config);
<?php
// 基本配置信息
$config = array(
'username' => 'scott',
'password' => 'tiger',
'dbname' => '192.168.10.145/orcl'
);
// 高级配置信息
$config = array(
'dbname' => '(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=xe)(FAILOVER_MODE=(TYPE=SELECT)(METHOD=BASIC)(RETRIES=20)(DELAY=5))))',
'username' => 'scott',
'password' => 'tiger',
'charset' => 'AL32UTF8'
);
// 创建连接
$connection = new PhalconDbAdapterPdoOracle($config);
你可以在连接的时候,通过传递’options’参数,设置PDO选项:
<?php
// 带PDO options参数的创建连接
$connection = new PhalconDbAdapterPdoMysql(
array(
"host" => "localhost",
"username" => "root",
"password" => "sigma",
"dbname" => "test_db",
"options" => array(
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'",
PDO::ATTR_CASE => PDO::CASE_LOWER
)
)
);
文档 PhalconDb 提供了几种方法去查询行。在这个例子中,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的值。你可以改变这种行为通过使用 PhalconDbResult::setFetchMode() 。这个方法接受一个常量值,确定哪些类型的指标是被要求的。
常量 | 描述 |
---|---|
PhalconDb::FETCH_NUM | 返回一个包含数字下标的数组 |
PhalconDb::FETCH_ASSOC | 返回一个包含字段名的数组 |
PhalconDb::FETCH_BOTH | 返回一个包含字段名和数字下标的数组 |
PhalconDb::FETCH_OBJ | 返回一个对象而不是一个数组 |
<?php
$sql = "SELECT id, name FROM robots ORDER BY name";
$result = $connection->query($sql);
$result->setFetchMode(PhalconDb::FETCH_NUM);
while ($robot = $result->fetch()) {
echo $robot[0];
}
这个 PhalconDb::query() 方法返回一个 PhalconDbResultPdo 实例。这些对象封装了凡是涉及到返回的结果集的功能,例如遍历,寻找特定行,计算总行数等等
<?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();
在 PhalconDb 中支持绑定参数。虽然使用绑定参数会有很少性能的损失,但是我们鼓励你使用这个方法 去消除(译者注:是消除,不是减少,因为使用参数绑定可以彻底解决SQL注入问题)SQL注入攻击的可能性。 字符串和占位符都支持,就像下面展示的那样,绑定参数可以简单地实现:
<?php
// 用数字占位符绑定参数
$sql = "SELECT * FROM robots WHERE name = ? ORDER BY name";
$result = $connection->query($sql, array("Wall-E"));
// 用指定的占位符绑定参数
$sql = "INSERT INTO `robots`(name`, year) VALUES (:name, :year)";
$success = $connection->query($sql, array("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 parameterers 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, array(1 => "Wall-E"));
去插入,更新或者删除行,你可以使用原生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, array('Astro Boy', 1952));
// 使用类中预设的方法插入行
$success = $connection->insert(
"robots",
array("Astro Boy", 1952),
array("name", "year")
);
// 插入数据的另外一种方法
$success = $connection->insertAsDict(
"robots",
array(
"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, array('Astro Boy', 101));
// 使用类中预设的方法更新行
$success = $connection->update(
"robots",
array("name"),
array("New Astro Boy"),
"id = 101" // Warning! In this case values are not escaped
);
// 更新数据的另外一种方法
$success = $connection->updateAsDict(
"robots",
array(
"name" => "New Astro Boy"
),
"id = 101" // Warning! In this case values are not escaped
);
// With escaping conditions
$success = $connection->update(
"robots",
array("name"),
array("New Astro Boy"),
array(
'conditions' => 'id = ?',
'bind' => array(101),
'bindTypes' => array(PDO::PARAM_INT) // Optional parameter
)
);
$success = $connection->updateAsDict(
"robots",
array(
"name" => "New Astro Boy"
),
array(
'conditions' => 'id = ?',
'bind' => array(101),
'bindTypes' => array(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, array(101));
// 使用类中预设的方法删除行
$success = $connection->delete("robots", "id = ?", array(101));
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();
}
除了标准的事务,PhalconDb提供了内置支持`嵌套事务`_(如果数据库系统支持的话)。 当你第二次调用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();
}
PhalconDb 可以发送事件到一个 EventsManager 中,如果它存在的话。 一些事件当返回布尔值false可以停止操作。我们支持下面这些事件:
事件名 | 何时触发 | 可以停止操作吗? |
---|---|---|
afterConnect | 当成功连接数据库之后触发 | No |
beforeQuery | 在发送SQL到数据库前触发 | Yes |
afterQuery | 在发送SQL到数据库执行后触发 | No |
beforeDisconnect | 在关闭一个暂存的数据库连接前触发 | No |
beginTransaction | 事务启动前触发 | No |
rollbackTransaction | 事务回滚前触发 | No |
commitTransaction | 事务提交前触发 | No |
绑定一个EventsManager给一个连接是很简单的,PhalconDb将触发这些类型为“db”的事件:
<?php
use PhalconEventsManager as EventsManager;
use PhalconDbAdapterPdoMysql as Connection;
$eventsManager = new EventsManager();
// 监听所有数据库事件
$eventsManager->attach('db', $dbListener);
$connection = new Connection(
array(
"host" => "localhost",
"username" => "root",
"password" => "secret",
"dbname" => "invo"
)
);
// 把eventsManager分配给适配器实例
$connection->setEventsManager($eventsManager);
数据库事件中,停止操作是非常有用的,例如:如果你想要实现一个注入检查器,在发送SQL到数据库前触发:
<?php
$eventsManager->attach('db:beforeQuery', function ($event, $connection) {
// 检查是否有恶意关键词
if (preg_match('/DROP|ALTER/i', $connection->getSQLStatement())) {
// DROP/ALTER 操作是不允许的, 这肯定是一个注入!
// 返回false中断此操作
return false;
}
// 一切正常
return true;
});
PhalconDb 包含了一个性能分析组件,叫 PhalconDbProfiler ,它被用于分析数据库的操作性能以便诊断性能问题,并发现瓶颈。 使用 PhalconDbProfiler 来分析数据库真的很简单:
<?php
use PhalconEventsManager as EventsManager;
use PhalconDbProfiler as DbProfiler;
$eventsManager = new EventsManager();
$profiler = new DbProfiler();
// 监听所有数据库的事件
$eventsManager->attach('db', function ($event, $connection) use ($profiler) {
if ($event->getType() == 'beforeQuery') {
// 操作前启动分析
$profiler->startProfile($connection->getSQLStatement());
}
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";
你也可以基于 PhalconDbProfiler 建立你自己的分析器类,以记录SQL语句发送到数据库的实时统计:
<?php
use PhalconEventsManager as EventsManager;
use PhalconDbProfiler as Profiler;
use PhalconDbProfilerItem 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);
使用例如 PhalconDb 的高级抽象组件操作数据库,被发送到数据库中执行的原生SQL语句是难以获知的。使用 PhalconLogger 和 PhalconDb 来配合使用,可以在数据库抽象层上提供记录的功能。
<?php
use PhalconLogger;
use PhalconEventsManager as EventsManager;
use PhalconLoggerAdapterFile as FileLogger;
$eventsManager = new EventsManager();
$logger = new FileLogger("app/logs/db.log");
// 监听所有数据库事件
$eventsManager->attach('db', function ($event, $connection) use ($logger) {
if ($event->getType() == 'beforeQuery') {
$logger->log($connection->getSQLStatement(), Logger::INFO);
}
});
// 设置事件管理器
$connection->setEventsManager($eventsManager);
// 执行一些SQL
$connection->insert(
"products",
array("Hot pepper", 3.50),
array("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)
你可以实现你自己的日志类来记录数据库的所有操作,通过创建一个实现了”log”方法的类。 这个方法需要接受一个字符串作为第一个参数。你可以把日志类的对象传递给PhalconDb::setLogger(), 这样执行SQL时将调用这个对象的log方法去记录。
PhalconDb 也提供了方法去检索详细的表和视图信息:
<?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命令返回的信息非常相似,它包含以下信息:
下标 | 描述 |
---|---|
Field | 字段名称 |
Type | 字段类型 |
Key | 是否是主键或者索引 |
Null | 是否允许为空 |
对于被支持的数据库系统,获取视图的信息的方法也被实现了:
<?php
// 获取test_db数据库的视图
$tables = $connection->listViews("test_db");
// 'robots'视图是否存在数据库中
$exists = $connection->viewExists("robots");
不同的数据库系统(MySQL,Postgresql等)通过了CREATE, ALTER 或 DROP命令提供了用于创建,修改或删除表的功能。但是不同的数据库语法不同。 PhalconDb 提供了统一的接口来改变表,而不需要区分基于目标存储系统上的SQL语法。
下面这个例子展示了怎么建立一个表:
<?php
use PhalconDbColumn as Column;
$connection->createTable(
"robots",
null,
array(
"columns" => array(
new Column(
"id",
array(
"type" => Column::TYPE_INTEGER,
"size" => 10,
"notNull" => true,
"autoIncrement" => true,
)
),
new Column(
"name",
array(
"type" => Column::TYPE_VARCHAR,
"size" => 70,
"notNull" => true,
)
),
new Column(
"year",
array(
"type" => Column::TYPE_INTEGER,
"size" => 11,
"notNull" => true,
)
)
)
)
);
PhalconDb::createTable()接受一个描述数据库表相关的数组。字段被定义成class PhalconDbColumn 。 下表列出了可用于定义字段的选项:
PhalconDb 支持下面的数据库字段类型:
传入PhalconDb::createTable() 的相关数组可能含有的下标:
下标 | 描述 | 是否可选 |
---|---|---|
“columns” | 一个数组包含表的所有字段,字段要定义成 PhalconDbColumn | 不 |
“indexes” | 一个数组包含表的所有索引,索引要定义成 PhalconDbIndex | 是 |
“references” | 一个数组包含表的所有外键,外键要定义成 PhalconDbReference | 是 |
“options” | 一个表包含所有创建的选项. 这些选项常常和数据库迁移有关. | 是 |
随着你的应用的增长,作为一个重构的一部分,或者增加新功能,你也许需要修改你的数据库。 因为不是所有的数据库允许你修改已存在的字段或者添加字段在2个已存在的字段之间。所以 PhalconDb 会受到数据库系统的这些限制。
<?php
use PhalconDbColumn as Column;
// 添加一个新的字段
$connection->addColumn(
"robots",
null,
new Column(
"robot_type",
array(
"type" => Column::TYPE_VARCHAR,
"size" => 32,
"notNull" => true,
"after" => "name"
)
)
);
// 修改一个已存在的字段
$connection->modifyColumn(
"robots",
null,
new Column(
"name",
array(
"type" => Column::TYPE_VARCHAR,
"size" => 40,
"notNull" => true
)
)
);
// 删除名为"name"的字段
$connection->dropColumn(
"robots",
null,
"name"
);
删除数据库表的例子:
<?php
// 删除'robots'表
$connection->dropTable("robots");
// 删除数据库'machines'中的'robots'表
$connection->dropTable("robots", "machines");