既然不推荐使用 fetchAll() 和 FetchMode,如何将结果提取到自定义对象中?
How to fetch results into a custom object now that fetchAll() and FetchMode are deprecated?
在一些现有代码上,我有以下语句(经过相当长的查询构建练习):
return $statement->fetchAll(
DBAL\FetchMode::CUSTOM_OBJECT,
PublishedLead::class
);
这有效(到目前为止),但我现在看到自 DBAL 2.11 以来 fetchAll()
和 FetchMode
都被弃用了:
// ResultStatement::fetchAll()
/*
* @deprecated Use fetchAllNumeric(), fetchAllAssociative()
* or fetchFirstColumn() instead.
*/
// FetchMode
/*
* @deprecated Use one of the fetch- or iterate-related
* methods on the Statement
*/
为了使我的代码尽可能向前兼容,如何编写此代码以将结果提取到自定义对象中?我是否必须根据结果编写自定义水化逻辑,或者 DBAL 可以为我做这个?
DBAL 3
API 对 DBAL 3 进行了一些重大更改。值得注意的是对于这个答案,语句不再 re-used 用于存储和访问结果,因此执行语句然后简单地循环它是不再可能。与已执行的 Statement
不同,从 $statement->execute()
编辑的 Result
对象 return 不是 Traversable
,因此下面建议的代码必须更改为显式调用 $result->fetchAllAssociative
在循环之前(或在 foreach 语句中),但在其他方面仍然兼容(此时变量只是具有不同的类型):
function getDatabaseResult(): Generator { // change return type hint, if applicable
// rest of your function/method
$result = $statement->execute(); // or ->execute($values);
foreach ($result->fetchAllAssociative() as $row) {
yield PublishedLead::fromArray($row);
}
}
DBAL 2
据我通读 DBAL 源代码可以看出,通常不推荐使用获取模式,而应该使用提供的辅助方法,这将结果限制为数字或关联数组。
这意味着将结果编组到您自己的 class 的过程现在可能应该在 DBAL 之外处理。这可能是促进使用 Doctrine ORM 的战术决定,或者他们可能只是想关注名称中的内容(抽象数据库访问)并忽略与该任务无关的内容。无论哪种方式,编写自定义保湿逻辑实际上并不那么复杂,您基本上可以编写一个提供静态方法 fromArray($data)
的 Trait,该方法遍历数组并设置所有对象属性,然后 returns对象(参见 the answer to the corresponding question)。在要从关联数组构建的所有 classes 中使用此特征。
我假设您在某个时刻循环访问您的对象数组,因此您实际上可以将您的函数变成一个生成器。如果您最终使用 foreach
来遍历您的结果集,这甚至不需要对使用该结果的代码进行任何更改。这意味着用以下循环替换您的 return 语句:
foreach ($statement as $row) {
yield PublishedLead::fromArray($row);
}
如果您不熟悉生成器,这会将您的函数变成 return 一个 \Generator
,它可以像 foreach 中的数组一样使用,但实际上并不需要整个内存 space 来保存所有数据。相反,每当需要下一个值时,原始函数的执行将恢复,直到到达下一个 yield 语句,此时生成的值被 returned 并立即使用。
此外,如果您想知道,该语句实际上实现了 Traversable
,因此您可以在从 execute
获取它后直接 foreach
直接通过它,而无需实际调用任何获取方法,这就是我在上面的示例中所做的; $row
将是一个关联数组,或者更准确地说,是从 \PDO::FETCH_BOTH
默认获取模式获得的数组。
这是一个完整的原型:
<?php
// newly created
trait FromArrayTrait {
public static function fromArray(array $data = []): self {
foreach (get_object_vars($obj = new self) as $property => $default) {
$obj->$property = $data[$property] ?? $default;
}
return $obj;
}
}
class PublishedLead {
use FromArrayTrait; // add this line
// rest of your class
}
function getDatabaseResult(): Generator { // change return type hint, if applicable
// rest of your function/method
// the following 3 lines replace 'return $statement->fetchAll(...);'
foreach ($statement as $row) {
yield PublishedLead::fromArray($row);
}
}
// your actual main code, this is unchanged assuming you already use foreach
foreach (getDatabaseResult() as $lead) {
$lead->doSomething();
}
显然要考虑 namespaces 并将这些部分放在代码中应有的位置。顺便说一句,我稍微更改了 fromArray
方法,以便它在数组值为 null 的情况下使用默认值。如果您真的希望能够用 null 替换默认值,请恢复到上面链接的原始版本。如果你想设置动态属性,即使它们没有在你的 class 中明确声明,循环通过 $data
而不是 get_object_vars()
:
public static function fromArrayDynamic(iterable $data = []): self {
$obj = new self;
foreach ($data as $property => $value) {
$obj->$property = $value;
}
return $obj;
}
当然,如果数组中包含空值,这将用空值覆盖默认值。作为奖励,这个与 iterable
输入兼容,因此它不仅可以接受数组,还可以与生成器和可遍历器一起使用。
在一些现有代码上,我有以下语句(经过相当长的查询构建练习):
return $statement->fetchAll(
DBAL\FetchMode::CUSTOM_OBJECT,
PublishedLead::class
);
这有效(到目前为止),但我现在看到自 DBAL 2.11 以来 fetchAll()
和 FetchMode
都被弃用了:
// ResultStatement::fetchAll()
/*
* @deprecated Use fetchAllNumeric(), fetchAllAssociative()
* or fetchFirstColumn() instead.
*/
// FetchMode
/*
* @deprecated Use one of the fetch- or iterate-related
* methods on the Statement
*/
为了使我的代码尽可能向前兼容,如何编写此代码以将结果提取到自定义对象中?我是否必须根据结果编写自定义水化逻辑,或者 DBAL 可以为我做这个?
DBAL 3
API 对 DBAL 3 进行了一些重大更改。值得注意的是对于这个答案,语句不再 re-used 用于存储和访问结果,因此执行语句然后简单地循环它是不再可能。与已执行的 Statement
不同,从 $statement->execute()
编辑的 Result
对象 return 不是 Traversable
,因此下面建议的代码必须更改为显式调用 $result->fetchAllAssociative
在循环之前(或在 foreach 语句中),但在其他方面仍然兼容(此时变量只是具有不同的类型):
function getDatabaseResult(): Generator { // change return type hint, if applicable
// rest of your function/method
$result = $statement->execute(); // or ->execute($values);
foreach ($result->fetchAllAssociative() as $row) {
yield PublishedLead::fromArray($row);
}
}
DBAL 2
据我通读 DBAL 源代码可以看出,通常不推荐使用获取模式,而应该使用提供的辅助方法,这将结果限制为数字或关联数组。
这意味着将结果编组到您自己的 class 的过程现在可能应该在 DBAL 之外处理。这可能是促进使用 Doctrine ORM 的战术决定,或者他们可能只是想关注名称中的内容(抽象数据库访问)并忽略与该任务无关的内容。无论哪种方式,编写自定义保湿逻辑实际上并不那么复杂,您基本上可以编写一个提供静态方法 fromArray($data)
的 Trait,该方法遍历数组并设置所有对象属性,然后 returns对象(参见 the answer to the corresponding question)。在要从关联数组构建的所有 classes 中使用此特征。
我假设您在某个时刻循环访问您的对象数组,因此您实际上可以将您的函数变成一个生成器。如果您最终使用 foreach
来遍历您的结果集,这甚至不需要对使用该结果的代码进行任何更改。这意味着用以下循环替换您的 return 语句:
foreach ($statement as $row) {
yield PublishedLead::fromArray($row);
}
如果您不熟悉生成器,这会将您的函数变成 return 一个 \Generator
,它可以像 foreach 中的数组一样使用,但实际上并不需要整个内存 space 来保存所有数据。相反,每当需要下一个值时,原始函数的执行将恢复,直到到达下一个 yield 语句,此时生成的值被 returned 并立即使用。
此外,如果您想知道,该语句实际上实现了 Traversable
,因此您可以在从 execute
获取它后直接 foreach
直接通过它,而无需实际调用任何获取方法,这就是我在上面的示例中所做的; $row
将是一个关联数组,或者更准确地说,是从 \PDO::FETCH_BOTH
默认获取模式获得的数组。
这是一个完整的原型:
<?php
// newly created
trait FromArrayTrait {
public static function fromArray(array $data = []): self {
foreach (get_object_vars($obj = new self) as $property => $default) {
$obj->$property = $data[$property] ?? $default;
}
return $obj;
}
}
class PublishedLead {
use FromArrayTrait; // add this line
// rest of your class
}
function getDatabaseResult(): Generator { // change return type hint, if applicable
// rest of your function/method
// the following 3 lines replace 'return $statement->fetchAll(...);'
foreach ($statement as $row) {
yield PublishedLead::fromArray($row);
}
}
// your actual main code, this is unchanged assuming you already use foreach
foreach (getDatabaseResult() as $lead) {
$lead->doSomething();
}
显然要考虑 namespaces 并将这些部分放在代码中应有的位置。顺便说一句,我稍微更改了 fromArray
方法,以便它在数组值为 null 的情况下使用默认值。如果您真的希望能够用 null 替换默认值,请恢复到上面链接的原始版本。如果你想设置动态属性,即使它们没有在你的 class 中明确声明,循环通过 $data
而不是 get_object_vars()
:
public static function fromArrayDynamic(iterable $data = []): self {
$obj = new self;
foreach ($data as $property => $value) {
$obj->$property = $value;
}
return $obj;
}
当然,如果数组中包含空值,这将用空值覆盖默认值。作为奖励,这个与 iterable
输入兼容,因此它不仅可以接受数组,还可以与生成器和可遍历器一起使用。