将从SQL查询计算的字段添加到Yii2 ActiveRecord模型

问题描述:

我正在编写代码以返回位于指定区域内的位置列表,并且接近某个经度和纬度。数据库布局是有一个包含业务的位置表,一个区域表(它定义了区域(UrlName是一个标识该区域的块)和一个将区域映射到位置的区域位置表。将从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(); 
    } 

的问题是,当我运行查询,我想为所计算的“距离”一栏被列入结果。但是,由于数据库中没有名为“距离”的实际字段,因此Yii2的ActiveRecord类将不会将字段添加到生成的模型中。

我可以通过在Location表中创建一个名为“Distance”的列来避开它,但我希望有一种方法可以在不更改数据库的情况下执行此操作。

我最终的目的是让模型返回一个Location对象数组,每个Location对象都有一个“Distance”属性。

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

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

函数在模型中?并返回一组行? – scaisEdge

+0

正确。但它是一个web服务,最终将输出整个集合作为json对象。 – TMorgan

您应该将此属性只是添加到您的类:

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然后,控制器会使用类似于下面的代码生成一个JSON饲料。

+0

这似乎是一个更优雅的解决方案,但我的控制器需要能够用这些对象生成json提要(问题已被编辑以反映这一点)。当我将响应格式设置为json并返回具有公共$ Distance属性的对象时,该属性被省略,因为它不像所有其他字段一样是属性。但是当我对结果集进行json_encode时,我得到一个除了距离属性以外都没有的对象数组(这些属性被省略)。 – TMorgan

+0

答复已更新。 – soju

+0

那确实增加了一个距离字段,但是每个记录的值都是空的。事实证明,我需要摆脱该答案以前版本中的距离公共属性。正如所写,它能正常工作。 – TMorgan

问完这个问题后不久,我发现了一个稍微不雅的回答。

我不得不在我的位置模型中覆盖填充记录和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); 
    } 

一旦添加了这些,距离属性开始出现在模型中。

+0

不需要这样做 – soju