Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

implementing multi-level "iterator" in PHP

I'm trying to create a iterator like this one, for a list of comments:

// the iterator class, pretty much the same as the one from the php docs...
abstract class MyIterator implements Iterator{

  public $position = 0,
         $list;

  public function __construct($list) {
    $this->list = $list;
    $this->position = 0;
  }

  public function rewind() {
    $this->position = 0;
  }

  public function current() {
    return $this->list[$this->position];
  }

  public function key() {
    return $this->position;
  }

  public function next() {
    ++$this->position;
  }

  public function valid() {
    return isset($this->list[$this->position]);
  }
}

The comment iterator:

class MyCommentIterator extends MyIterator{

  public function current(){
    return new Comment($this->list[$this->position]);
  }    
}

And this is how I use it:

$comments = GetComments(); // gets the comments from the db
if($comments): ?>

  <ol>
    <?php foreach(new MyCommentIterator($comments) as $comment): ?>
    <li>
      <p class="author"><?php echo $comment->author(); ?></p>

      <div class="content">
        <?php echo $comment->content(); ?>
      </div>

      <!-- check for child comments and display them -->

    </li>
    <?php endforeach; ?>
  </ol>

<?php endif; ?>

So everything is working fine, besides one thing: I can't figure it out how to process nested comments :(

The $comments array returns a flat list of comments, like:

[0] => object(
    'id' => 346,
    'parent' => 0,  // top level comment
    'author' => 'John',
    'content' => 'bla bla'         
  ),

[1] => object(
    'id' => 478,
    'parent' => 346,  // child comment of the comment with id =346
    'author' => 'John',
    'content' => 'bla bla'         
  )
...

I need to somehow be able to check for child comments (on multiple levels) and insert them before the </li>'s of their parent comments...

Any ideas?

like image 551
Alex Avatar asked Jul 04 '11 12:07

Alex


3 Answers

Recursion is your friend.

displaycomment(comment):
    $html .= "<ol>" . comment->html;
    foreach comment->child:
        $html .= "<li>" . displaycomment(child) . "</li>";
    $html .= "</ol>";
    return $html;

All code appearing in this post is pseudo. Any resemblance to real code, working or broken, is purely coincidental.

like image 64
aib Avatar answered Sep 20 '22 00:09

aib


You might want to look into the RecursiveIterator Interface PHP Manual. If you extend your iterator with the methods by that interface, you're able to iterate over your comments with an instance of RecursiveIteratorIterator PHP Manual sequentially.

However as your output is a flat list, you need to take care of the logic for the levels on your own, e.g. inserting <ol> per depth up, and </ol> per depth down.

Use the flags to control the order how children are traversed.

like image 26
hakre Avatar answered Sep 17 '22 00:09

hakre


You are using a flat array, but in reality, the items of that array are a tree or hierarchical data structure.

You are basically displaying a sequential list. Maybe you should construct a tree / hierarchical data structure first, without displaying, and later display data from the tree list.

/* array */ function FlatArrayToTreeArray(/* array */ $MyFlatArray)
{
  ...
}

/* void */ function IterateTree(/* array */ $MyTreeArray)
{
  ...
}

/* void */ function Example() {
  $MyFlatArray = Array(
  0 => object(
      'id' => 346,
      'parent' => 0,  // top level comment
      'author' => 'John',
      'title' => 'Your restaurant food its too spicy',
      'content' => 'bla bla'         
    ),
  1 => object(
      'id' => 478,
      'parent' => 346,  // child comment of the comment with id =346
      'author' => 'Mike',
      'title' => 'Re: Your restaurant food its too spicy',
      'content' => 'bla bla'         
    ),  
  2 => object(
      'id' => 479,
      'parent' => 478,  // child comment of the comment with id =346
      'author' => 'John',
      'title' => 'Re: Your restaurant food its too spicy',
      'content' => 'bla bla'         
    ),  
  3 => object(
      'id' => 479,
      'parent' => 346,  // child comment of the comment with id =346
      'author' => 'Jane',
      'title' => 'Re: Your restaurant food its too spicy',
      'content' => 'bla bla'         
    )
  );

  $MyTreeArray = FlatArrayToTreeArray($myflatarray);

  IterateTree($MyTreeArray);
} // function Example()

Cheers.

like image 21
umlcat Avatar answered Sep 17 '22 00:09

umlcat