views:

418

answers:

2

I've got a problem with a belongsTo relationship in CakePHP.

I've got an "Action" model that uses the "actions" table and belongs to one of two other models, either "Transaction" or "Tag". The idea being that whenever a user completes a transaction or adds a tag, the action model is created to keep a log of it. I've got that part working, whenever a Transaction or Tag is saved, the aftersave() method also adds an Action record. The problem comes when I try to do a find('all') on the Action model, the related Transaction or Tag record is not being returned.

actions: id model model_id created

I thought I could use the "conditions" parameter in the belongsTo relationship like this:

<?php
class Action extends AppModel {

var $name = 'Action';
var $actsAs = array('Containable');
var $belongsTo = array(
    'Transaction' => array(
        'foreignKey' => 'model_id',
        'conditions' => array("Action.model"=>"Transaction")
    ),
    'User' => array(
        'fields' => array('User.username') 
    ),
    'Recommendation' => array(
        'conditions' => array("Action.model"=>"Recommendation"),
        'foreignKey' => 'model_id'
    )
);  
}
?>

But that doesn't work.

Am I missing something here, are my relationships wrong (I suspect so)? After Googling this problem I cam across something called Polymorphic Behaviour but I'm not sure this will help me.

A: 

So if I'm correct you want to save data automatically to the Actions table? Maybe other users will give the answer that it is possible, but as far as I know this isn't.

Maybe it's a good idea to look at the model callback 'afterSave()'. Here you can do a save() after the other query is done.

JanWillem
No I've got the aftersave thing working for the Transaction and Recommendation models. The Action record is saved find. But when do a find('all') on the Action model I want to bring in the associated Transaction *OR* Recommendation model, depending on which type of action it is.
gomezuk
+1  A: 

If I understand correctly, it sounds like the Polymorphic Behavior is a great answer. It allows you to tie a give Action record to any other model (in your case, a Transaction or Tag). If you then retrieve an Action--and memory serves (I've only used this once)--then the associated model will come back too (assuming the appropriate recursive setting).

As an example, in a recent project I built an Alert model. Alerts could be attached to any model. Any given alert record identified which model it belonged to and the appropriate record ID:

class Alert extends AppModel {
  public $actsAs    = array (
    'Polymorphic' => array (
      'classField' => 'model',
      'foreignKey' => 'entity_id'
    )
  );

  # Additional model code
}

And my table was built to the following spec:

CREATE TABLE alerts (
  id CHAR(36) NOT NULL,
  alert_template_id VARCHAR(255) NOT NULL,
  model VARCHAR(255) NOT NULL,
  entity_id CHAR(36) NOT NULL,
  valid_from BIGINT NULL,
  valid_to BIGINT NULL,
  replacement_keys TEXT NULL,
  active BOOL NOT NULL DEFAULT 0,
  created BIGINT NOT NULL,
  updated BIGINT NOT NULL,
  PRIMARY KEY(id),
  FOREIGN KEY(alert_template_id)
    REFERENCES alert_templates(id)
      ON DELETE NO ACTION
      ON UPDATE NO ACTION
)TYPE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

Note the entity_id and model fields. For each Alert record, these hold a unique identifier and model name (e.g. User), respectively. It sounds like this would meet your needs nicely.

Rob Wilkerson
Great answer Rob! I wanted to read more oh PM behavior so I looked @ the bakery, here's the link: http://bakery.cakephp.org/articles/view/polymorphic-behavior
Travis Leleu
Thank you very much. That's exactly the answer I was hoping for.
gomezuk
Thanks for adding a link, Travis. I was sure that I'd linked to it in my answer, but it appears not. I appreciate you correcting that.
Rob Wilkerson