How to add dynamic field in magento collection?
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.
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; }
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);
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);