DBAL 3
随着DBAL 3的发布,API发生了一些重大变化。特别是对于这个答案来说,语句不再被重复使用以存储和访问结果,因此执行语句,然后简单地循环遍历它已经不再可行。与已执行的Statement
不同,从$statement->execute()
返回的Result
对象不是可迭代的,因此建议下面的代码在循环之前(或直接在foreach语句中)显式调用$result->fetchAllAssociative
,但否则仍然兼容(变量在那时只是具有不同的类型):
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源代码中了解到的情况,一般来说使用fetch模式已经被弃用,应该使用提供的帮助方法来限制结果为数字或关联数组。
这意味着现在将结果转换为自己的类的过程可能应该在DBAL之外处理。这可能是一种策略性决定,以促进Doctrine ORM的使用,或者他们只想专注于名字中的内容(抽象数据库访问),并留出与该任务没有真正相关的事情。无论哪种方式,编写自定义的数据填充逻辑实际上并不那么复杂,你可以基本上只需编写一个提供静态方法fromArray($data)
的Trait,该方法遍历数组并设置所有对象属性,然后返回对象(参见对应问题的答案)。在所有要从关联数组构建的类中使用此trait。
我假设你在某个时候会循环遍历你的对象数组,因此你实际上可以将你的函数转换为生成器。如果你最终使用foreach
来遍历你的结果集,这甚至不需要在使用结果的代码中进行任何更改。这意味着将你的返回语句替换为以下循环:
foreach ($statement as $row) {
yield PublishedLead::fromArray($row);
}
如果您对生成器不熟悉,这将使您的函数变成一个返回\Generator
的函数,可以在foreach
中像数组一样使用,但实际上并不占用整个内存空间以保存所有数据。相反,只有在需要下一个值时,才会恢复原始函数的执行,直到达到下一个yield
语句,在此时返回并立即使用产生的值。
此外,如果您好奇,该语句确实实现了Traversable
,因此在从execute
获取它后,您可以直接通过foreach
进行遍历,而无需调用任何提取方法,这就是我在上面示例中所做的;$row
将是关联数组或更准确地说是从\PDO::FETCH_BOTH
默认提取模式获得的数组。
以下是完整的原型:
<?php
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;
}
function getDatabaseResult(): Generator {
foreach ($statement as $row) {
yield PublishedLead::fromArray($row);
}
}
foreach (getDatabaseResult() as $lead) {
$lead->doSomething();
}
显然要考虑命名空间,并将这些部分放在代码中应该放置的位置。顺便说一下,我稍微修改了fromArray
方法,所以它在数组值为空的情况下使用默认值。如果您实际上想要能够将默认值替换为null,请恢复到上面链接的原始版本。如果您想设置动态属性,即使它们没有在您的类中明确声明,请通过循环$data
而不是get_object_vars()
:
public static function fromArrayDynamic(iterable $data = []): self {
$obj = new self;
foreach ($data as $property => $value) {
$obj->$property = $value;
}
return $obj;
}
当然,如果数组中包含null,则这个函数将用null覆盖默认值。作为额外的好处,该函数兼容可迭代输入(iterable
),因此它不仅可以使用数组,还可以使用生成器和可遍历对象。