0

I have the 'Article' class with the private property words which holds and array of Word class objects. I also have getWord method which can return one word:

class Article {
    private $words = Array(1 => new Word(1), 2 => new Word(2), ...);

    public function getWord($wordId) {
        if (array_key_exists($wordId, $this->words)) {
            return $this->words[$wordId];
        } else {
            return NULL;
        }
    }
}

I need to iterate through all exising words. What is the best way to do that?

Currently I'm returning all words as array using another method, but I don't think that is a good option.

Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488

2 Answers2

1

Your solution is the best an cleanest one. Making getters for private Properties is widely accepted in the OOP. See this stackoverflow entry

A getter looks like this:

public function getWords() {
  return $this->words;
}

If you would that every class can access AND edit the property you can also make it a public property. But as I understand your code other classes and methods should have read only access to this property, so a getter is the definitive best solution.

If you only want to expose some specific word objects of your property then the method would look like:

public function getFilteredWords($param) {
  $tmpWords = array();

  foreach($this->words as $w) {
    if(/*word NOT matches your filter criteria ($param)*/)
      continue;

    $tmpWords[] = $w;
  }

  return $tmpWords;
}
Community
  • 1
  • 1
TiMESPLiNTER
  • 5,741
  • 2
  • 28
  • 64
  • Thanks, but in this way I expose the inner structure of the words. Wouldn't it be better to create a variable holding the count of words and use it in loop? – Max Koretskyi Oct 30 '13 at 08:02
  • Use what in loop? Did I understand you correct that you want access the $words property (and also the Word objects instances this property is holding) of your Article class in another method in another class? – TiMESPLiNTER Oct 30 '13 at 08:06
  • Yes, that is correct. But I don't want to expose all those words. – Max Koretskyi Oct 30 '13 at 08:08
  • Okay alright, well then you have to create a getSomeWords() method where you iterate over the $words property and put the word objects you want to expose in a temporary array and return this array afterwards. – TiMESPLiNTER Oct 30 '13 at 08:10
  • Thanks, but I think that iterator option suggested by Sven is the solution. I'm trying out it now. – Max Koretskyi Oct 30 '13 at 08:11
  • Well but there you also accessing *all word objects* as he writes. I added another method in my answer above. – TiMESPLiNTER Oct 30 '13 at 08:14
  • 1
    You'd probably add a FilterIterator object and return it instead of a pure ArrayIterator. – Sven Oct 30 '13 at 17:03
1

If you want to iterate over an Article object, accessing all Word objects, you should implement either the Iterator interface, or IteratorAggregate.

If this is done, iterating would be as easy as this:

foreach ($article as $word) {
    // do stuff with Word object here
}

The easiest way would be to convert your existing getWords method. You could add a new method getIterator as required by the IteratorAggregate interface and implement it like this:

public function getIterator() {
    return new ArrayIterator($this->getWords());
}

If you want to get rid of getWords(), you could also pass the internal array directly.

Sven
  • 69,403
  • 10
  • 107
  • 109
  • Thanks, I think this is what I need. I'll pass the internal array directly. Can you please tell me what is the advantage of returning an iterator instead of array in terms of incapsulation? – Max Koretskyi Oct 30 '13 at 08:21
  • The iterator is needed because of the interface. If you ask for an iterator (and PHP does internally in the foreach loop if the object implements that interface), the result must be an iterator object. Otherwise things might break. – Sven Oct 30 '13 at 08:34