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

时间:2016-09-28 20:47:10

标签: yii2 yii2-model

我正在编写代码以返回指定区域内的位置列表,并且接近某个纬度和经度。数据库布局是包含业务的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();
    }

2 个答案:

答案 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']);
    }

    // ...
}

详细了解Selecting extra fields

答案 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属性开始出现在模型中。