typo3 - 使用未引用的命名参数时,TYPO3 抛出异常
问题描述
我正在尝试使用未引用的参数(案例 1、1A)以类似 PDO 的样式使用准备好的语句执行原始查询,无论如何它都会引发异常:
执行 'SELECT * FROM pages WHERE title LIKE :title' 时发生异常:您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以在第 1 行的 ':title' 附近使用正确的语法
此外,引用命名参数不起作用(案例 2),它不会引发异常,但也找不到任何东西。
根据需要使用未命名/编号和未引用的参数(案例 3、3A)或executeQuery()
代替prepare()
(案例 4)。特别是我想使用命名参数,最后一个是我的选择。
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
...
public function queryPagesByTitle(string $title = null): array
{
/** @var Connection $conn */
$conn = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages');
// Case 1: DOESN'T work with non-quoted params
$stmt = $conn->prepare("SELECT * FROM pages WHERE title LIKE :title");
$stmt->execute(['title' => $title]);
// Case 1A: DOESN'T work with non-quoted params
$stmt = $conn->prepare("SELECT * FROM pages WHERE title LIKE :title");
$stmt->bindValue('title', $title, \PDO::PARAM_STR);
$stmt->execute();
// Case 1B: DOESN'T work with non-quoted,unique params
$stmt = $conn->prepare("SELECT * FROM pages WHERE title LIKE :dcUniqueParam");
$stmt->bindParam('dcUniqueParam', $title, \PDO::PARAM_STR);
$stmt->execute();
// Case 1C: DOESN'T work with non-quoted,unique params even with :colon while binding
$stmt = $conn->prepare("SELECT * FROM pages WHERE title LIKE :dcUniqueParam");
$stmt->bindParam(':dcUniqueParam', $title, \PDO::PARAM_STR);
// Case 2: DOESN'T work with quoted params neither, doesn't throw an exception, but doesn;t find anything
$stmt = $conn->prepare("SELECT * FROM pages WHERE title LIKE ':title'");
$stmt->execute(['title' => $title]);
// Case 3: Works with numbered param(s)
$stmt = $conn->prepare("SELECT * FROM pages WHERE title LIKE ?");
$stmt->execute([1 => $title]);
// Case 3A: Works with numbered param(s)
$stmt = $conn->prepare("SELECT * FROM pages WHERE title LIKE ?");
$stmt->bindParam(1, $title, \PDO::PARAM_STR);
$stmt->execute();
// Case 4: Works with non-quoted named param(s)
$stmt = $conn->executeQuery(
"SELECT uid, title FROM pages WHERE title LIKE :title",
['title' => $title],
[\PDO::PARAM_STR]
);
return $stmt->fetchAll(FetchMode::ASSOCIATIVE);
}
几个问题
- 为什么第一个案例在 PDO 继承之后不能像我预期的那样工作,或者 Doctrine实际上是如何做到的?
executeQuery()
使用而不是有一些缺点(如果有的话)prepare()
吗?- 我应该使用
prepare()
带编号的参数吗? - 使用原始查询而不是 QueryBuilder 之间有什么显着区别吗?
笔记
我知道,为了正确使用模型数据和存储库,我可以/应该使用通用的 QueryBuilder 接口。这种情况适用于我的数据库中不使用数据映射的一些原始数据,我正在寻找一些性能改进。pages
表在这里仅用于演示概念。
解决方案
最后,这一切都归结为类似 PDO 的语句,但是使用mysqli
作为驱动程序(https://www.php.net/manual/en/mysqli.quickstart.prepared-statements.php)或pdo_mysql
作为驱动程序(https://www.php.net/manual/en/pdo.prepared-statements.php)。
PDO 文档( https://www.php.net/manual/en/pdo.prepare.php )中提到了重要的方面:
PDO 将为本身不支持它们的驱动程序模拟准备好的语句/绑定参数,并且如果驱动程序支持一种样式但不支持另一种样式,还可以将命名或问号样式参数标记重写为更合适的内容。
原始问题中给出的代码片段在pdo_mysql
用作驱动程序时有效 - 而不是mysqli
可以在typo3conf/LocalConfiguration.php
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['driver'] = 'pdo_mysql';
现在关注 Doctrine DBAL 的内部细节,它也只是 or 的一个包装器mysqli
——在pdo_mysql
内部 DBAL 使用定位参数?
并相应地转换命名参数。
实际上,这发生在Doctrine DBAL 的 Connection中——命名参数被转换为定位参数(无论使用了哪个数据库驱动程序):
SELECT * FROM `pages` WHERE `title` LIKE :dcValue1
转换为
SELECT * FROM `pages` WHERE `title` LIKE ?
概括
- DBAL 的内部
Connection::executeQuery
使用Connection::prepare
,请参阅https://github.com/doctrine/dbal/blob/2.10.x/lib/Doctrine/DBAL/Connection.php#L886 - DBAL
?
也使用定位/编号参数 - 命名参数:dcValue
只是“虚拟” - using
$stmt->bindParam
直接不执行该转换过程, usingConnection::executeQuery
并解释了为什么剪断工作
除此之外,由于您已经处于 TYPO3 环境中,您可能希望使用它QueryBuilder
,它也在内部使用准备好的语句。
public function queryPagesByTitle(string $title = null): array
{
$builder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('page');
$stmt = $builder->select('*')
->from('pages')
->where($builder->expr()->like(
'title',
$builder->createNamedParameter(
'%' . $builder->escapeLikeWildcards($title) . '%',
\PDO::PARAM_STR
)
))
->execute();
return $stmt->fetchAll(FetchMode::ASSOCIATIVE) ?? [];
}
推荐阅读
- r - Rcpp 使用外部和 pmax
- c++ - 类/结构内外的模板参数
- scala - 从案例类的 Seq 生成单个案例类
- ruby-on-rails - 如何在rails中使用有很多条件
- qt - 使用控制 + 加号和控制 - 减号进行缩放?
- python - 基于使用 Seaborn 或 Matplotlib 的两个不同 pandas DataFrame(具有相同结构)的记录的子图
- c++ - 如何使用 Win32 为 MDI 应用程序的标题设置文本颜色?
- node.js - 在 MongoDB 中保存纳秒时间戳(由 Go 生成,使用 Node.js 保存)
- python - 已知短语前后的正则表达式条件
- c# - 使用 EWS 下载附件时有没有办法扫描病毒?