2016-09-28 17 views
1

指定された地域内にあり、特定の緯度と経度に近い場所のリストを返すコードを書いています。データベースのレイアウトは、ビジネスを含むロケーションテーブル、リージョンを定義するRegionテーブル(リージョンを識別するスラッグであるUrlName)、リージョンをLocationsにマップするregionLocationテーブルがあることです。SQLクエリから計算されたフィールドをYii2 ActiveRecordモデルに追加する

SQLクエリはかなり毛深いですが、返されるモデルでアクセス可能にしたい "Distance"という名前の仮想列を計算します。ここで

は私の場所のモデルに表示されたコードの短縮版である:

 public static function getByRegionAndLatLong($regionName, $lat, $long) { 

     $sql = "SELECT 
      `Location`.`LocationId`, 
      `Location`.`Latitude`, 
      `Location`.`Longitude`, 
        (
        3959 * acos (
        cos (radians(:Latitude)) 
        * cos(radians(latitude)) 
        * cos(radians(longitude) - radians(:Longitude)) 
        + sin (radians(:Latitude)) 
        * sin(radians(latitude)) 
        ) 
       ) AS Distance 
       FROM Location 
        LEFT JOIN RegionLocation RL 
        ON RL.LocationId = Location.LocationId 
        LEFT JOIN Region R 
        ON R.RegionId = RL.RegionId 
       WHERE R.UrlName= :UrlName 
       ORDER BY Distance ; 
     "; 

     return Location::findBySql($sql, [ 
      ':Latitude' => $lat, 
      ':Longitude' => $long, 
      ':UrlName' => $UrlName 
     ])->all(); 
    } 

問題は、私は、クエリを実行すると、私はその計算された「距離」の列の中に含まれたいということです結果。しかし、 "Distance"という名前のデータベースには実際のフィールドが存在しないため、Yii2のActiveRecordクラスはフィールドを生成されたモデルに追加しません。

"Distance"という名前のLocationテーブルに列を作成することで回避できますが、データベースを変更せずにこれを行う方法があることを期待しています。

私の最終的な目的は、モデルにLocationオブジェクトの配列を返し、各Locationオブジェクトに "Distance"属性があるようにすることでした。その後、コントローラは次のようなコードを使用してJSONフィードを生成します:

public function actionGetStudiosByLatLong($regionName = '', $latitude='', $longitude='') { 
     \Yii::$app->response->format = 'json'; 

     return Location::getByRegionAndLatLong($regionName, $latitude, $longitude); 
    } 
+0

機能はモデルにありますか?行のコレクションを返しますか? – scaisEdge

+0

修正。しかし、それは最終的にセット全体をjsonオブジェクトとして出力するWebサービスのためのものです。 – TMorgan

答えて

2

あなたは、単にあなたのクラスにこの属性を追加する必要があります

class Location extends \yii\db\ActiveRecord 
{ 

    public function attributes() 
    { 
     // add distance attribute (will work for json output) 
     return array_merge(parent::attributes(), ['Distance']); 
    } 

    // ... 
} 

Selecting extra fieldsについては、こちらをご覧ください。

+0

これはより洗練された解決策のようですが、私のコントローラはこれらのオブジェクトを使ってjsonフィードを生成できる必要があります(質問はそれを反映するよう編集されています)。応答形式をjsonに設定し、public $ Distanceプロパティを持つオブジェクトを返すと、プロパティは他のすべてのフィールドと同様に属性ではないので、このプロパティは省略されます。しかし、結果セットをjson_encodeにすると、距離プロパティ以外のオブジェクトの配列が得られます(属性は省略されます)。 – TMorgan

+0

回答が更新されました。 – soju

+0

距離フィールドを追加しましたが、値はすべてのレコードで空でした。この答えの前のバージョンからの距離公共財産を取り除く必要があることが判明しました。書かれているように、正しく動作します。 – TMorgan

0

この質問をした直後、私はちょっと面白くない答えを見つけました。

私は、ロケーションモデル内のデータレコードとhasAttributeメソッドをオーバーライドする必要がありました。

 /** 
    * Adds "Distance" field to records if applicable. 
    */ 
    public static function populateRecord($record, $row) 
    { 
     $columns = static::getTableSchema()->columns; 

     foreach ($row as $name => $value) { 
      if (isset($columns[$name])) { 
       $row[$name] = $columns[$name]->phpTypecast($value); 
      } 
     } 
     parent::populateRecord($record, $row); 

     if(isset($row['Distance'])) { 
      $record->setAttribute('Distance', $row['Distance']); 
     } 
    } 

    // Needed to convince the powers that be that "Distance" really is a field. 
    public function hasAttribute($name) 

    { 
     if($name == 'Distance') return true; 
     return parent::hasAttribute($name); 
    } 

これらが追加されると、Distanceプロパティがモデルに表示され始めました。

+0

これを行う必要はありません – soju

関連する問題