Question

I have created one magento collection like this to calculate distance from lat long.

$collection = Mage::getModel('module/module')->getCollection()->addFieldToFilter('status',1);
            $collection->getSelect()->columns(array('distance' => new Zend_Db_Expr("( 6371 * acos( cos( radians(23.0130648) ) * cos( radians( latitude ) ) * cos( radians( longitude) - radians(72.4909026) ) + sin( radians(23.0130648) ) * sin( radians( latitude ) ) ) )")))
            ->having('distance <10')
            ->order('distance ' . Varien_Db_Select::SQL_ASC);

But I am getting error SQLSTATE[42S22]: Column not found: 1054 Unknown column 'distance' in 'having clause'.

When I print collection object Distance calculation is right. so what was the problem?

My problem is similar like this question Issue using "having" in Magento collection

I am using pagination on collection, if I remove pagination class then it works perfect. But I am unable to solve with solution provided. Here's my pagination code.

In _prepareLayout() function I put this

$pager = $this->getLayout()->createBlock('page/html_pager', 'pager');
          $pager->setAvailableLimit(array(5=>5,10=>10,20=>20,'all'=>'all'));
          $pager->setCollection($this->getCollection());
          $this->setChild('pager', $pager);
          $this->getCollection()->load();
          return $this;

add this function also in block file

  public function getPagerHtml()
  {
    return $this->getChildHtml('pager');
  }

and call this in phtml file as <?php echo $this->getPagerHtml(); ?>

Was it helpful?

Solution

I can try to use addExpressionFieldToSelect.
You can find the method in Mage_Core_Model_Resource_Db_Collection_Abstract.
In your case it should be something like this: (This is just an assumption, you might get some errors, but the idea is ok)

$collection = Mage::getModel('module/module')->getCollection()->addFieldToFilter('status',1);
$collection->addExpressionFieldToSelect('distance', '( 6371 * acos( cos( radians(23.0130648) ) * cos( radians( {{latitude}}) ) * cos( radians( {{longitude}}) - radians(72.4909026) ) + sin( radians(23.0130648) ) * sin( radians( {{latitude}}) ) ) )', array('latitude'=>'latitude', 'longitude'=>'longitude'));
$collection->getSelect()->having('distance > 10');

The addExpressionFieldToSelect works like this:
the first parameter is the alias of the expression (virtual field name).
The second parameter is the expression. Replace the field names with placeholders wrapped arround {{...}}
The third parameter is the placeholder correspondence (without {{}}). In your case latitide placeholder corresponds to latitude field so {{latitude}} will be replaced with latitude. Same goes for longitude.

[EDIT]
There is a problem when adding pagination to the $collection like this

$collection->setCurPage(1)->setPageSize(5);  

Here is the backtrace of the issue. When the collection is loaded this is called _renderLimit(). The method looks like this

protected function _renderLimit()
{
    if($this->_pageSize){
        $this->_select->limitPage($this->getCurPage(), $this->_pageSize);
    }

    return $this;
}

So this calls getCurPage() (see Varien_Data_Collection class).
getCurPage has an additional verification to see if the page number is not outside the max range so it calculates the total number of pages in getLastPageNumber().
The problem here is that Magento resets the columns in the select for calculating the collection size. In Varien_Data_Collection_Db::getSelectCountSql there is this:

$countSelect->reset(Zend_Db_Select::COLUMNS);

By reseting the columns you end up with this sql

SELECT COUNT(*) FROM `table_name_here` AS `main_table` HAVING (distance < 10)

This is what generates the error.

I see 2 options here.

  1. You override in your collection class the method getSelectCountSql and remove the column reset:

    public function getSelectCountSql()
    {
        $this->_renderFilters();
    
        $countSelect = clone $this->getSelect();
        $countSelect->reset(Zend_Db_Select::ORDER);
        $countSelect->reset(Zend_Db_Select::LIMIT_COUNT);
        $countSelect->reset(Zend_Db_Select::LIMIT_OFFSET);
        //$countSelect->reset(Zend_Db_Select::COLUMNS);//comment this line
    
        $countSelect->columns('COUNT(*)');
    
        return $countSelect;
    }
    
  2. You override the getCurPage() method to skip the range validation:

    public function getCurPage($displacement = 0){
        if (!empty($this->_curPage)){
            return $this->_curPage + $displacement;
        }
        return 1;
    }
    

[EDIT TO EDIT]
To avoid affecting the rest of the modules you can override the getCurPage method like this:

public function getCurPage($displacement = 0){
    if (!$this->getDirectCurPage()){//if a specific flag is not set behave as default
        return parent::getCurPage($displacement);
    }
    if (!empty($this->_curPage)){
        return $this->_curPage + $displacement;
    }
    return 1;
}

Now when you want to use your having method just add this to your collection

$collection->setDirectCurPage(1);

OTHER TIPS

Try using this instead:

$collection = Mage::getModel('module/module')->getCollection()->addFieldToFilter('status',1);
            $collection->getSelect()->columns(array('distance' => new Zend_Db_Expr("( 6371 * acos( cos( radians(23.0130648) ) * cos( radians( latitude ) ) * cos( radians( longitude) - radians(72.4909026) ) + sin( radians(23.0130648) ) * sin( radians( latitude ) ) ) )")))
            ->addAttributeHaving('distance <10')
            ->addAttributeToSort('distance', Varien_Db_Select::SQL_ASC);

You could try where() instead of having() as you don't use any JOIN statements:

$collection->getSelect()->where('distance > ?', 10);
Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top