2011-11-08 66 views
3

朋友。我知道,这些迭代器已经有很多问题了。 我读过一些东西,我不是初学者...但我的思想有点卡住了。请帮助我理解我在实践中如何使用迭代器。使用PHP迭代器

假设,我有一个ORM对象可以从数据库中选择实例。和一个实例包含字段,并可以插入,uodate等照常。 我想遍历一个类型的所有对象,但由于可以有很多这样的对象,所以我更喜欢用“页面”来选择它们。我的代码:

$limit = 100; 
$offset = 0; 
do 
{ 
    $recs = $orm->select($filter, $sorting, $limit , $offset); 
    $offset += $limit; 
    foreach ($recs as $rec) 
    { 
    // doing something with record 
    } 
} 
while (count($recs) == $limit); 

我觉得迭代器模式在这里什么适合,但什么接口是更好的在这种情况下实施或者一些基础SPL类?

UPDATE 理想代码上面迭代可能看起来像:

$iterator = new ORMPagedIterator($ormobject, $filter, $sorting); 
foreach ($iterator as $rec) 
{ 
    // do something with record 
} 

例如所有那些逐页行为都在迭代器中。

+0

您可能想详细说明您的场景。我不明白你的意思是“我喜欢按页面选择它们”。此外,查看http://stackoverflow.com/questions/1957133/php-spl-reference-documentation各种SPL相关资源。 – Gordon

+0

“通过页面”的意思是“通过某些部分”,而不是“一个请求中的所有”。其实,现在我没有太多时间去阅读大量的文档,所以我希望得到一个好的建议。 – dmitry

+0

AFAIK通常在服务器上提取更多数据比在同一查询中运行更多次更容易 – max4ever

回答

4

一旦达到以前的迭代结束时,我会使用迭代另一个迭代器,并请求下一个迭代的迭代器......好吧,听起来莫复杂得多,它实际上是:

<?php 
$limit = 100; 
$offset = 0; 

$iter = new NextIteratorCallbackIterator(function($i) use ($orm, $limit, &$offset) { 
    printf("selecting next bunch at offset %d\n", $offset); 
    $recs = $orm->select($filter, $sorting, $limit , $offset); 
    $offset += $limit; 
    if ($recs) { 
     return new ArrayIterator($recs); 
    } 
    return null; // end reached 
}); 

foreach ($iter as $rec) { 
    // do something with record 
} 
?> 

这里是NextIteratorCallbackIterator的样本实现:

<?php 
class NextIteratorCallbackIterator implements Iterator { 
    private $_iterator = null; 
    private $_count = 0; 
    private $_callback; 

    public function __construct($callback) { 
     if (!is_callable($callback)) { 
      throw new Exception(__CLASS__.": callback must be callable"); 
     } 
     $this->_callback = $callback; 
    } 

    public function current() { 
     return $this->_iterator !== null ? $this->_iterator->current() : null; 
    } 

    public function key() { 
     return $this->_iterator !== null ? $this->_iterator->key() : null; 
    } 

    public function next() { 
     $tryNext = ($this->_iterator === null); 
     do { 
      if ($tryNext) { 
       $tryNext = false; 
       $this->_iterator = call_user_func($this->_callback, ++$this->_count); 
      } 
      elseif ($this->_iterator !== null) { 
       $this->_iterator->next(); 
       if ($this->_iterator->valid() == false) { 
        $tryNext = true; 
       } 
      } 
     } while ($tryNext); 
    } 

    public function rewind() { 
     $this->_iterator = call_user_func($this->_callback, $this->_count = 0); 
    } 

    public function valid() { 
     return $this->_iterator !== null; 
    } 
} 
?> 

UPDATE:你ORMPagedIterator可以使用NextIteratorCallbackIterator一样容易实现:

<?php 
class ORMPagedIterator implements IteratorAggregate { 
    function __construct($orm, $filter, $sorting, $chunksize = 100) { 
     $this->orm = $orm; 
     $this->filter = $filter; 
     $this->sorting = $sorting; 
     $this->chunksize = $chunksize; 
    } 

    function iteratorNext($i) { 
     $offset = $this->chunksize * $i; 
     $recs = $this->orm->select($this->filter, $this->sorting, $this->chunksize, $offset); 
     if ($recs) { 
      return new ArrayIterator($recs); 
     } 
     return null; // end reached 
    } 

    function getIterator() { 
     return new NextIteratorCallbackIterator(array($this,"iteratorNext")); 
    } 
} 
?> 
+0

嗯,这很好,谢谢。 – dmitry

+1

我想补充一点,你应该避免选择大的偏移量,因为随着偏移量的增加,你的数据库将不得不做更多的工作来获得结果。如果你的结果集是通过它的主键排序的,你应该使用前一个结果集的最后一个键作为下一个的开始键......这样你的查询就不会变慢...... – muhqu