4

In Yii, I need to add a "derived" column to any resultset from my model. The column doesn't actually exist in the database table.

For example, say I have an Activity model. There are only two types of Activities: (1) Income, or (2) Expense.

If I want to add a column called income_total or expense_total (depending on what type of activity is being accessed), how would I do that?

Here's a working example of an Activity model in Ruby on Rails (I'm basically wondering how to do the same thing, but in Yii):

class Activity < ActiveRecord::Base

  attr_accessible :name, :effective_at, :amount, :category
  scope :incomes,  :conditions => { :category => 'Income'  }
  scope :expenses, :conditions => { :category => 'Expense' }

  def self.incomes_total
    incomes.sum :amount
  end

  def self.expenses_total
    expenses.sum :amount
  end

end

Update 2012-07-01:

The answer provided by Leonardo points to the use of Virtual Attributes, this adds an attribute to each "row" of the resultset that I'm retrieving from the database.

If the Activity model has a BELONGS_TO relationship with Parent, a Parent View may look like the following:

<h2><?php echo $parent->name; ?></h2>
<ul>
  <?php foreach($portfolio->activities as $activity): ?>
    <li>
      <?php echo CHtml::link($activity->name, array('activity/view', 'id' => $activity->id)); ?>
      <?php echo $activity->amount; ?>
      <?php echo $activity->incomes_total; ?>
    </li>
  <?php endforeach; ?>
</ul>

However, it doesn't make sense for this Virtual Attribute to be accessed within the foreach() loop.

These Virtual Attributes that I want provide one "aggregated" value for the whole resultset, so I want to be able access it using $parent->activities->incomes_total, like so:

<h2><?php echo $parent->name; ?></h2>
<?php echo $parent->activities->incomes_total; ?>
<ul>
  <?php foreach($portfolio->activities as $activity): ?>
    <li>
      <?php echo CHtml::link($activity->name, array('activity/view', 'id' => $activity->id)); ?>
      <?php echo $activity->amount; ?>
    </li>
  <?php endforeach; ?>
</ul>

What do I need to do within the Activity model code to achieve this, or should I be thinking about this a different way?

Community
  • 1
  • 1
Turgs
  • 1,729
  • 1
  • 20
  • 49
  • I've just seen that Yii has an implementation of [Named Scopes](http://www.yiiframework.com/doc/guide/1.1/en/database.ar#named-scopes) too. So I'm reading into that. It may point me in the right direction. – Turgs Jun 15 '12 at 04:08

3 Answers3

3

I believe it is pretty similar to Ruby. PHP has magic methods, which in Yii is implemented by CComponent (base class of many including CActiveRecord).

In short you can do this within your model

   class Activity extends CActiveRecord {
     public function getIncome_Total() {
       // ...
     }
   }

And, to access it from controller

$activity = Activity::model->findByPk(1);
$income_total = $activity->income_total;
  • I had no idea that could be done. Great. Yii calls it "[Virtual Attributes](http://www.yiiframework.com/wiki/167/understanding-virtual-attributes-and-get-set-methods/#hh0)" – Turgs Jun 15 '12 at 05:01
  • This helps there the virtual attribute is specific for each CActiveRecord instance, however I need the virtual attribute span across the whole resultset. I've updated the question to reflect this detail. – Turgs Jul 02 '12 at 06:08
0

I don't know how this can be done within the model.

Although it is more processing, an option or workaround is to add another foreach() loop to the View like so:

$incomes_total = 0;
foreach($parent->activities as $activity)
  $incomes_total += $activity->amount;
echo $incomes_total;
Turgs
  • 1,729
  • 1
  • 20
  • 49
0

As I posted in the other answer You can simply use a stat relation. Specify a STAT relation in Parent model as

'incomes_total'=>array(self::STAT, 'Activity', 'parent_id','select'=>'SUM(amount)','condition'=>"incomes_total.category='Income'")

Similiarly

'expense_total'=>array(self::STAT, 'Activity', 'parent_id','select'=>'SUM(amount)','condition'=>"incomes_total.category='Expense'")

NOTE: Change the attribute names parent_id and amount as in your model

Community
  • 1
  • 1
dInGd0nG
  • 4,162
  • 1
  • 24
  • 37
  • I've tried to play with it but I need this to be in the Activity model rather than in the Parent model so that in the Activity actionIndex() View, I can retrieve the total there too. – Turgs Jul 02 '12 at 10:19
  • @Turgs I'm not clear what you said. Please specify exactly what you want so that i can edit my answer – dInGd0nG Jul 02 '12 at 11:36
  • Rather than putting the STAT relation in the Parent model, I need it to be defined somehow within the Activity model. Almost like a STAT relation to itself. That said, I don't like how this STAT relation approach will trigger a separate database query. I would ultimately like a way that uses all the data that's already been retrieved. – Turgs Jul 02 '12 at 12:15
  • 1
    @Turgs You are wrong with the sentance `all the data that's already been retrieved`. Yii doesn't retrieve any related models when the object is initialized. It only retrieves it when it is accessed. And I have no idea how to make a STAT relation to the same class. May be its possible – dInGd0nG Jul 02 '12 at 15:23
  • I guess I'm not trying to retrieve the data when the object is initialised. I'm just wanting something within the same class, so it's available in the views for that model as a CActiveDataProvider('Activity') object. Although the data isn't available when the Activity model is initialised, it is available **at the time** the data has been retrieved. Maybe what I'm looking for is hard due to a PHP limitation. The way this can be done in Ruby (as shown in my question) is easy using a "self" method. For now, using a simple foreach to do the SUM when needed seems simplest. – Turgs Jul 03 '12 at 12:16