数据库抽象层¶
概览¶
下的组件Phalcon\Db
命名空间中的类负责提供框架中MVC的Phalcon\Mvc\Model类——框架中Model
MVC部分的核心数据库支持。它是一个完全用C语言编写、独立的高级数据库系统抽象层。
此组件允许比使用传统模型更底层的数据库操作。
适配器¶
该组件使用适配器封装特定数据库系统的细节。Phalcon 使用 PDO 来连接数据库。目前支持以下数据库引擎:
类 | 描述 |
---|---|
Phalcon\Db\Adapter\Pdo\Mysql | 是全球最广泛使用的作为服务器运行的关系型数据库管理系统(RDBMS),为多个用户提供访问多个数据库的功能 |
Phalcon\Db\Adapter\Pdo\Postgresql | PostgreSQL 是一个功能强大的开源关系型数据库系统。经过超过15年积极的发展,其架构已经非常成熟,以可靠性、数据完整性和准确性著称。 |
Phalcon\Db\Adapter\Pdo\Sqlite | SQLite 是一个实现了自包含、无服务端、零配置、事务性 SQL 数据库引擎的软件库 |
常量¶
The Phalcon\Db\Enum类暴露了几个可以在 DB 层使用的常量。-FETCH_ASSOC
= \Pdo::FETCH_ASSOC
- FETCH_BOTH
= \Pdo::FETCH_BOTH
- FETCH_BOUND
= \Pdo::FETCH_BOUND
- FETCH_CLASS
= \Pdo::FETCH_CLASS
- FETCH_CLASSTYPE
= \Pdo::FETCH_CLASSTYPE
- FETCH_COLUMN
= \Pdo::FETCH_COLUMN
- FETCH_FUNC
= \Pdo::FETCH_FUNC
- FETCH_GROUP
= \Pdo::FETCH_GROUP
- FETCH_INTO
= \Pdo::FETCH_INTO
- FETCH_KEY_PAIR
= \Pdo::FETCH_KEY_PAIR
- FETCH_LAZY
= \Pdo::FETCH_LAZY
- FETCH_NAMED
= \Pdo::FETCH_NAMED
- FETCH_NUM
= \Pdo::FETCH_NUM
- FETCH_OBJ
= \Pdo::FETCH_OBJ
- FETCH_PROPS_LATE
= \Pdo::FETCH_PROPS_LATE
- FETCH_SERIALIZE
= \Pdo::FETCH_SERIALIZE
- FETCH_UNIQUE
= \Pdo::FETCH_UNIQUE
更多的常量可在Phalcon\Db\Column对象中找到。此对象用于描述数据库表中的列(或字段)。这些常量还定义了 ORM 支持的类型。
绑定类型
类型 | 描述 |
---|---|
BIND_PARAM_BLOB | Blob |
BIND_PARAM_BOOL | 布尔值 |
BIND_PARAM_DECIMAL | 小数 |
BIND_PARAM_INT | 整数 |
BIND_PARAM_NULL | 允许空值 |
BIND_PARAM_STR | 字符串 |
BIND_SKIP | 跳过绑定 |
列类型
类型 | 描述 |
---|---|
TYPE_BIGINTEGER | 大整数 |
TYPE_BINARY | 二进制 |
TYPE_BIT | 位 |
TYPE_BLOB | Blob |
TYPE_BOOLEAN | 布尔型 |
TYPE_CHAR | 字符 |
TYPE_DATE | 日期 |
TYPE_DATETIME | 日期时间 |
TYPE_DECIMAL | 小数 |
TYPE_DOUBLE | 双精度浮点数 |
TYPE_ENUM | 枚举 |
TYPE_FLOAT | 浮点数 |
TYPE_INTEGER | 整数 |
TYPE_JSON | JSON |
TYPE_JSONB | JSONB |
TYPE_LONGBLOB | 长Blob |
TYPE_LONGTEXT | 长文本 |
TYPE_MEDIUMBLOB | 中等Blob |
TYPE_MEDIUMINTEGER | 中等整数 |
TYPE_MEDIUMTEXT | 中等文本 |
TYPE_SMALLINTEGER | 小整数 |
TYPE_TEXT | 文本 |
TYPE_TIME | 时间 |
TYPE_TIMESTAMP | 时间戳 |
TYPE_TINYBLOB | 微型Blob |
TYPE_TINYINTEGER | 微型整数 |
TYPE_TINYTEXT | 微型文本 |
TYPE_VARBINARY | 变长二进制 |
TYPE_VARCHAR | 可变字符 |
注意
根据你的RDBMS,某些类型可能不可用(例如,JSON
在Sqlite中不被支持)。
方法¶
向表中添加一列 向表中添加索引public function addForeignKey(
string $tableName,
string $schemaName,
ReferenceInterface $reference
): bool
INSERT
/UPDATE
/DELETE
操作影响的行数 在连接中启动一个事务 关闭活动连接并返回成功状态。Phalcon 会自动关闭和销毁活动连接 提交连接中的活动事务 此方法在Phalcon\Db\Adapter\Pdo\AbstractPdo构造函数中被自动调用。当你需要恢复数据库连接时调用它 创建一个新的保存点 创建一个表 创建视图 public function delete(
mixed $table,
string $whereCondition = null,
array $placeholders = [],
array $dataTypes = []
): bool
public function dropForeignKey(
string $tableName,
string $schemaName,
string $referenceName
): bool
public function dropTable(
string $tableName,
string $schemaName = null,
bool $ifExists = true
): bool
public function dropView(
string $viewName,
string $schemaName = null,
bool $ifExists = true
): bool
public function execute(
string $sqlStatement,
array $bindParams = [],
array $bindTypes = []
): bool
public function fetchAll(
string $sqlQuery,
int $fetchMode = 2,
array $bindParams = [],
array $bindTypes = []
): array
public function fetchColumn(
string $sqlQuery,
array $placeholders = [],
mixed $column = 0
): string | bool
$invoicesCount = $connection
->fetchColumn('SELECT count(*) FROM co_invoices')
print_r($invoicesCount)
$invoice = $connection->fetchColumn(
'SELECT inv_id, inv_title
FROM co_invoices
ORDER BY inv_created_at DESC',
1
)
print_r($invoice)
public function fetchOne(
string $sqlQuery,
int $fetchMode = 2,
array $bindParams = [],
array $bindTypes = []
): array
public function insert(
string $table,
array $values,
mixed $fields = null,
mixed $dataTypes = null
): bool
$success = $connection->insertAsDict(
'co_invoices',
[
'inv_cst_id' => 1,
'inv_title' => 'Invoice for ACME Inc.',
]
)
// SQL
// INSERT INTO `co_invoices`
// ( `inv_cst_id`, `inv_title` )
// VALUES
// ( 1, 'Invoice for ACME Inc.' )
public function modifyColumn(
string $tableName,
string $schemaName,
ColumnInterface $column,
ColumnInterface $currentColumn = null
): bool
public function query(
string $sqlStatement,
array $bindParams = [],
array $bindTypes = []
): ResultInterface | bool
public function setNestedTransactionsWithSavepoints(
bool $nestedTransactionsWithSavepoints
): AdapterInterface
schema.table
获取表的创建选项 public function update(
string $table,
mixed $fields,
mixed $values,
mixed $whereCondition = null,
mixed $dataTypes = null
): bool
public function updateAsDict(
string $table,
mixed $data,
mixed $whereCondition = null,
mixed $dataTypes = null
): bool
$success = $connection->updateAsDict(
'co_invoices',
[
'inv_title' => 'Invoice for ACME Inc.',
],
'inv_id = 1'
)
// SQL
// UPDATE `co_invoices`
// SET `inv_title` = 'Invoice for ACME Inc.'
// WHERE inv_id = 1
自定义¶
The Phalcon\Db\AdapterInterface必须实现该接口,以创建你的数据库适配器或扩展已有适配器。此外,你还可以扩展Phalcon\Db\AbstractAdapter该类已经包含了一些可复用的自定义适配器实现。
转义¶
转义标识符功能默认是启用的。但是,如果你需要禁用此功能,可以使用setup()
方法:
工厂¶
newInstance()
¶
虽然所有适配器类都可以通过new
关键字实例化,Phalcon 提供了Phalcon\Db\Adapter\PdoFactory类,以便你可以轻松地实例化 PDO 适配器实例。所有上述适配器都在工厂中注册,并在调用时懒加载。该工厂允许你注册额外(自定义)的适配器类。唯一需要考虑的是选择适配器名称时要与现有名称区分开来。如果你定义了相同名称,则会覆盖内置的适配器。对象会在工厂中缓存,因此如果你调用newInstance()
方法,你会得到相同的对象返回。
预留的名称有:
名称 | 适配器 |
---|---|
mysql | Phalcon\Db\Adapter\Pdo\Mysql |
postgresql | Phalcon\Db\Adapter\Pdo\Postgresql |
sqlite | Phalcon\Db\Adapter\Pdo\Sqlite |
下面的例子展示了如何使用new
关键字或工厂:
<?php
use Phalcon\Db\Adapter\Pdo\MySQL;
$connection = new MySQL(
[
'host' => 'localhost',
'username' => 'root',
'password' => '',
'dbname' => 'test',
]
);
<?php
use Phalcon\Db\Adapter\PdoFactory;
$factory = new PdoFactory();
$connection = $factory
->newInstance(
'mysql',
[
'host' => 'localhost',
'username' => 'root',
'password' => '',
'dbname' => 'test',
]
)
;
load()
¶
您还可以使用load()
方法通过配置对象或数组创建适配器。下面的例子使用了一个ini
文件来使用load()
。该load()
方法接受一个Phalcon\Config\Config
对象或包含两个元素的数组:适配器名称(adapter
)和适配器选项(options
)。
[database]
adapter = mysql
options.host = DATA_MYSQL_HOST
options.username = DATA_MYSQL_USER
options.password = DATA_MYSQL_PASS
options.dbname = DATA_MYSQL_NAME
options.port = DATA_MYSQL_PORT
options.charset = DATA_MYSQL_CHARSET
<?php
use Phalcon\Config\Adapter\Ini;
use Phalcon\Di\Di;
use Phalcon\Db\Adapter\PdoFactory;
$container = new Di();
$config = new Ini('config.ini');
$container->set('config', $config);
$container->set(
'db',
function () {
return (new PdoFactory())->load($this->config->database);
}
);
方言¶
内置¶
Phalcon 将每个数据库引擎的特定细节封装在方言中。Phalcon\Db\Dialect向适配器提供通用函数和 SQL 生成器。
类 | 描述 |
---|---|
Phalcon\Db\Dialect\Mysql | MySQL 数据库系统的特定 SQL 方言 |
Phalcon\Db\Dialect\Postgresql | PostgreSQL 数据库系统的特定 SQL 方言 |
Phalcon\Db\Dialect\Sqlite | SQLite 数据库系统的特定 SQL 方言 |
自定义¶
The Phalcon\Db\DialectInterface必须实现该接口,以创建数据库方言或扩展已有方言。你还可以通过添加 PHQL 可识别的新命令/方法来增强当前的方言。例如,在使用 MySQL 适配器时,你可能希望让 PHQL 支持MATCH ... AGAINST ...
语法。我们将该语法关联到MATCH_AGAINST
我们实例化方言。我们添加了自定义函数,以便 PHQL 在解析过程中遇到它时知道如何处理。在下面的例子中,我们注册了一个名为MATCH_AGAINST
的新自定义函数。之后,我们需要做的就是将自定义的方言对象添加到我们的连接中。
<?php
use Phalcon\Db\Dialect\MySQL as SqlDialect;
use Phalcon\Db\Adapter\Pdo\MySQL as Connection;
$dialect = new SqlDialect();
$dialect->registerCustomFunction(
'MATCH_AGAINST',
function ($dialect, $expression) {
$arguments = $expression['arguments'];
return sprintf(
' MATCH (%s) AGAINST (%s)',
$dialect->getSqlExpression($arguments[0]),
$dialect->getSqlExpression($arguments[1])
);
}
);
$connection = new Connection(
[
'host' => 'localhost',
'username' => 'root',
'password' => '',
'dbname' => 'test',
'dialectClass' => $dialect,
]
);
现在我们可以在 PHQL 中使用这个新函数,它会将其转换为正确的 SQL 语法:
<?php
$phql = '
SELECT *
FROM Invoices
WHERE MATCH_AGAINST(title, :pattern:)';
$posts = $modelsManager->executeQuery(
$phql,
[
'pattern' => $pattern,
]
);
注意
在PHQL文档中指定的。
连接¶
创建连接需要实例化适配器类。它只需要一个包含连接参数的数组。下面的例子展示了如何通过传递必需和可选参数来创建连接:
适配器 | 参数 | 状态 |
---|---|---|
MySQL | host | 必需 |
username | 必需 | |
password | 必需 | |
dbname | 必需 | |
persistent | 可选 | |
PostgreSQL | host | 必需 |
username | 必需 | |
password | 必需 | |
dbname | 必需 | |
schema | 可选 | |
Sqlite | dbname | 必需 |
连接到每个适配器可以通过上述工厂方式实现,或者通过将相关选项传递给每个类的构造函数实现。
<?php
use Phalcon\Db\Adapter\Pdo\Mysql;
use Phalcon\Db\Adapter\Pdo\Postgresql;
use Phalcon\Db\Adapter\Pdo\Sqlite;
$config = [
'host' => '127.0.0.1',
'username' => 'mike',
'password' => 'sigma',
'dbname' => 'test_db',
];
$connection = new Mysql($config);
$config = [
'host' => 'localhost',
'username' => 'postgres',
'password' => 'secret1',
'dbname' => 'template',
];
$connection = new Postgresql($config);
$config = [
'dbname' => '/path/to/database.db',
];
$connection = new Sqlite($config);
附加的 PDO 选项
你可以在连接时通过传递参数设置 PDO 选项options
:
<?php
use Phalcon\Db\Adapter\Pdo\Mysql;
$connection = new 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,
]
]
);
创建¶
要插入一行数据到数据库,你可以使用原始 SQL 或使用适配器提供的方法:
<?php
$sql = "
INSERT INTO `co_invoices`
( `inv_cst_id`, `inv_title` )
VALUES
( 1, 'Invoice for ACME Inc.' )
";
$success = $connection->execute($sql);
<?php
$sql = '
INSERT INTO `co_invoices`
( `inv_cst_id`, `inv_title` )
VALUES
( ?, ? )
';
$success = $connection->execute(
$sql,
[
1,
'Invoice for ACME Inc.',
]
);
<?php
$success = $connection->insert(
'co_invoices',
[
1,
'Invoice for ACME Inc.',
],
[
'inv_cst_id',
'inv_title',
]
);
<?php
$success = $connection->insertAsDict(
'co_invoices',
[
'inv_cst_id' => 1,
'inv_title' => 'Invoice for ACME Inc.',
]
);
更新¶
要更新数据库中的一行数据,你可以使用原始 SQL 或使用适配器提供的方法:
<?php
$sql = "
UPDATE
`co_invoices`
SET
`inv_cst_id`= 1,
`inv_title` = 'Invoice for ACME Inc.'
WHERE
`inv_id` = 4
";
$success = $connection->execute($sql);
<?php
$sql = "
UPDATE
`co_invoices`
SET
`inv_cst_id`= ?,
`inv_title` = ?
WHERE
`inv_id` = ?
";
$success = $connection->execute(
$sql,
[
1,
'Invoice for ACME Inc.',
4,
]
);
<?php
$success = $connection->update(
'co_invoices',
[
'inv_cst_id',
'inv_title',
],
[
1,
'Invoice for ACME Inc.',
],
'inv_id = 4'
);
注意
使用上面的语法,where
子句中的变量update
(inv_id = 4
)不会被转义!
<?php
$success = $connection->updateAsDict(
'co_invoices',
[
'inv_cst_id' => 1,
'inv_title' => 'Invoice for ACME Inc.',
],
'inv_id = 4'
);
注意
使用上面的语法,where
子句中的变量update
(inv_id = 4
)不会被转义!
<?php
$success = $connection->update(
'co_invoices',
[
'inv_cst_id',
'inv_title',
],
[
1,
'Invoice for ACME Inc.',
],
[
'conditions' => 'id = ?',
'bind' => [
4
],
'bindTypes' => [
\PDO::PARAM_INT
],
]
);
<?php
$success = $connection->updateAsDict(
'co_invoices',
[
'inv_cst_id' => 1,
'inv_title' => 'Invoice for ACME Inc.',
],
[
'conditions' => 'id = ?',
'bind' => [
4
],
'bindTypes' => [
\PDO::PARAM_INT
],
]
);
删除¶
原始 SQL<?php
$sql = '
DELETE
`co_invoices`
WHERE
`inv_id` = ?
';
$success = $connection->execute(
$sql,
[
4
]
);
参数¶
The Phalcon\Db
适配器提供了几个方法来从表中查询行。在这种情况下,需要目标数据库引擎的特定 SQL 语法:
<?php
$sql = '
SELECT
inv_id,
inv_title
FROM
co_invoices
ORDER BY
inv_created_at
';
$result = $connection->query($sql);
while ($invoice = $result->fetch()) {
echo $invoice['inv_title'];
}
$invoices = $connection->fetchAll($sql);
foreach ($invoices as $invoice) {
echo $invoice['inv_title'];
}
$invoice = $connection->fetchOne($sql);
默认情况下,这些调用会创建同时带有关联索引和数字索引的数组。对于像fetchArray()
, fetch()
和dataSeek()
这样的方法,你可以通过使用Phalcon\Db\Result::setFetchMode()
来更改此行为。对于像fetchAll()
或fetchOne()
这样的方法,你可以使用$fetchMode
参数。
The fetchMode
接收一个常量,定义所需的索引类型。
常量 | 描述 |
---|---|
Phalcon\Db\Enum::FETCH_NUM | 返回仅带数字索引的数组 |
Phalcon\Db\Enum::FETCH_ASSOC | 返回仅带关联索引的数组 |
Phalcon\Db\Enum::FETCH_BOTH | 返回同时带有关联和数字索引的数组 |
Phalcon\Db\Enum::FETCH_GROUP | 返回分组的关联和数字索引数组 |
Phalcon\Db\Enum::FETCH_OBJ | 返回一个对象而不是数组 |
Phalcon\Db\Enum::FETCH_COLUMN | 返回单行字符串或多行数组 |
还有很多其他常量可以使用,类似于 PDO::FETCH_* 常量
<?php
$sql = '
SELECT
inv_id,
inv_title
FROM
co_invoices
ORDER BY
inv_created_at
';
$result = $connection->query($sql);
$result->setFetchMode(
Phalcon\Db\Enum::FETCH_NUM
);
while ($invoice = $result->fetch()) {
echo $invoice[0];
}
$invoices = $connection->fetchAll($sql, Phalcon\Db\Enum::FETCH_ASSOC);
// or using the previous query() method
$invoices = $result->fetchAll(Phalcon\Db\Enum::FETCH_ASSOC)
foreach ($invoices as $invoice) {
echo $invoice['inv_title'];
}
$invoice = $connection->fetchOne($sql, Phalcon\Db\Enum::FETCH_ASSOC);
The query()
方法返回一个Phalcon\Db\Result\Pdo的实例。这些对象封装了所有与返回的结果集相关的功能,比如遍历、定位特定记录等。count
等等。
<?php
$sql = '
SELECT
inv_id,
inv_title
FROM
co_invoices
ORDER BY
inv_created_at
';
$result = $connection->query($sql);
while ($invoice = $result->fetch()) {
echo $invoice['name'];
}
$result->seek(2);
$invoice = $result->fetch();
echo $result->numRows();
绑定¶
支持绑定参数。尽管使用绑定参数会对性能产生极小的影响,但强烈建议你使用这种方法以避免代码受到 SQL 注入攻击的可能性。支持字符串占位符和位置占位符。
<?php
$sql = '
SELECT
inv_id,
inv_title
FROM
co_invoices
WHERE
inv_cst_id = ?
ORDER BY
inv_created_at
';
$result = $connection->query(
$sql,
[
4,
]
);
<?php
$sql = "
UPDATE
`co_invoices`
SET
`inv_cst_id`= :cstId,
`inv_title` = :title
WHERE
`inv_id` = :id
";
$success = $connection->query(
$sql,
[
'cstId' => 1,
'title' => 'Invoice for ACME Inc.',
'id' => 4,
]
);
当使用数字占位符时,你需要将它们定义为整数,例如:1
或2
在这种情况下'1'
或'2'
被视为字符串而不是数字,因此占位符无法成功替换。在任何适配器中,数据都会使用以下方式自动转义:PDO 引用此函数考虑了连接的字符集,因此建议在连接参数或数据库服务器配置中定义正确的字符集,因为错误的字符集在存储或检索数据时会产生不良影响。
另外,你可以直接将参数传递给以下方法:execute
或query
方法。在这种情况下,绑定参数会直接传递给 PDO:
<?php
$sql = '
SELECT
inv_id,
inv_title
FROM
co_invoices
WHERE
inv_cst_id = ?
ORDER BY
inv_created_at
';
$result = $connection->query(
$sql,
[
1 => 4,
]
);
带类型¶
占位符允许你绑定参数以避免 SQL 注入:
<?php
$phql = '
SELECT
inv_id,
inv_title
FROM
Invoices
WHERE
inv_cst_id = :customerId:
ORDER BY
inv_created_at
';
$invoices = $this
->modelsManager
->executeQuery(
$phql,
[
'customerId' => 4,
]
)
;
然而,某些数据库系统在使用占位符时需要额外的操作,例如指定绑定参数的类型:
<?php
use Phalcon\Db\Column;
// ...
$phql = '
SELECT
inv_id,
inv_title
FROM
Invoices
WHERE
inv_cst_id = :customerId:
ORDER BY
inv_created_at
';
$invoices = $this
->modelsManager
->executeQuery(
$phql,
[
'customerId' => 4,
],
Column::BIND_PARAM_INT
)
;
你可以在参数中使用带类型的占位符,而不是在以下中指定绑定类型:executeQuery()
:
<?php
$phql = '
SELECT
inv_id,
inv_title
FROM
Invoices
WHERE
inv_cst_id = {customerId:int}
ORDER BY
inv_created_at
';
$invoices = $this
->modelsManager
->executeQuery(
$phql,
[
'customerId' => 4,
],
)
;
$phql = '
SELECT
inv_id,
inv_title
FROM
Invoices
WHERE
inv_title <> {title:str}
ORDER BY
inv_created_at
';
$invoices = $this
->modelsManager
->executeQuery(
$phql,
[
'title' => 'Invoice for ACME Inc',
],
)
;
如果不需要指定类型,你也可以省略该类型:
<?php
$phql = '
SELECT
inv_id,
inv_title
FROM
Invoices
WHERE
inv_cst_id = {customerId}
ORDER BY
inv_created_at
';
$invoices = $this
->modelsManager
->executeQuery(
$phql,
[
'customerId' => 4,
],
)
;
带类型占位符也更强大,因为现在我们可以绑定一个静态数组,而不必将每个元素作为占位符单独传递:
<?php
$phql = '
SELECT
inv_id,
inv_title
FROM
Invoices
WHERE
inv_cst_id IN ({ids:array})
ORDER BY
inv_created_at
';
$invoices = $this
->modelsManager
->executeQuery(
$phql,
[
'ids' => [1, 3, 5],
],
)
;
可用的类型如下:
绑定类型 | 绑定类型常量 | 示例 |
---|---|---|
字符串 | Column::BIND_PARAM_STR | {name:str} |
int | Column::BIND_PARAM_INT | {number:int} |
浮点数 | Column::BIND_PARAM_DECIMAL | {price:double} |
bool | Column::BIND_PARAM_BOOL | {enabled:bool} |
大对象(BLOB) | Column::BIND_PARAM_BLOB | {image:blob} |
空值 | Column::BIND_PARAM_NULL | {exists:null} |
数组 | 类型为Column::BIND_PARAM_STR | {codes:array} |
数组-字符串 | 类型为Column::BIND_PARAM_STR | {names:array-str} |
数组-整数 | 类型为Column::BIND_PARAM_INT | {flags:array-int} |
类型转换¶
默认情况下,绑定的参数不会在 PHP 用户区转换为指定的绑定类型。此选项允许你在使用 PDO 绑定之前让 Phalcon 进行值的类型转换。一个常见的情况是当传递字符串到以下占位符时:LIMIT
/OFFSET
占位符:
<?php
$number = '100';
$phql = '
SELECT
inv_id,
inv_title
FROM
Invoices
LIMIT
{number:int}
';
$invoices = $modelsManager->executeQuery(
$phql,
[
'number' => $number,
]
);
这导致以下异常:
Fatal error: Uncaught exception 'PDOException' with message
'SQLSTATE[42000]: Syntax error or access violation: 1064.
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right
syntax to use near ''100'' at line 1' in ....
发生这种情况是因为'100'
是一个字符串变量。可以通过先将值转换为整数来轻松解决:
<?php
$number = '100';
$phql = '
SELECT
inv_id,
inv_title
FROM
Invoices
LIMIT
{number:int}
';
$invoices = $modelsManager->executeQuery(
$phql,
[
'number' => (int) $number,
]
);
然而,这种解决方案要求开发人员特别注意如何传递绑定参数及其类型。为了简化此任务并避免意外异常,你可以指示 Phalcon 替你进行这种转换:
根据指定的绑定类型执行以下操作:
绑定类型 | 动作 |
---|---|
Column::BIND_PARAM_STR | 将值转换为原生 PHP 字符串 |
Column::BIND_PARAM_INT | 将值转换为原生 PHP 整数 |
Column::BIND_PARAM_BOOL | 将值转换为原生 PHP 布尔值 |
Column::BIND_PARAM_DECIMAL | 将值转换为原生 PHP 浮点数 |
数据填充¶
PDO 总是将从数据库系统返回的值表示为字符串值,无论该值属于哪种列类型。numeric
或boolean
这种情况发生的原因是一些列类型不能用相应的 PHP 原生类型表示,这是由于它们的大小限制。例如,BIGINT
在 MySQL 中可以存储大整数值,这些值无法在 PHP 中表示为 32 位整数。因此,PDO 和 ORM 默认会安全地决定将所有值保留为字符串。
你可以设置 ORM 自动将这些类型转换为其对应的 PHP 原生类型:
这样你就可以对变量使用严格运算符或对其类型做出假设:
注意
如果你想在使用lastInsertId
调用integer
方法时返回主键,可以在模型上使用以下特性:castLastInsertIdToInt => true
事务处理支持与 PDO 相同的方式。使用事务在大多数数据库系统中都能提高性能,并确保数据完整性:
事务¶
Working with transactions is supported the same way as with PDO. Using transactions increases performance in most database systems and also ensures data integrity:
<?php
try {
$connection->begin();
$connection->execute('DELETE `co_invoices` WHERE `inv_id` = 1');
$connection->execute('DELETE `co_invoices` WHERE `inv_id` = 2');
$connection->execute('DELETE `co_invoices` WHERE `inv_id` = 3');
$connection->commit();
} catch (Exception $e) {
$connection->rollback();
}
除了标准事务之外,如果所使用的数据库系统支持嵌套事务,则适配器还提供对以下内容的内置支持:嵌套事务当再次调用以下函数时,将创建嵌套事务:begin()
for a second time a nested transaction is created:
<?php
try {
$connection->begin();
$connection->execute('DELETE `co_invoices` WHERE `inv_id` = 1');
try {
$connection->begin();
$connection->execute('DELETE `co_invoices` WHERE `inv_id` = 2');
$connection->execute('DELETE `co_invoices` WHERE `inv_id` = 3');
$connection->commit();
} catch (Exception $e) {
$connection->rollback();
}
$connection->execute('DELETE `co_invoices` WHERE `inv_id` = 4');
$connection->commit();
} catch (Exception $e) {
$connection->rollback();
}
事件¶
适配器还会向事件管理器发送事件(如果存在)。如果某个事件返回以下内容:false
它可以停止当前操作。支持以下事件:
事件名称 | 触发时机 | 可以停止 |
---|---|---|
afterQuery | 查询执行后 | 否 |
beforeQuery | 查询执行前 | 是 |
beginTransaction | 事务开始前 | 否 |
createSavepoint | 创建保存点前 | 否 |
commitTransaction | 事务提交前 | 否 |
releaseSavepoint | 释放保存点前 | 否 |
rollbackTransaction | 事务回滚前 | 否 |
rollbackSavepoint | 回滚保存点前 | 否 |
如果你将事件管理器绑定到数据库连接,所有类型为db
的事件都将启用,并为相关监听器触发。
<?php
use Phalcon\Events\Manager;
use Phalcon\Db\Adapter\Pdo\Mysql;
$manager = new Manager();
$manager->attach('db', $listener);
$connection = new Mysql(
[
'host' => 'localhost',
'username' => 'root',
'password' => 'secret',
'dbname' => 'tutorial',
]
);
$connection->setEventsManager($manager);
你可以利用这些事件的功能来保护你的应用程序免受危险的 SQL 操作。
<?php
use Phalcon\Events\Event;
$manager->attach(
'db:beforeQuery',
function (Event $event, $connection) {
$sql = $connection->getSQLStatement();
if (true === preg_match('/DROP|ALTER/i', $sql)) {
return false;
}
return true;
}
);
性能剖析¶
适配器包含以下组件:Phalcon\Db\Profiler该组件用于分析数据库操作的性能,以诊断性能问题并发现瓶颈。
<?php
use Phalcon\Events\Event;
use Phalcon\Events\Manager;
use Phalcon\Db\Profiler;
$manager = new Manager();
$profiler = new Profiler();
$manager->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($manager);
$sql = '
SELECT
inv_id,
inv_title
FROM
co_invoices
';
$connection->query($sql);
$profile = $profiler->getLastProfile();
echo 'SQL Statement: ', $profile->getSQLStatement(), PHP_EOL,
'Start Time: ', $profile->getInitialTime(), PHP_EOL,
'Final Time: ', $profile->getFinalTime(), PHP_EOL,
'Total Elapsed Time: ', $profile->getTotalElapsedSeconds(), PHP_EOL;
分析器公开了以下方法:getProfiles()
,返回一个包含以下对象的数组:Phalcon\Db\Profiler\Item
对象。每个对象包含相关的统计信息,包括对秒、微秒和纳秒的计算。
你还可以基于以下类创建自己的分析类,以记录发送到数据库的语句的实时统计数据:Phalcon\Db\Profiler class to record real-time statistics of the statements that are sent to the database:
<?php
use Phalcon\Events\Manager;
use Phalcon\Db\Profiler;
use Phalcon\Db\Profiler\Item;
class DbProfiler extends Profiler
{
public function beforeStartProfile(Item $profile)
{
echo $profile->getSQLStatement();
}
public function afterEndProfile(Item $profile)
{
echo $profile->getTotalElapsedSeconds();
}
}
$manager = new Manager();
$listener = new DbProfiler();
$manager->attach('db', $listener);
日志记录¶
使用诸如Phalcon\Db
适配器之类的高级抽象组件访问数据库,使得理解哪些语句被发送到数据库系统变得困难。以下组件与Phalcon\Logger\Logger适配器交互,提供数据库抽象层的日志记录功能。Phalcon\Db
adapters offering logging capabilities on the database abstraction level.
<?php
use Phalcon\Events\Event;
use Phalcon\Events\Manager;
use Phalcon\Logger\Logger;
use Phalcon\Logger\Adapter\Stream;
$adapter = new Stream('/storage/logs/queries.log');
$logger = new Logger(
'messages',
[
'main' => $adapter,
]
);
$manager = new Manager();
$manager->attach(
'db:beforeQuery',
function (Event $event, $connection) use ($logger) {
$sql = $connection->getSQLStatement();
$logger->info(
sprintf(
'%s - [%s]',
$connection->getSQLStatement(),
json_encode($connection->getSQLVariables())
)
);
}
);
$connection->setEventsManager($manager);
$connection->insert(
'products',
[
'Hot pepper',
3.50,
],
[
'name',
'price',
]
);
$connection->insert(
'co_invoices',
[
1,
'Invoice for ACME Inc.',
],
[
'inv_cst_id',
'inv_title',
]
);
如上所述,文件/storage/logs/queries.log
将包含类似以下内容:
[2019-12-25 01:02:03][INFO] INSERT INTO `co_invoices`
SET (`inv_cst_id`, `inv_title`)
VALUES (1, 'Invoice for ACME Inc.')
该监听器还将与模型及其操作一起工作。它还将在日志语句的末尾包含查询使用的所有绑定参数。
[2019-12-25 01:02:03][INFO] SELECT `co_customers`.`cst_id`,
...,
FROM `co_customers`
WHERE LOWER(`co_customers`.`cst_email`) = :cst_email
LIMIT :APL0 - [{"emp_email":"team@phalcon.ld","APL0":1}]
表格¶
描述¶
The Phalcon\Db
适配器还提供了获取有关表格和视图详细信息的方法:
phalcon_db
数据库 检查数据库中是否存在名为co_invoices
的表。 <?php
$fields = $connection->describeColumns('co_invoices');
foreach ($fields as $field) {
echo 'Column Type: ', $field['Type'];
}
co_invoices
表 <?php
$indexes = $connection->describeIndexes('co_invoices');
foreach ($indexes as $index) {
print_r(
$index->getColumns()
);
}
co_invoices
表 <?php
$references = $connection->describeReferences('co_invoices');
foreach ($references as $reference) {
print_r(
$reference->getReferencedColumns()
);
}
表描述与 MySQL 的DESCRIBE
命令非常相似,它包含以下信息:
字段 | 类型 | 键 | 允许空值 |
---|---|---|---|
字段名称 | 列类型 | 该列是否是主键或索引的一部分? | 该列是否允许空值? |
每个受支持的数据库系统也都实现了获取视图信息的方法:
获取数据库上的phalcon_db
数据库 检查数据库中是否存在视图vw_invoices
在数据库中 创建¶
不同的数据库系统(MySQL、PostgreSQL 等)提供了使用诸如CREATE
, ALTER
或DROP
这样的命令来创建、修改或删除表的功能。具体的 SQL 语法取决于所使用的数据库系统。Phalcon\Db
适配器提供了一个统一的接口用于修改表,而无需根据目标存储系统区分 SQL 语法。
下面展示了一个如何创建表的示例:
<?php
use \Phalcon\Db\Column as Column;
$connection->createTable(
'co_invoices',
null,
[
'columns' => [
new Column(
'inv_id',
[
'type' => Column::TYPE_INTEGER,
'size' => 10,
'notNull' => true,
'autoIncrement' => true,
'primary' => true,
]
),
new Column(
'inv_cst_id',
[
'type' => Column::TYPE_INTEGER,
'size' => 11,
'notNull' => true,
]
),
new Column(
'inv_title',
[
'type' => Column::TYPE_VARCHAR,
'size' => 100,
'notNull' => true,
]
),
]
]
);
The createTable
方法接受一个描述表的关联数组。列通过Phalcon\Db\Column类进行定义。下表显示了可用于定义列的选项:
选项 | 描述 | 可选的 |
---|---|---|
after | 该列必须放在指定列之后 | 是 |
autoIncrement | 设置此列是否将由数据库自动递增。表中只能有一个列具有此属性。 | 是 |
bind | 其中之一的BIND_TYPE_* 常量,说明保存前列应该如何绑定 | 是 |
default | 默认值(当与'notNull' => true )。 | 是 |
first | 该列必须放在列顺序的第一位 | 是 |
notNull | 该列可以存储空值 | 是 |
primary | true 如果该列是表主键的一部分 | 是 |
scale | DECIMAL 或NUMBER 列可能有小数位数来指定应存储多少位小数 | 是 |
size | 某些列类型如VARCHAR 或INTEGER 可能具有特定大小 | 是 |
type | 列的类型。必须是一个Phalcon\Db\Column常量(下面会列出可用常量) | 否 |
unsigned | INTEGER 列可能是signed 或unsigned 。此选项不适用于其他类型的列 | 是 |
适配器支持以下数据库列类型:
Phalcon\Db\Column::TYPE_INTEGER
Phalcon\Db\Column::TYPE_DATE
Phalcon\Db\Column::TYPE_VARCHAR
Phalcon\Db\Column::TYPE_DECIMAL
Phalcon\Db\Column::TYPE_DATETIME
Phalcon\Db\Column::TYPE_CHAR
Phalcon\Db\Column::TYPE_TEXT
传递给createTable()
can have the following keys:
Index | 描述 | 可选的 |
---|---|---|
columns | 包含使用Phalcon\Db\Column | 否 |
indexes | 包含使用Phalcon\Db\Index | 是 |
references | 包含使用Phalcon\Db\Reference | 是 |
options | 包含创建选项的数组。(特定于数据库系统) | 是 |
修改¶
随着应用程序的增长,你可能需要作为重构或添加新功能的一部分对数据库结构进行更改。并非所有数据库系统都允许你修改现有列或在两个已有列之间插入新列。Phalcon\Db就受到这些限制的制约。
<?php
use Phalcon\Db\Column as Column;
$connection->addColumn(
'co_invoices',
null,
new Column(
'inv_status_flag',
[
'type' => Column::TYPE_INTEGER,
'size' => 1,
'notNull' => true,
'default' => 0,
'after' => 'inv_cst_id',
]
)
);
$connection->modifyColumn(
'co_invoices',
null,
new Column(
'inv_status_flag',
[
'type' => Column::TYPE_INTEGER,
'size' => 2,
'notNull' => true,
]
)
);
$connection->dropColumn(
'co_invoices',
null,
'inv_status_flag'
);
删除¶
要从当前数据库中删除一个已存在的表,请使用dropTable
方法。若要从自定义数据库中删除表,可以通过第二个参数设置数据库名称。
co_invoices
中删除表 删除表co_invoices
从数据库phalcon_db