Nette 框架 - Nette\Database\ResultSet 仅实现一种迭代器

Nette framework - Nette\Database\ResultSet implements only one way iterator

我遇到了 foreach 问题。 如果在第一个 foreach {foreach $children as $child} 中返回了一个结果,那么它就起作用了。多了就报错: Nette\Database\ResultSet 只实现一种方式的迭代器。

错误可能是第一个($children)和第二个($invoices)foreach在同一个tablechildern中被查询引起的。我需要列出所有 children(例如 $child->firstName)并为每个分配项目(例如 $invoice->date $invoice->snackName)。但是,我无法取消查询中的“JOIN ON children”。

输出:

child 1 个名字

child 2 姓名

不知道怎么了? 谢谢

public function actionShow($year, $month)
 {
    $invoices = $this->database->query('
    SELECT
        o.date AS date,
        o.snack AS snack
    FROM
        diet_orders o
    LEFT JOIN children ch ON o.child_id = ch.id
    WHERE
        ch.user_id = ?
        and YEAR (o.date) = ?
        and MONTH (o.date) = ?
            ', $this->getUser()->id, $year, $month,'');
            $children = $this->database->table('children')->where('user_id = ?', $this->getUser()->id);
    
    $this->template->invoices = $invoices;
    $this->template->children = $children;
}

拿铁

{block content}
{foreach $children as $child}
{$child->fisrtName}
    {foreach $invoices as $invoice}
      {invoice->snack}
     {/foreach}
{/foreach}

{/block}

你的推理是正确的。大多数 Nette\Database 方法返回的 ResultSet 是一种单向迭代器,因此您不能在循环体的多次执行中使用它。这是一种优化——一旦循环推进迭代器,前一行可能会被垃圾收集,这对于在大型数据库 tables.

上节省内存很有用

如果您确实需要多次迭代 table,您可以:

  • 多次创建$invoices
    • 可能将整个赋值移动到模板内的循环中
    • 或创建函数返回该结果并在模板中调用它
  • 或将整个 ResultSet 加载到内存中,例如将其转换为数组。这也是 documentation 推荐的:

    Over the ResultSet is possible to iterate only once, if we need to iterate multiple times, it is necessary to convert the result to the array via fetchAll() method.

但最好的解决方案是修复它以使用 JOINs,如果可能的话。如果您在数据库中使用 FOREIGN KEYS,则可以避免编写原始 SQL 并使用 much nicer interface,当您尝试访问时,它会透明高效地为您创建 JOIN通过具有外键约束的列来自其他 table 的数据。