从 yii 中的模型获取相关数据并返回 json 的最佳方法

Posted

技术标签:

【中文标题】从 yii 中的模型获取相关数据并返回 json 的最佳方法【英文标题】:Best way to get related data from models in Yii and return JSON 【发布时间】:2011-09-24 03:31:39 【问题描述】:

你好,只是个问题

我正在为一个在前端使用 sproutcore 的项目开发一个安静的应用程序。

我的问题是,当需要返回 json 时,从具有其他相关模型的模型中获取数据的最有效方法是什么。我昨天读到它建议在处理数组时使用 DAO 层,所以对于我的示例来说,这就是我目前所拥有的。

我有一个客户列表,每个客户都有 HAS_MANY 品牌和每个品牌都有 HAS_MANY 项目。不是 用他们的品牌得到一个很好的客户群,这就是我所拥有的

$clients = Yii::app()->db->createCommand('select client.* from client where client.status = 1')->queryAll();

        foreach($clients as $ckey => $client)
        
            $clients[$ckey] = $client;
            $brand_ids = Yii::app()->db->createCommand('select brand.id as brand_id, brand.client_id as b_client_id from brand where brand.client_id ='.$client['id'])->queryAll();

            foreach($brand_ids as $bkey => $brand_id)
            
                $clients[$ckey]['brands'][] = $brand_id['brand_id'];
            

    

到目前为止,这是返回我想要的东西,但它是实现我所追求的最有效的方法吗?

【问题讨论】:

如果您的表client 没有CActiveRecord 对象,请先执行此操作(请参阅:yiiframework.com/doc/guide/1.1/en/database.ar)。之后,您将能够获得简单的数据:$clients = Client::model()->with('brand')->findByAttributes(array('status'=>1)); 由于我将返回 json,使用 DAO 层不是更好,因为使用 ar 执行查询返回的信息比我需要的多得多。所以当我对数组进行编码时,我最终得到的对象比我需要返回的要多?? 如果我理解正确的话:1)你可以写出你想选择的列(抛出模型类Client并与Brand建立关系)2)接下来,在foreach之后声明选择这些 2.1) 并像您在问题示例中所做的那样转换为 json / 我仍然建议首先为表创建模型。我先做,如果我对表进行了一些全局更改,我只更改我的模型类。 @briiC.lv 您能否以代码的形式提供一个示例作为答案,以便我可以为您排名 【参考方案1】:

设置客户端模型

class Client extends CActiveRecord
    
    //...
    /**
     * @return array relational rules.
     */
    public function relations()
    
            // NOTE: you may need to adjust the relation name and the related
            // class name for the relations automatically generated below.
            return array(
                    'brands' => array(self::HAS_MANY, 'Brand', 'client_id'),
            );
    
    //...
    public function defaultScope() 
        return array('select'=>'my, columns, to, select, from, client'); //or just comment this to select all "*"
    


设置品牌型号

class Brand extends CActiveRecord

    //...
    /**
     * @return array relational rules.
     */
    public function relations()
    
            // NOTE: you may need to adjust the relation name and the related
            // class name for the relations automatically generated below.
            return array(
                    'client' => array(self::BELONGS_TO, 'Client', 'client_id'),
            );
    
    //...
    //...
    public function defaultScope() 
        return array('select'=>'my, columns, to, select, from, brand'); //or just comment this to select all "*"
    


在您的操作函数中进行客户/品牌搜索

$clients = Client::model()->with('brands')->findAllByAttributes(array('status'=>1));

$clientsArr = array();
if($clients) 
    foreach($clients as $client) 
        $clientsArr[$client->id]['name'] = $client->name; //assign only some columns not entire $client object.
        $clientsArr[$client->id]['brands'] = array();

        if($client->brands) 
            foreach($client->brands as $brand) 
                $clientsArr[$client->id]['brands'][] = $brand->id;
            
        

    


print_r($clientsArr);
/*
Array (
    [1] => Array (
        name => Client_A,
        brands => Array (
            0 => Brand_A,
            1 => Brand_B,
            2 => Brand_C
        )
    )
    ...
)

*/

这是你想要的吗? 我意识到,如果您只想选择品牌 ID(没有其他数据),您可以通过 sql 和 GROUP_CONCAT (mysql) 进行搜索,并在以逗号分隔的一行中选择客户端的所有品牌 ID。 1,2,3,4,5,20,45,102.

【讨论】:

【参考方案2】:

如果您不想通过with() 功能使用CActiveRecord,那么您应该编写一个SQL 查询加入brand 表。

$rows = Yii::app()->db
    ->createCommand(
        'SELECT c.*, b.id as brand_id 
        FROM client c INNER JOIN brand b 
        WHERE c.status = 1 AND b.client_id = c.id')
    ->queryAll();
$clients = array();
foreach ($rows as row) 
    if (!isset($clients[$row['id']])) 
        $clients[$row['id']] = $row;
        $clients[$row['id']]['brands'] = array();
    
    $clients[$row['id']]['brands'][] = $row['brand_id'];

这比执行一次查询来检索所有客户,然后执行 N 次查询来获取他们的品牌(其中 N 是客户的数量)要高效得多。您还可以加入您的第三张表 projects 并检索每个品牌的所有相关项目。

【讨论】:

嘿,galymzhan,似乎在回答我所有与 yii 相关的话题!这似乎是一种更明智的方法,但是在尝试您的示例时,我很难让 json 很好地返回,php 在将数组转换为 json 时似乎做了一些时髦的事情。我会继续尝试使用您的示例根据客户请求的需要构建数组!【参考方案3】:

我知道这已经过时了,但我自己也在寻找解决方案,我认为这是一个很好的解决方案。

在我的基本控制器类(protected/Components/Controller.php)中,我添加了以下函数:

protected function renderJsonDeep($o) 
    header('Content-type: application/json');
        // if it's an array, call getAttributesDeep for each record
    if (is_array($o)) 
        $data = array();
        foreach ($o as $record) 
            array_push($data, $this->getAttributesDeep($record));
        
        echo CJSON::encode($data);
     else 
            // otherwise just do it on the passed-in object
        echo CJSON::encode( $this->getAttributesDeep($o) );
    

        // this just prevents any other Yii code from being output
    foreach (Yii::app()->log->routes as $route) 
        if($route instanceof CWebLogRoute) 
            $route->enabled = false; // disable any weblogroutes
        
    
    Yii::app()->end();


protected function getAttributesDeep($o) 
        // get the attributes and relations
        $data = $o->attributes;
    $relations = $o->relations();
    foreach (array_keys($relations) as $r) 
            // for each relation, if it has the data and it isn't nul/
        if ($o->hasRelated($r) && $o->getRelated($r) != null) 
                    // add this to the attributes structure, recursively calling
                    // this function to get any of the child's relations
            $data[$r] = $this->getAttributesDeep($o->getRelated($r));
        
    
    return $data;

现在,对一个对象或对象数组调用 renderJsonDeep 会以 JSON 格式对对象进行编码,包括您提取的任何关系,例如将它们添加到 DbCriteria 中的“with”参数中。

如果子对象有任何关系,也会在 JSON 中设置这些关系,因为 getAttributesDeep 是递归调用的。

希望这对某人有所帮助。

【讨论】:

以上是关于从 yii 中的模型获取相关数据并返回 json 的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

RESTful 响应如何在 Yii2 中返回 JSON 而不是 XML?

Yii中的相关表格

从 Laravel 中的 Mysql Pivot 表中获取列并返回 JSON 对象

Yii uuid 模型保存为主键返回 null

YII对象结果转为数组或直接输入JSON格式

Yii CActiveDataProvider with() 并加入