Лучший способ получить связанные данные из моделей в YII и вернуть JSON - PullRequest
5 голосов
/ 28 июня 2011

Привет, просто вопрос

Я работаю над успокоительным приложением для проекта, который использует 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'];
            }

    }

Это возвращение того, чего я хочу до сих пор, но это самый эффективный способ достичь того, что я после ??

Ответы [ 3 ]

3 голосов
/ 05 июля 2011

Настройка Клиент модель

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

*/

Это ты хотел?Я понимаю, что если вы хотите выбрать только идентификатор бренда (больше никаких данных), вы можете выполнить поиск по sql и GROUP_CONCAT (MySQL) и выбрать все идентификаторы бренда для клиента в одной строке отдельнос запятыми.1,2,3,4,5,20,45,102.

1 голос
/ 16 сентября 2013

Я понимаю, что это старое, но я сам искал решение, и я подумал, что оно хорошее.

В моем базовом классе Controller (protected / Components / Controller.php) я добавил следующее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;
}

Теперь, вызывая renderJsonDeep для объекта или массива объектов, кодируем объект (ы) в JSON, в том числе любые вырванные вами отношения, например, добавляя их в 'with'param в DbCriteria.

Если у дочернего объекта есть какие-либо отношения, они также будут установлены в JSON, поскольку getAttributesDeep вызывается рекурсивно.

Надеюсь, это кому-нибудь поможет.

1 голос
/ 30 июня 2011

Если вы не хотите использовать CActiveRecord с использованием функции with(), то вам нужно написать один 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 и получить все связанные проекты для каждого бренда.

...