Question

I'm currently having an issue with pagination in Zend Framework 2.

This code

public function findAllByCriteria(CourseSearchInput $input) {
    $concatDelimiter = self::CONCAT_DELIMITER;
    $select = new Select();
    $where = new Where();
    $having = new Having();
    $select->columns(array(
        'id', 'title', 'description'
    ));
    $select->from($this->tableGateway->getTable());
    $select
        ->join('coursedata', 'courses.id = coursedata.id', array(
            'relevance' => $this->buildRelevanceExpressionFromCriteria($input)
        ))
    ;
    $having
        ->greaterThanOrEqualTo('relevance', self::RELEVANCE_MIN);
    ;
    $select->where($where, Predicate::OP_AND);
    $select->having($having);
    $select->group(array('courses.id'));

    $dbAdapter = $this->tableGateway->getAdapter();
    // $dbAdapter->getDriver()->getConnection()->execute('SET sql_mode = "";');
    $adapter = new \Zend\Paginator\Adapter\DbSelect($select, $dbAdapter);
    $paginator = new \Zend\Paginator\Paginator($adapter);
    return $paginator;
}

create this SQL:

SELECT
    `courses`.`id` AS `id`,
    `courses`.`title` AS `title`,
    `courses`.`description` AS `description`,
    MATCH (coursedata.title) AGAINST ('Salsa') * 5 + MATCH (coursedata.description) AGAINST ('Salsa') * 2 AS `relevance`
FROM `courses`
INNER JOIN `coursedata` ON `courses`.`id` = `coursedata`.`id`
GROUP BY `courses`.`id`
HAVING `relevance` >= '3'

It ueses the MySQL Extensions to GROUP BY and cannot be executed, if the sql_mode is set to ONLY_FULL_GROUP_BY. So, I tried to reset the sql_mode before the statement is executed (see the commented out line above: $dbAdapter->getDriver()->getConnection()->execute('SET sql_mode = "";');). But it didn't worked. So, how can I set the sql_mode in order to execute my non-standard SQL?

Was it helpful?

Solution

This may not be the answer to the question you are asking, but I can see you are going to have an issue with your query regardless when using Paginator.

The DbSelect Adapter for the Paginator doesn't like the aggregate function in there (Group By)

The Paginator will try and use your query to build it's own query to calculate the "count" for items in the collection. This is broken due to you using an aggregate in your query, any groups etc will break the adapter.

if you check the default implementation you will see:

/**
 * Returns the total number of rows in the result set.
 *
 * @return integer
 */
public function count()
{
    if ($this->rowCount !== null) {
        return $this->rowCount;
    }

    $select = clone $this->select;
    $select->reset(Select::COLUMNS);
    $select->reset(Select::LIMIT);
    $select->reset(Select::OFFSET);

    // This won't work if you've got a Group By in your query
    $select->columns(array('c' => new Expression('COUNT(1)')));

    $statement = $this->sql->prepareStatementForSqlObject($select);
    $result    = $statement->execute();
    $row       = $result->current();

    $this->rowCount = $row['c'];

    return $this->rowCount;
}

this doesn't like when you are using Group BY and will give back incorrect results.

You can create your own adataper, and extend the existing DbSelect and override the count method when you are planning to use Group BY;

Off the top of my head something like this should work, but may not be the most efficient way of doing it

/**
 * Returns the total number of rows in the result set.
 * 
 * @return integer
 */
public function count()
{
    if ($this->rowCount !== null) {
        return $this->rowCount;
    }

    /**
     * If the query hasn't got 'GROUP BY' just try and use the old method
     */
    $stateGroup = $this->select->getRawState('group');
    if( ! isset($stateGroup) || empty($stateGroup)) {
        return parent::count();
    }

    $select = clone $this->select;
    $select->reset(Select::LIMIT);
    $select->reset(Select::OFFSET);

    $statement = $this->sql->prepareStatementForSqlObject($select);
    $result    = $statement->execute();

    $this->rowCount = $result->count();

    return $this->rowCount;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top