我正在编写代码以返回指定区域内的位置列表,并且接近某个纬度和经度。数据库布局是包含业务的Location表,定义区域的Region表(UrlName是标识区域的slug),以及将区域映射到Locations的regionLocation表。
SQL查询非常多毛,但它会计算一个名为" Distance"的虚拟列。我希望在返回的模型中可以访问。
以下是我的位置模型中显示的代码的缩短版本:
Align
问题在于,当我运行查询时,我想要计算出的"距离"要包含在结果中的列。但是,由于数据库中没有名为" Distance"的实际字段,Yii2的ActiveRecord类不会将字段添加到生成的模型中。
我可以通过在Location表中创建一个名为" Distance"的列来解决它,但我希望有一种方法可以在不进行数据库更改的情况下执行此操作。
我的最终目的是让模型返回一个Location对象数组,每个Location对象都有一个" Distance"属性。然后,控制器将使用类似于以下代码生成json提要:
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();
}
答案 0 :(得分:3)
您只需将此属性添加到您的班级:
class Location extends \yii\db\ActiveRecord
{
public function attributes()
{
// add distance attribute (will work for json output)
return array_merge(parent::attributes(), ['Distance']);
}
// ...
}
答案 1 :(得分:0)
在问这个问题后不久,我发现了一个稍微不优雅的答案。
我必须在Location模型中覆盖populate记录和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属性开始出现在模型中。