我正在SilverStripe 3.4.0中的每个mysql的位置Y周围的半径X内过滤位置。
到目前为止,我已经实现了一个原始查询来获取圈子中的ID,而不是使用这些ID来过滤每个SilverStripe ORM,因为我必须根据多个条件进行过滤,而geofilter只是其中之一。
另请参阅Google“商店定位器”示例: https://developers.google.com/maps/articles/phpsqlsearch_v3
$searchDistance = '...';
$searchLat = '...';
$searchLng = '...';
$geolimitedIDs = DB::query('SELECT id, (6371 * acos(cos(radians('.$searchLat.')) * cos(radians(Latitude)) * cos( radians(Longitude) - radians('.$searchLng.')) + sin(radians('.$searchLat.')) * sin(radians(Latitude))))
AS distance
FROM "DataObject"
HAVING distance < ' . $searchDistance . '
ORDER BY distance')->column();
if($geolimitedIDs) {
$DataObjects = $DataObjects->filter(array(
'ID' => $geolimitedIDs
));
}
在_config中我创建了DataObject-Tabel MyISAM
DataObject:
create_table_options:
MySQLDatabase:
'ENGINE=MyISAM'
这可以提供所需的结果,但需要额外的查询。 是否可以将地理过滤器直接添加到ORM中的查询中?
答案 0 :(得分:3)
是的,可以使用ORM来实现这一目标。您可以从DataQuery
中提取DataList
,更改它(添加条款等),然后使用它更新DataList
。
类似的东西:
$dataList = MyObject::get();
$dataQuery = $dataList->dataQuery();
$dataQuery->where(...);
$dataQuery->having(...);
$dataList->setDataQuery($dataQuery);
添加和别名选择有点棘手,因为您需要针对SQLQuery
修改DataQuery
,但它也应该是可行的,尽管只需添加哈希公式作为排序工作
$dataList->sort(...)
答案 1 :(得分:1)
我不确定“直接查询ORM中的查询”是什么意思,但是我的意思是“没有调用网络服务”,那么我可以提供以下解决方案......
注意我建议使用javascript直接在商店定位器中使用google地图执行此操作,以便通过javascript同步完成计算。
function FilterMemberByPostCodeDistance($params, $query){
$query->where('Member.PostCode IS NOT NULL')
->innerJoin('PostCodeToLocation',"SUBSTRING_INDEX(SUBSTRING_INDEX(Member.PostCode,' ', 1),' ',-1) = PostCodeToLocation.OutCode");
$latitude = (float)$postCodeToLocation->Latitude;
$longitude = (float)$postCodeToLocation->Longitude;
$fTemp = floatval($params['Distance']) / 111.045;
$fMagicSquareMinLatitude = $latitude - $fTemp;
$fMagicSquareMaxLatitude = $latitude + $fTemp;
$fTemp = 50.0 / (111.045 * cos(deg2rad($latitude)));
$fMagicSquareMinLongitude = $longitude - $fTemp;
$fMagicSquareMaxLongitude = $longitude + $fTemp;
$query->where(
//Magic Square - this is a simple square to filter out most out of distance values before the magic circle
//this is done because the circle calculation is much more expensive that the square
'PostCodeToLocation.Latitude BETWEEN '.$fMagicSquareMinLatitude.' AND '.$fMagicSquareMaxLatitude.'
AND PostCodeToLocation.Longitude BETWEEN '.$fMagicSquareMinLongitude.' AND '.$fMagicSquareMaxLongitude
//Magic Circle (https://en.wikipedia.org/wiki/Haversine_formula)
//This is what does the complicated maths to determine if the postcode is in the the cirectle or not
//not as we are using out codes only, this is a "good estimate" but not 100% accurate
//.' AND acos(sin(RADIANS('.$latitude.'))
// * sin(RADIANS(PostCodeToLocation.Latitude))
// + cos(RADIANS('.$latitude.'))
// * cos(RADIANS(PostCodeToLocation.Latitude))
// * cos(RADIANS(PostCodeToLocation.Longitude)
// - (RADIANS('.$longitude.'))))
// * 6371 <= '.($params['Distance'] * 1.60934) //Kilometers
//REFACTOR of above to process more upfront within PHP
.' AND acos(sin('.deg2rad($latitude).')
* sin(RADIANS(PostCodeToLocation.Latitude)) + '.cos(deg2rad($latitude))
.' * cos(RADIANS(PostCodeToLocation.Latitude))
* cos(RADIANS(PostCodeToLocation.Longitude) - '.deg2rad($longitude).'))
* 6371 <= '.($params['Distance'] * 1.60934) //Kilometers
);
return $query;
}
上面的函数使用邮政编码从公共可用数据之后的lon / lat对(此位仅限英国)。
class PostCodeToLocation extends DataObject{
static $db = array(
'OutCode' => 'Varchar(5)',
'Latitude' => 'Float',
'Longitude' => 'Float'
);
public static $indexes = array(
'OutCode' => true
);
public function PopulatePostCodeToLocationTable() {
DB::query('TRUNCATE TABLE PostCodeToLocation');
$arrPostCodetoLocations = file(BASE_PATH .'/mysite/.../postcode_outcode_to_latlong.csv');
if(!empty($arrPostCodetoLocations))
foreach ($arrPostCodetoLocations as $strPostCodetoLocation) {
list ($strOutCode,$strLatitude,$strLongitude) = explode(',',$strPostCodetoLocation);
DB::query("INSERT INTO PostCodeToLocation (OutCode, Latitude, Longitude )
VALUES ('".$strOutCode."','".$strLatitude."','".$strLongitude."')"
);
}
}
}
找到上述数据文件here。