如何使用 CakePHP 查询生成器生成 SQL 函数调用?

How to generate SQL function calls with the CakePHP query builder?

我有一个作者的全名列,想将姓氏提取到另一列中。我使用以下原始 SQL:

SELECT name,
SUBSTRING_INDEX(`name`, ' ', -1) AS `surname`
FROM qr.authors;

输出:

食谱"Using SQL Functions"下说:

In addition to the above functions, the func() method can be used to create any generic SQL function such as year, date_format, convert, etc.

但是如何通过 func() 方法创建这个 SUBSTRING_INDEX 函数,以便我可以将它与 CakePHP 查询生成器一起使用?

函数生成器附带预定义 methods/functions

FunctionsBuilder class 附带了一堆现成的 methods/functions 供您使用,例如 sum(), count(), concat()dateDiff()now() 等。您可以找到受支持函数的完整列表以及有关如何使用它们的示例 in the Cookbook and the API docs

任意函数都可以通过调用它们来构建

FunctionsBuilder class 使用魔术方法 __call 处理程序来构建任意 SQL 函数表达式,因此如果您的函数没有现成的方法,你可以 "call" 你的 SQL 函数:

$query = $this->SomeTable->find();

$func = $query->func()->substring_index([
    'name' => 'identifier',
    ' ',
    -1 => 'literal'
]);
$query->select([/* ... */, 'surname' => $func]);

这应该大部分是不言自明的,神奇的方法名称是 SQL 函数名称,传递的数组包含应该传递给函数的参数,在本例中是第一个和最后一个参数被定义为分别被视为标识符作为文字,因此两者都被直接插入到查询中,即不是作为将被转义的绑定参数!

标识符一将另外受到可能的自动标识符引用,即 name 将被转换为例如 `name`"name"[name],具体取决于正在使用的数据库驱动程序。第二个参数也可以是文字(通过传递例如 '" "'),我只是为了示例目的没有将它设置为一个。不这样做将导致该值为 bound/casted 作为字符串。

编译后的结果 SQL 看起来像这样:

substring_index(name, :c0, -1)

最终会被执行为

substring_index(name, ' ', -1) 

处理非硬编码数据,例如用户输入

使用非硬编码数据(即动态数据或可能发生变化的数据)时,请确保在必要时在第二个参数中为 casting/escaping 定义正确的类型,例如 integerdatetime 等。为了使其正常工作,您必须对列名称值使用标识符表达式,否则在使用 [=30 时将忽略第二个参数=]语法:

$func = $query->func()->substring_index(
    [
        new \Cake\Database\Expression\IdentifierExpression('title'),
        ' ',
        $userInput,
    ],
    [
        null,     // no typecasting for the first argument
        'string', // second argument will be bound/casted as string
        'integer' // third argument will be bound/casted as integer
    ]
);

类型将通过数字索引进行匹配,第一个将被忽略,因为它是一个表达式,因此只传递 null.

您甚至可以使用原始表达式

在您的情况下,您传递的是安全的硬编码值,这些值不需要作为绑定参数插入到查询中,并且 SUBSTRING_INDEX 不是任何函数所涵盖的函数在 CakePHP 附带的方言中,您甚至可以使用原始查询来代替 - 但是您将失去在自定义方言中转换表达式的能力,并且自动标识符引用也将不再适用,所以只有在您知道什么的情况下才这样做你在做!

$query->newExpr('SUBSTRING_INDEX(`name`, "", -1)')

另见