CActiveDataProvider with custom value

563 views Asked by At

In my Yii project I need to search and sort data by some custom value. I have 'users' table and I need every User instance to have month_profit property which should combine SQL data + lots of my own calculations. At the moment I have in my User model:

public $month_profit;

public function search($pageSize = 10, $defaultOrder = '`t`.`reg_date` DESC')
{
    $criteria = new CDbCriteria;
    $criteria->with = array('money');
    $requests_table = Requests::model()->tableName();
    $requests_count_sql = "(SELECT COUNT(*) FROM $requests_table rt WHERE rt.partner_id = t.id) ";

    $referrals_table = Referrals::model()->tableName();
    $referrals_count_sql = "(SELECT COUNT(*) FROM $referrals_table reft WHERE reft.user_id = t.id) ";
    $referrals_payed_sql = "(SELECT COUNT(*) FROM $referrals_table reft WHERE reft.user_id = t.id AND reft.status = 'Оплачено') ";
    //$month profit_sql = ???;

    $criteria->select = array(
        '*',
        $requests_count_sql . "as requests_count",
        $referrals_count_sql . "as referrals_count",
        $referrals_payed_sql . "as referrals_payed_count",
        $month_profit_sql . "as month_profit",
    );

    $criteria->compare($requests_count_sql, $this->requests_count);
    $criteria->compare($referrals_count_sql, $this->referrals_count);
    $criteria->compare($referrals_payed_sql, $this->referrals_payed_count);
    $criteria->compare($month_profit_sql, $this->month_profit);

    $criteria->compare('t.id', $this->id);
    $criteria->compare('t.reg_date', $this->reg_date, true);
    $criteria->compare('username', $this->username, true);
    $criteria->compare('password', $this->password, true);
    $criteria->compare('site', $this->site, true);
    $criteria->compare('status', $this->status, true);

    return new CActiveDataProvider(get_class($this), array(
        'criteria' => $criteria,
        'pagination' => array( 'pageSize' => $pageSize ),
        'sort' => array(
            'defaultOrder' => $defaultOrder,
            'attributes' => array(
                'id' => array(
                    'asc' => '`t`.`id` ASC',
                    'desc' => '`t`.`id` DESC',
                ),
                'email' => array(
                    'asc' => '`t`.`email` ASC',
                    'desc' => '`t`.`email` DESC',
                ),
                'requests_count' => array(
                    'asc' => 'requests_count ASC',
                    'desc' => 'requests_count DESC',
                ),
                'referrals_count' => array(
                    'asc' => 'referrals_count ASC',
                    'desc' => 'referrals_count DESC',
                ),
                'referrals_payed_count' => array(
                    'asc' => 'referrals_payed_count ASC',
                    'desc' => 'referrals_payed_count DESC',
                ),
                'money' => array(
                    'asc' => 'money.profit',
                    'desc' => 'money.profit DESC',
                ),
                'fullProfit' => array(
                    'asc' => 'money.full_profit',
                    'desc' => 'money.full_profit DESC',
                ),
                '*',
            ),
        )
    ));
}

E.g. I have a relation in my User model:

public function relations()
{
    return array(
        'clients' => array( self::HAS_MANY, 'Referrals', 'user_id'),

Let's say my month_profit will be equal: count of User's clients registered in last 30 days * 150. I need to somehow pass this data to search() and create CDbCriteria to sort users by month_profit. Is this even real? :) Should I create another function to calculate everything and then pass to search()? All my tries followed to failure so far.

1

There are 1 answers

0
Kostas Mitsarakis On

I suppose you need something like the following, based on this guide http://www.yiiframework.com/wiki/319/searching-and-sorting-by-count-of-related-items-in-cgridview

First make a statistical query in relations at your User model:

'month_profit' => array(self::STAT, 'User', 'id', 'select'=>'COUNT(*) * 150', 'condition'=>'DATE_SUB(CURDATE(), INTERVAL 30 DAY) <= reg_date'),

Then mark "month_profit" attribute as safe in search scenario in rules.

public function rules() {
    return array(

        ...

        array('username, ... , month_profit', 'safe', 'on' => 'search' ),   
    );
}

Add all these where each one is needed in User search() method:

$user_table = User::model()->tableName();
$month_profit_sql = "(SELECT COUNT(*) FROM user_table WHERE DATE_SUB(CURDATE(), INTERVAL 30 DAY) <= reg_date)";

$criteria->select = array(
    '*',
    $requests_count_sql . "as requests_count",
    $referrals_count_sql . "as referrals_count",
    $referrals_payed_sql . "as referrals_payed_count",
    $month_profit_sql . "as month_profit",
);

...
$criteria->compare($month_profit_sql, $this->month_profit);

return new CActiveDataProvider(get_class($this), array(
    'criteria' => $criteria,
    'pagination' => array( 'pageSize' => $pageSize ),
    'sort' => array(
        'defaultOrder' => $defaultOrder,
        'attributes' => array(
            'id' => array(
                'asc' => '`t`.`id` ASC',
                'desc' => '`t`.`id` DESC',
            ),

            ...

            'month_profit' => array(
                'asc' => 'month_profit ASC',
                'desc' => 'month_profit DESC',
            ),

            '*',
        ),
    )
));

Finally, modify your grid:

$this->widget('zii.widgets.grid.CGridView', array(
    'dataProvider' => $model->search(),
    'filter' => $model,
    'columns' => array(
        'username',

        ... ,

        'month_profit',
        array(
            'class'=>'CButtonColumn',
        ),
    ),
));

Please, let me know if that worked.

EDIT If that doesnt' work try it without the statistical query.