2016-08-15 18 views
3

SilverStripe 3.4.0内のmysqlごとに位置Yを中心に半径X以内の場所をフィルタリングしています。SilverStripe内のmysqlごとに半径X以内の場所ORM

これまで、サークル内のIDを取得するためのrawクエリを実装しました。複数の条件に対してフィルタを適用する必要があり、ジオフィルタはその1つのみであるため、SilverStripe ORMごとにフィルタリングするために使用します。

参照してください。また、 "店舗検索" の例をグーグル:私はDataObjectの-TabelのMyISAM

DataObject: 
    create_table_options: 
    MySQLDatabase: 
     'ENGINE=MyISAM' 

これは望ましい結果を提供をしたが、細胞外を必要としました_configで 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 
    )); 
} 

クエリ。 ジオフィルタをORM内のクエリに直接追加することはできますか?

+0

フィルタをORMに直接追加するとはどういう意味ですか? – Shadow

+0

@Shadow - SilverStripeは遅延ロードしません。これは、クエリ$ DataObjectsを変更できることを意味します。 $ DataObjects-> where( '...')のようなものです。 – munomono

+0

あなたのクエリにはhaving節しかないので、dataobjectクラスの 'having()'メソッドを使用します。しかし、 'filter()'メソッドにあるものは、クエリのwhere句に簡単に含めることができます。 SilverStripeの方法で生のクエリを書き直す方法を見つけるのではなく、生のクエリにこの基準を追加する方が簡単かもしれません。 – Shadow

答えて

1

私は...

(注)「直接ORM内のクエリに」何を意味するかわからないんだけど、その後、私は以下のソリューションを提供できる「Webサービスを呼び出すことなく」を意味すると解釈計算が同期して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です。

+0

thx @Barryしかし正直言って、私はもっと好きですあなたの提案よりも私が持っているもの。 location-Yの緯度/経度を取得するには、Googleごとにジオコードを実行しますが、サークル内の位置を見つけるにはMySQLごとにDBを照会するだけです。しかし、私は1つのクエリでそれをしたいと思います。 – munomono

+0

まあ私が言ったように "あなたが意味するならば..."私は本当に確かではありませんでした。あなたが持っているものは、HAVING句をORMに入れることができないので、最良の状況だと思います。ご質問を誤解して申し訳ありませんが、私は時々それを行います:) – Barry

+0

あなたは毎日何か新しいことを学びます... http://api.silverstripe.org/3.1/class-SQLQuery.html#_having – Barry

3

はい、これを実現するためにORMを使用することは可能です。 からDataQueryを取り出して変更して(句を追加するなど)、DataListを更新してください。

ような何か:ちょうどとしてhaversin式を追加しても、あなたはその後、DataQueryに対してSQLQueryを変更する必要があるが、それは同様に可能でなければなりませんよう

$dataList = MyObject::get(); 

$dataQuery = $dataList->dataQuery(); 

$dataQuery->where(...); 
$dataQuery->having(...); 

$dataList->setDataQuery($dataQuery); 

選択を追加し、エイリアシングが少しトリッキーです並べ替えが機能します。

$dataList->sort(...) 
関連する問題