Last active
September 21, 2018 13:38
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?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