Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CakePHP declaring Model Associations within a Behavior

Tags:

php

cakephp

I am building a "Notification" system and am working on a Notifiable Behavior which I aim to attach to various objects -- Posts, Transactions, Friends, etc. While I had this working using manual associations declared within each model, I was hoping that I could have each model $actsAs Notifiable and dynamically declare the model associations from within there. This would save a fair amount of lines of code and make it a little more expandable in the future.

Model structure is pretty much this as an example:

Post

id
post_content

Notification

id
type
parent_id

The aim is to associate these models (and any other 'Notifiable' model) by saying: Post hasMany Notification WHERE Notification.type = 'Post' AND Notification.parent_id = Post.id or something to that effect.

I'm running into problems. The first is that the SQL is returning an error that the associated columns are not found in the model referenced. The second is that my contains() function is not able to find the associated models either.

Post Model

class Post extends AppModel {
    public $name = 'Post'
    public $actsAs = array( 'Containable', 'Notifiable' );
}

Notifiable Behavior

    public function setup(Model $Model, $settings = array()) {
    if (!isset($this->settings[$Model->alias])) {
        $this->settings[$Model->alias] = array();
    }
    $this->settings[$Model->alias] = array_merge($this->settings[$Model->alias], $settings);
    $Model->bindModel( array(
        'hasMany' => array(
            'Notification' => array(
                'className' => 'Notification',
                'foreignKey' => 'parent_id',
                'conditions' => array(
                    'Notification.type = "' . $Model->name . '"',
                ),
                'dependent' => true,
            ),
        ),
    ), false );
    $Model->Notification->bindModel( array(
        'belongsTo' => array(
            $Model->name => array(
                'className' => $Model->name,
                'foreignKey' => 'parent_id',
                'conditions' => array(
                    'Notification.type = "' . $Model->name . '"',
                )
            )
        )
    ), false );
}            

Am I going about this the wrong way and/or is my understanding of what Behaviors are for a little skewed? I know that bindModel will work until the end of the request with false set as the second variable. However, are these associations not accessible globally when declared within a behavior?

In other words, if I have a NotificationsController which has:

'contain' => array( 
    'Friend',
    'Post',
    'Transaction'
)

Should this work if those Models actAs Notifiable? Like I said, I can get it to work if I manually declare the model associations within each model. But this becomes cumbersome in the case of the Notification model, which I must then declare for every single Notifiable model:

$belongsTo = array(
    'ModelAlias' => array(
        'className' => 'Model',
        'foreignKey' => 'parent_id',
        'conditions' => array(
            'Notification.type' => 'Model'
        )
    )
);

So in the terms of scalability, I'd prefer not to have to continue to manually declare these associations every time I add a new model that I decide should be notifiable.

I hope I've explained that well enough. I'm still getting the grip of CakePHP so please let me know if I'm going about this all wrong.

Thanks!

EDIT:

To simplify the description of the problem and error:

I have Transaction actsAs Notifiable . The code for my Notifiable behavior is still as it appears above. The intended function is to declare Transaction hasMany Notification and Notification belongsTo Transaction. In my TransactionsController I attempt to Paginate the Notifications model using the following:

$this->Paginator->settings = array(
    'contain' => array(
        'Transaction',
    )
    'limit' => 5,
);
$this->notifications = $this->Paginator->paginate( 'Notification' );
$this->set( 'notifications', $this->notifications );

And what I receive is a set of Notification models that do not have any of their associated belongsTo Models attached, along with the error:

Notice (8): Undefined index: Transaction [APP/View/Notifications/index.ctp, line 10]

The same occurs with every Notifiable Model that I have, unless I explicitly instantiate them elsewhere in the app and/or attach it manually to the Notifiable model.

I feel it may have to do with the declaration of $Model->Notification->belongsTo, which assigns the Notification explicitly to the Model, rather than declaring the Notification model itself as having that association, if that makes any sense at all.

Does that help clarify the problem?

EDIT:

I have found that if I explicitly call $this->Transaction->create(); (or create() on any other Notifiable model), within my NotificationsController before I run $this->paginate();, the association is returned correctly. This, however kind of defeats the purpose of the automation I am trying to create if I have to manually create/declare each Notifiable object before running the query.

like image 271
user3551652 Avatar asked May 26 '26 22:05

user3551652


1 Answers

I have found when I declare associations like this in a behavior it seems to work more "automatically" like you wanted. Ran into the same problem as you recently. I think this might be a fix. Try it out!

public function setup(Model $Model, $settings = array()) {
    $Model->hasMany = array(
      //set associations here
    );
}
like image 121
Royalty Avatar answered May 28 '26 15:05

Royalty



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!