honnix honnix - 3 months ago 32
MySQL Question

Count of rows in Doctrine 2

I'm trying to retrieve comments from a table Comment which has id, game (a foreign key) and date.

Every time I ask for comments I want to get 3 comments sorted by date for a specified game and I want to know if there is more comments to show later. For that, I've written two functions, the first one returns the three comments:

public function getRecentComments($offset,$id) {
$dql = "SELECT c FROM Comment c
WHERE c.game = ?1
ORDER BY c.date DESC";
$query = $this->getEntityManager()->
createQuery($dql)->
setParameter(1, (int)$id)->
setMaxResults(3)->
setFirstResult($offset);
return $query->getResult();


And the second one returns the number of comments I could get later. The reason of this function is wehter show a button "More comments" or not. This is the second function:

public function moreComments($offset,$id) {

$dql = "SELECT COUNT(c.id) FROM Comment c
WHERE c.game = ?1
ORDER BY c.date DESC";
$query = $this->getEntityManager()
->createQuery($dql)
->setParameter(1, (int)$idPartido)
->setFirstResult($offset+3)
->setMaxResults(1)
->getSingleScalarResult();

return $query;
}


But the second function doesn't work for the next error:

Fatal error: Uncaught exception 'Doctrine\ORM\NoResultException' with message 'No result was found for query although at least one row was expected.

Which I think it is due to use setFirstResult and count().

So, I've used

public function moreComments($offset,$id) {

$dql = "SELECT c FROM Comentario c
WHERE c.partido = ?1
ORDER BY c.fecha DESC";
$query = $this->getEntityManager()
->createQuery($dql)
->setParameter(1, (int)$idPartido)
->setFirstResult($offset+3)
->setMaxResults(1)
->getSingleScalarResult();

return sizeof($query);
}


Which obviously is bad written because I shouldn't get the data for only a count. How could I write the second function correctly?

Thanks in advance.

Answer

If you will only be using MySQL, then you can take advantage of its FOUND_ROWS() function.

This will require using native queries, which will most likely hinder your ability to use a DB other than MySQL, but it works quite well in my experience.

I have used something like the following with great success.

use Doctrine\ORM\Query\ResultSetMapping;

public function getRecentComments($offset, $id) {
    $sql = "SELECT SQL_CALC_FOUND_ROWS * FROM Comment c 
        WHERE c.game = ?
        ORDER BY c.date DESC
        LIMIT ?,3";
    $rsm = new ResultSetMapping();
    $rsm->addEntityResult('Comment', 'c');
    $rsm->addFieldResult('c', 'id', 'id');
    $rsm->addFieldResult('c', 'game_id', 'game_id');
    $rsm->addFieldResult('c', 'date', 'date');
    $query = $this->getEntityManager()->createNativeQuery($dql, $rsm);
    $query->setParameters(array(
      (int)$id,
      (int)$offset
    ));
    $results = $query->getResult();

    // Run FOUND_ROWS query and add to results array
    $sql = 'SELECT FOUND_ROWS() AS foundRows';
    $rsm = new ResultSetMapping();
    $rsm->addScalarResult('foundRows', 'foundRows');
    $query = $this->getEntityManager()->createNativeQuery($sql, $rsm);
    $foundRows = $query->getResult();
    $results['foundRows'] = $foundRows[0]['foundRows'];

    return $results;
}

After getting the results array from the above function, I extract the 'foundRows' element to a separate variable, unset it (i.e., unset($results['foundRows'])), and then continue using the array as normal.

Hope this helps.