tags:

views:

45

answers:

4

I have a datamodel, let's say: invoices (m:n) invoice_items and currently I store the invoice total, calculated in PHP by totalling invoice_items, in a column in invoices. I don't like storing derived data as it paves the way for errors later.

How can I create a logical column in the invoices table in MySql? Is this something I would be better handling in the PHP (in this case CakePHP)?

+1  A: 

Either you can return the derived one when you want it via

SELECT COUNT(1) as total FROM invoice_items

Or if invoices can be multiple,

//assuming that invoice_items.num is how many there are per row
SELECT SUM(num) as total FROM invoice_items

Or you can use a VIEW, if you have a certain way you want it represented all the time.

zebediah49
This works great, but is programatic solution. Is there a way to store that query as a column?
Leo
+1  A: 

http://forge.mysql.com/wiki/MySQL_virtual_columns_preview

It's not implemented yet, but it should be implemented in mysql 6.0

Currently you could create a view.

Naktibalda
Come v6 that could be an option...I like views, but I really wanted this as a field in the record.
Leo
+2  A: 

Leo,

One thing you could do is to modify the afterFind() method in your model. This would recalculate the total any time you retrieve an invoice (costing runtime processing), but would mean you're not storing it in the invoices table, which is apparently what you want to avoid (correct if I'm wrong).

Try this:

class Invoice extends AppModel {
// .. other stuff
 function afterFind() {
  parent::afterFind();
  $total = 0;
  foreach( $this->data['Invoice']['InvoiceItems'] as $item )
   $total += ($item['cost'] * $item['quantity']);

  $this->data['Invoice']['total'] = $total;
 }
}

I may have messed up the arrays on the hasMany relationship (the foreach line), but I hope you get the jist of it. HTH,

Travis

Travis Leleu
This is a good suggestion. Although if there are lots of records it could become unwieldy, but at least it's a `CakeWay`
DavidYell
I think I'll probably go with this - thanks. At least I will when I have rebuilt my ubuntu box which mysteriously ate about 15GB of disk before my eyes. Could be something to do with the power out we had yesterday :(
Leo
I like the way this solution fakes up the table the Cake way.
Leo
I would call this more of a hack than the "Cake" way. See my answer on Virtual Fields.
Michael
Agreed, if you're doing something simple like this the virtual fields method might be better. I think the afterFind() gives you a little more flexibility, which could be good/bad depending on the coder/application. Upvoted your answer, placer.
Travis Leleu
@Travis: Thanks. However, I would argue that you can get as much flexibility out of the virtualFields as you would in afterFind. I think the real question is how the field would be used. The benefits of virtualFields over afterFind (or viceversa) in a scenario such as this is unclear and probably one is just as good as the other.
Michael
+3  A: 

There's something called Virtual Fields in CakePHP which allows you to achieve the same result from within your Model instead of relying on support from MySQL. Virtual Fields allow you to "mashup" various data within your model and provide that as an additional column in your record. It's cleaner than the other approaches here...(no afterFind() hacking).

Read more here: http://book.cakephp.org/view/1608/Virtual-fields

Michael
1.3 only, but worth knowing about.
Leo