5

In my Mapper class I'm extending AbstractDbMapper from ZfcBase to fetch rows from the database. A simple example would be code like this:

class MyMapper extends AbstractDbMapper
{

    //...

    public function fetchAll() {
        $select = $this->getSelect();
        return $this->select($select); // returns HydratingResultSet
    }
}

The problem is that $this->select() returns a Zend\Db\ResultSet\HydratingResultSet (containing the needed and hydrated objects). But I would like to return an array of these objects instead of a HydratingResultSet containing the objects.

The first thing to look at would be HydratingResultSet::toArray() but this returns a multidimensional array instead of an array of objects.

So I chose to do it by hand:

public function fetchAll() {
        $select = $this->getSelect();

        $results = array();
        foreach ($this->select($select) as $object) {
            $results[] = $object;
        }
        return $results; // returns array of needed objects
}

This works but looks ugly in every fetch method. Do I have to modify the code from select() to get the wanted behavior or is there an easier way?

Btw: Is it even recommended to return an array or convert it like this? Thanks for your help!

Karl Lorey
  • 1,536
  • 17
  • 21
  • Is there a reason you need an array? The Resultset will work as an iterator just fine and can be used in most cases.. There's no built in method to get the array without doing what you are doing now, you could add the method into your Mapper or a just Hydrator as a shortcut. – Andrew May 16 '13 at 16:23
  • I'm using these results in my Controller and think it would be cleaner to work with arrays instead of some Zend\Db objects. I don't understand why they chose to do it this way... – Karl Lorey May 16 '13 at 17:18
  • 1
    they chose to do it this way because you get extra functionality, such as buffering the result set etc. Think about if you have a collection of 9million objects, you array will become HUGE and consume RAM, with this collection the items are made on demand one at a time, it's much more efficient. – Andrew May 16 '13 at 21:14
  • Okay, that sounds reasonable. Thanks a lot, I didn't think of that. It would at least be nice if there was a built in function to retrieve the whole array since that would fit the need of most cases imho. I will add this by hand now. – Karl Lorey May 19 '13 at 17:27
  • Why is it needed though? You can iterate over the resultset perfectly fine..can you name a specific case when you MUST use an array? – Andrew May 20 '13 at 08:05
  • One reason would be to simply not have some Zend\Db-Object in the Controller. Another even more important reason is to be able to use array funtions. – Karl Lorey May 20 '13 at 20:30
  • 1
    It is a Zend internal inconsistency indeed. See this issue: https://github.com/zendframework/zf2/issues/4635 – tihe Jun 12 '13 at 08:51

2 Answers2

5

Update:

There is a cleaner possibility to do it (by limos from https://stackoverflow.com/a/19266650/1275778). Adapted to my example from above it works like this:

public function fetchAll() {
    $select = $this->getSelect();
    $results = $this->select($select);
    return \Zend\Stdlib\ArrayUtils::iteratorToArray($results); // returns desired array of objects
}

If limos posts his answer here, I will happily accept it.

Old answer:

Since no one could answer my question I tried to implement the cleanest option (to me): extending AbstractDbMapper to add the mentioned functionality. I document it here for anyone looking for a solution:

MyAbstractDbMapper extends AbstractDbMapper
{
    /**
     * @param Select $select
     * @param object|null $entityPrototype
     * @param HydratorInterface|null $hydrator
     * @return array
     */
    protected function select(Select $select, $entityPrototype = null, 
        HydratorInterface $hydrator = null)
    {
        $resultSet = parent::select($select, $entityPrototype, $hydrator);
        $results = array(); // Array of result objects
        foreach ($resultSet as $object) {
            $results[] = $object;
        }
        return $results;
    }
}

select() in MyAbstractDbMapper now returns an array of objects instead of HydratingResultSet.

As this is getting downvoted, could someone please explain why?

Community
  • 1
  • 1
Karl Lorey
  • 1,536
  • 17
  • 21
  • 1
    Better explanation and code example here: http://stackoverflow.com/questions/18940759/how-to-return-an-array-of-objects-in-zend-framework-2?ra=1 – Bill Ortell Nov 08 '13 at 16:33
1

usually you've already got the object class anyway so just popupate instances of your object (class):

public function fetchById($id) {

    [...]

    $resultSet = $this->getDbTable()->fetchAll($select);
    $entries   = array();

    foreach ($resultSet as $row) {
        $entries[] = $this->populate($row);
    }

    return $entries;
}

and the populate function could look something like this:

private function populate($row)
{
    $entry = new Application_Model_YourModel();

    $entry->setId($row->id);

    [...] //set other object values

    return $entry;
}

This will return an array of objects

simplyray
  • 1,200
  • 1
  • 16
  • 25
  • The objects in my loop are already of the needed type as `HydratedResultSet` provides an iterator to loop over the hydrated objects. So these are NOT row objects and have already been hydrated in the `AbstractDbMapper`. Therefore there is no need for a populate function and no need to use fetchAll as `$this->select()` is fetching the results. – Karl Lorey May 16 '13 at 14:42
  • 1
    Okay. which version of zend are you using? Does `toArray()` fulfill your needs? http://tgaconnect.com/dox/zend/html/class_zend_1_1_db_1_1_result_set_1_1_hydrating_result_set.html#a658defb34762c8f40085aec87e16ba1a – simplyray May 16 '13 at 14:51
  • 1
    I'm using ZF 2.2. No, toArray() returns a multidimensional array and thus contains no objects at all. – Karl Lorey May 16 '13 at 14:54