Skip to content

Instantly share code, notes, and snippets.

@dfelton
Last active September 21, 2018 13:38
Show Gist options
  • Select an option

  • Save dfelton/210ba6ee9ebeeaeef57fba5942ef9797 to your computer and use it in GitHub Desktop.

Select an option

Save dfelton/210ba6ee9ebeeaeef57fba5942ef9797 to your computer and use it in GitHub Desktop.
Add Haversine formula to a Magento resource collection. Expects the collection to have 'lat' and 'lng' attributes in the main FROM table.
<?php
// Just add the following methods inside your existing resource collection class.
class ModuleNamespace_ModuleName_Model_Resource_EntityName_Collection
extends Mage_Core_Model_Resource_Db_Collection_Abstract
{
/**
* Filters the collection to only include records within X miles of a specified
* latitude and longitude coordinates.
*
* Example (radius unit: mailes):
* $params = array(
* 'lat' => 44.9764839
* 'lng' => -93.4546814
* 'radius' => 25
* );
*
* @param array $params
* @return ModuleNamespace_ModuleName_Model_Resource_EntityName_Collection
*/
public function applyMapSearchFilter($params)
{
if (!$this->hasFlag('_appliedHaversine')) {
$this->setFlag('_appliedHaversine', true);
// Sanitize input
$params['lat'] = floatval($params['lat']);
$params['lng'] = floatval($params['lng']);
$sql = $this->getSelect();
$sql->columns("(3959 *
ACOS(
COS( RADIANS( {$params['lat']} ) ) *
COS( RADIANS( main_table.lat ) ) *
COS(
RADIANS( main_table.lng ) -
RADIANS( {$params['lng']} ) ) +
SIN( RADIANS( {$params['lat']} ) ) *
SIN( RADIANS( main_table.lat ) )
)
) AS distance_from_search")
;
$sql->where('main_table.lat IS NOT NULL');
$sql->where('main_table.lng IS NOT NULL');
$sql->having('`distance_from_search` <= ?', $params['radius']);
$this->setFlag('search_radius', $params['radius']); // used again in count select
}
return $this;
}
/**
* Get SQL for get record count.
* Extra HAVING, and (conditionally) Haversine formula added.
*
* @return Varien_Db_Select
*/
public function getSelectCountSql()
{
$countSelect = parent::getSelectCountSql();
$countSelect->reset(Zend_Db_Select::HAVING);
$this->_applyHaversineToCount($countSelect);
return $countSelect;
}
/**
* Filters the select count sql to records within range of the search radius.
*
* @param Varien_Db_Select $countSelect
*/
protected function _applyHaversineToCount($countSelect)
{
/* @var $resource Mage_Core_Model_Resource */
/* @var $read Magento_Db_Adapter_Pdo_Mysql */
if ($this->getFlag('_appliedHaversine')) {
$parts = $this->getSelect()->getPart(Zend_Db_Select::COLUMNS);
foreach ($parts as $part) {
$expr = is_array($part) && isset($part[1]) ? $part[1] : null;
if ( $expr instanceOf Zend_Db_Expr
&& 'distance_from_search' == substr($expr->__toString(), -20)
) {
$resource = Mage::getSingleton('core/resource');
$read = $resource->getConnection('core_read');
$sql = new Varien_Db_Select($read);
$sql
->from(
array(
'main_table' => $resource->getTableName($this->getModelName())
),
array(
'entity_id',
$expr->__toString()
)
)
->where('lat IS NOT NULL')
->where('lng IS NOT NULL')
->having('distance_from_search <= ?', $this->getFlag('search_radius'))
;
$entityIds = $read->fetchCol($sql);
$countSelect->where('entity_id IN (?)', $entityIds ? $entityIds : '0');
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment