从 Yii 中的模型获取相关数据并返回 JSON 的最佳方式

发布于 2024-11-17 12:07:58 字数 889 浏览 7 评论 0原文

我正在为一个在前端使用 SproutCore 的项目开发一个 RESTful 应用程序。

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

我有一份客户列表,其中每个客户都有_MANY 个品牌,每个品牌都有_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'];
            }

    }

这正在返回我迄今为止想要的东西,但这是实现我所追求的目标的最有效方法吗?

I'm working on a RESTful application for a project that is using SproutCore on the front end.

My question is really just what is the most effective way to grab data from a model with other related models when needing to return JSON. I read yesterday that it's recommended to work the the DAO layer when working with arrays, so for my example, this is what I have so far.

I have a list of clients, where each client HAS_MANY brands and each brand HAS_MANY projects. I'm not getting a nicely formed array back of clients with their brands. Here's what I have:

$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'];
            }

    }

This is returning what I want so far, but is it the most effective way to achieve what I'm after?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

深爱不及久伴 2024-11-24 12:07:58

设置客户模型

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

Setup Client model

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 "*"
    }

}

Setup Brand model

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 "*"
    }

}

Do client/brand search in your action function

$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
        )
    )
    ...
)

*/

Is this you wanted?
I realize, if you want select only brands ID (on no more data else) you could search by sql and GROUP_CONCAT (MySQL) and select all brand ids for client in one row separated with commas. 1,2,3,4,5,20,45,102.

旧瑾黎汐 2024-11-24 12:07:58

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

$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 并检索每个品牌的所有相关项目。

If you don't want to use CActiveRecord using with() functionality, then you should write one SQL query joining brand table.

$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'];
}

This is much more efficient than doing one query to retrieve all clients and then doing N queries to fetch their brands (where N is the number of clients). You could also join your third table projects and retrieve all related projects for each brand.

凡间太子 2024-11-24 12:07:58

我意识到这已经过时了,但我自己正在寻找一种解决方案,并且我认为这是一个很好的解决方案。

在我的基本 Controller 类 (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 是递归调用的。

希望这对某人有帮助。

I realize this is old, but I was looking for a solution myself and I thought it was a good one.

In my base Controller class (protected/Components/Controller.php) I added the following functions:

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;
}

Now, calling renderJsonDeep on an object, or array of objects, will encode the object(s) in JSON including any of the relations you have pulled, like by adding them to the 'with' param in the DbCriteria.

If the child object has any relationships, those will be set in the JSON as well since getAttributesDeep is called recursively.

Hope this helps someone.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文