跳转到内容

数据库抽象层


概览

下的组件Phalcon\Db命名空间中的类负责提供框架中MVC的Phalcon\Mvc\Model类——框架中ModelMVC部分的核心数据库支持。它是一个完全用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 addColumn(
    string $tableName, 
    string $schemaName, 
    ColumnInterface $column
): bool
向表中添加一列

public function addIndex(
    string $tableName, 
    string $schemaName,
    IndexInterface $index
): bool
向表中添加索引

public function addForeignKey(
    string $tableName, 
    string $schemaName, 
    ReferenceInterface $reference
): bool
向表中添加外键

public function addPrimaryKey(
    string $tableName, 
    string $schemaName, 
    IndexInterface $index
): bool
向表中添加主键

public function affectedRows(): int
返回最后一次INSERT/UPDATE/DELETE操作影响的行数

public function begin(
    bool $nesting = true
): bool
在连接中启动一个事务

public function close(): void
关闭活动连接并返回成功状态。Phalcon 会自动关闭和销毁活动连接

public function commit(
    bool $nesting = true
): bool
提交连接中的活动事务

public function connect(
    array $descriptor = []
): void
此方法在Phalcon\Db\Adapter\Pdo\AbstractPdo构造函数中被自动调用。当你需要恢复数据库连接时调用它

public function createSavepoint(
    string $name
): bool
创建一个新的保存点

public function createTable(
    string $tableName, 
    string $schemaName, 
    array $definition
): bool
创建一个表

public function createView(
    string $viewName, 
    array $definition, 
    string $schemaName = null
): bool
创建视图

public function delete(
    mixed $table, 
    string $whereCondition = null, 
    array $placeholders = [], 
    array $dataTypes = []
): bool
使用自定义 RDBMS SQL 语法从表中删除数据

public function describeColumns(
    string $table, 
    string $schema = null
): ColumnInterface[]
返回描述表的 Phalcon\Db\Column 对象数组

public function describeIndexes(
    string $table, 
        string $schema = null
): IndexInterface[]
列出表的索引

public function describeReferences(
    string $table, 
    string $schema = null
): ReferenceInterface[]
列出表的引用

public function dropColumn(
    string $tableName, 
    string $schemaName, 
    string $columnName
): bool
从表中删除一列

public function dropForeignKey(
    string $tableName, 
    string $schemaName, 
    string $referenceName
): bool
从表中删除外键

public function dropIndex(
    string $tableName, 
    string $schemaName, 
    string $indexName
): bool
从表中删除索引

public function dropPrimaryKey(
    string $tableName, 
    string $schemaName
): 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 escapeIdentifier(
    mixed identifier
): string
转义列/表/模式名称。

public function escapeString(string $str): string
转义一个值以避免 SQL 注入

public function execute(
    string $sqlStatement, 
    array $bindParams = [], 
    array $bindTypes = []
): bool
向数据库服务器发送SQL语句并返回执行成功状态。当发送到服务器的SQL语句不返回任何行时使用此方法

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
返回SQL查询结果第一行的第n个字段

$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
返回SQL查询结果的第一行

public function forUpdate(
    string $sqlQuery
): string
返回带有FOR UPDATE子句修改后的SQL

public function getColumnDefinition(
    ColumnInterface $column
): string
返回列的SQL列定义

public function getColumnList(
    mixed $columnList
): string
获取列列表

public function getConnectionId(): string
获取活动连接的唯一标识符

public function getDefaultValue(): RawValue
返回默认值以使RBDM使用表定义中声明的默认值

public function getDescriptor(): array
返回用于连接到活动数据库的描述符

public function getDialect(): DialectInterface
返回内部方言实例

public function getDialectType(): string
返回所使用方言的名称

public function getDefaultIdValue(): RawValue
返回用于插入到标识列的默认标识值

public function getErrorInfo(): array
返回最后的错误信息

public function getInternalHandler(): mixed
返回内部PDO处理器

public function getNestedTransactionSavepointName(): string
返回嵌套事务要用的保存点名称

public function getRealSQLStatement(): string
不替换已绑定参数的对象中当前SQL语句

public function getSQLStatement(): string
对象中的当前SQL语句

public function getSQLBindTypes(): array
对象中的当前SQL语句

public function getSQLVariables(): array
对象中的当前SQL语句

public function getType(): string
返回适配器所用的数据库系统类型

public function insert(
    string $table, 
    array $values, 
    mixed $fields = null, 
    mixed $dataTypes = null
): bool
使用自定义 RDBMS SQL 语法向表插入数据

public function insertAsDict(
    string $table, 
    mixed $data, 
    mixed $dataTypes = null
): bool
使用自定义 RBDM SQL 语法向表插入数据

$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 isNestedTransactionsWithSavepoints(): bool
返回是否嵌套事务应使用保存点

public function isUnderTransaction(): bool
检查连接是否处于数据库事务中

public function lastInsertId(
    mixed $sequenceName = null
): string | bool
返回最近SQL语句插入auto_increment列的插入ID

public function limit(
    string $sqlQuery, 
    int $number
): string
将LIMIT子句附加到sqlQuery参数上

public function listTables(
    string $schemaName = null
): array
列出数据库中的所有表

public function listViews(
    string $schemaName = null
): array
列出数据库中的所有视图

public function modifyColumn(
    string $tableName, 
    string $schemaName, 
    ColumnInterface $column, 
    ColumnInterface $currentColumn = null
): bool
基于定义修改表列

public function query(
    string $sqlStatement, 
    array $bindParams = [], 
    array $bindTypes = []
): ResultInterface | bool
向数据库服务器发送SQL语句并返回执行成功状态。当发送到服务器的SQL语句返回行时使用此方法

public function releaseSavepoint(
    string $name
): bool
释放给定的保存点

public function rollback(
    bool $nesting = true
): bool
回滚连接中的活动事务

public function rollbackSavepoint(
    string $name
): bool
回滚给定的保存点

public function sharedLock(
    string $sqlQuery
): string
返回带有LOCK IN SHARE MODE子句修改后的SQL

public function setNestedTransactionsWithSavepoints(
    bool $nestedTransactionsWithSavepoints
): AdapterInterface
设置是否嵌套事务应使用保存点

public function supportsDefaultValue(): bool
检查数据库系统是否支持默认值

public function supportSequences(): bool
检查数据库系统是否需要序列来生成自增数值

public function tableExists(
    string $tableName, 
    string $schemaName = null
): bool
生成SQL检查是否存在schema.table

public function tableOptions(
    string $tableName, 
    string $schemaName = null
): array
获取表的创建选项

public function update(
    string $table, 
    mixed $fields, 
    mixed $values, 
    mixed $whereCondition = null, 
    mixed $dataTypes = null
): bool
使用自定义 RDBMS SQL 语法更新表中的数据

public function updateAsDict(
    string $table, 
    mixed $data, 
    mixed $whereCondition = null, 
    mixed $dataTypes = null
): bool
使用自定义 RBDM SQL 语法更新表中的数据。另一种更方便的语法

$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

public function useExplicitIdValue(): bool
检查数据库系统是否需要为标识列提供显式值

public function viewExists(
    string $viewName, 
    string $schemaName = null
): bool
生成用于检查模式视图是否存在的 SQL

自定义

The Phalcon\Db\AdapterInterface必须实现该接口,以创建你的数据库适配器或扩展已有适配器。此外,你还可以扩展Phalcon\Db\AbstractAdapter该类已经包含了一些可复用的自定义适配器实现。

转义

转义标识符功能默认是启用的。但是,如果你需要禁用此功能,可以使用setup()方法:

<?php

\Phalcon\Db::setup(
    [
        'escapeIdentifiers' => false,
    ]
);

工厂

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);
原始 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);
原始 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
        ],
    ]
);
带有条件子句转义(替代语法)

删除

<?php

$sql     = '
DELETE 
   `co_invoices` 
WHERE
   `inv_id` = 4
';
$success = $connection->execute($sql);
原始 SQL

<?php

$sql     = '
DELETE 
   `co_invoices` 
WHERE
   `inv_id` = ?
';
$success = $connection->execute(
    $sql, 
    [
        4
    ]
);
占位符

<?php

$success = $connection->delete(
    'co_invoices',
    'inv_id = ?',
    [
        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,
    ]
);
使用命名占位符绑定

当使用数字占位符时,你需要将它们定义为整数,例如:12在这种情况下'1''2'被视为字符串而不是数字,因此占位符无法成功替换。在任何适配器中,数据都会使用以下方式自动转义:PDO 引用此函数考虑了连接的字符集,因此建议在连接参数或数据库服务器配置中定义正确的字符集,因为错误的字符集在存储或检索数据时会产生不良影响。

另外,你可以直接将参数传递给以下方法:executequery方法。在这种情况下,绑定参数会直接传递给 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,
    ]
);
使用 PDO 占位符绑定

带类型

占位符允许你绑定参数以避免 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 替你进行这种转换:

<?php

\Phalcon\Db::setup(
    [
        'forceCasting' => true,
    ]
);

根据指定的绑定类型执行以下操作:

绑定类型 动作
Column::BIND_PARAM_STR 将值转换为原生 PHP 字符串
Column::BIND_PARAM_INT 将值转换为原生 PHP 整数
Column::BIND_PARAM_BOOL 将值转换为原生 PHP 布尔值
Column::BIND_PARAM_DECIMAL 将值转换为原生 PHP 浮点数

数据填充

PDO 总是将从数据库系统返回的值表示为字符串值,无论该值属于哪种列类型。numericboolean这种情况发生的原因是一些列类型不能用相应的 PHP 原生类型表示,这是由于它们的大小限制。例如,BIGINT在 MySQL 中可以存储大整数值,这些值无法在 PHP 中表示为 32 位整数。因此,PDO 和 ORM 默认会安全地决定将所有值保留为字符串。

你可以设置 ORM 自动将这些类型转换为其对应的 PHP 原生类型:

<?php

use Phalcon\Mvc\Model;

Model::setup(
    [
        'castOnHydrate' => true,
    ]
);

这样你就可以对变量使用严格运算符或对其类型做出假设:

<?php

$invoice = Invoices::findFirst();
if (11 === $invoice->inv_id) {
    echo $invoice->inv_title;
}

注意

如果你想在使用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适配器还提供了获取有关表格和视图详细信息的方法:

<?php

$tables = $connection->listTables('phalcon_db');
获取phalcon_db数据库

<?php

$exists = $connection->tableExists('co_invoices');
检查数据库中是否存在名为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()
    );
}
打印 'co_invoices' 表上的外键

表描述与 MySQL 的DESCRIBE命令非常相似,它包含以下信息:

字段 类型 允许空值
字段名称 列类型 该列是否是主键或索引的一部分? 该列是否允许空值?

每个受支持的数据库系统也都实现了获取视图信息的方法:

<?php

$tables = $connection->listViews('phalcon_db');
获取数据库上的phalcon_db数据库

<?php

$exists = $connection->viewExists('vw_invoices');
检查数据库中是否存在视图vw_invoices在数据库中

创建

不同的数据库系统(MySQL、PostgreSQL 等)提供了使用诸如CREATE, ALTERDROP这样的命令来创建、修改或删除表的功能。具体的 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 DECIMALNUMBER列可能有小数位数来指定应存储多少位小数
size 某些列类型如VARCHARINTEGER可能具有特定大小
type 列的类型。必须是一个Phalcon\Db\Column常量(下面会列出可用常量)
unsigned INTEGER列可能是signedunsigned。此选项不适用于其他类型的列

适配器支持以下数据库列类型:

  • 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方法。若要从自定义数据库中删除表,可以通过第二个参数设置数据库名称。

<?php

$connection->dropTable('co_invoices');
删除表co_invoices中删除表

<?php

$connection->dropTable('co_invoices', 'phalcon_db');
删除表co_invoices从数据库phalcon_db

无噪 Logo
无噪文档
25 年 6 月翻译
版本号 5.9
文档源↗