Search code examples
cakephphas-and-belongs-to-manymodel-associations

CakePHP 2.4: Setting up models with both a HABTM assoc. and a belongsTo assoc. via a model alias and subsequently have saveAssociated() work


I'm stuck on what I've overlooked (or fundamentally misunderstood!) when trying to build this:

Accounts HABTM Users and
Administrators belongsTo Accounts, where
Administrator is an alias for User.

I am pretty awful at RDBMS so to further clarify:

Account Fields: id, name, administrator_id, etc.
User Fields: id, name, dob, etc.
AccountsUsers Fields: id, user_id, account_id

As for model associations, I have:
Users

    public $hasOne = array(
        'Account' => array(
            'className' => 'Account',
            'foreignKey' => 'administrator_id'
        )
    );

    public $hasAndBelongsToMany = array(
        'Account' =>
            array(
                'className' => 'Account',
                'joinTable' => 'users_accounts',
                'foreignKey' => 'user_id',
                'associationForeignKey' => 'account_id',
                'unique' => true,
                'conditions' => '',
                'fields' => '',
                'order' => '',
                'limit' => '',
                'offset' => '',
                'finderQuery' => '',
                'with' => ''
            )
    );

Accounts

public $belongsTo = array(
        'Administrator' => array(
            'className' => 'User',
            'foreignKey' => 'id',
            'conditions' => '',
            'fields' => '',
            'order' => ''
        )
    );
public $hasAndBelongsToMany = array(
    'User' => array(
        'className' => 'User',
        'joinTable' => 'users_accounts',
        'foreignKey' => 'account_id',
        'associationForeignKey' => 'user_id',
        'unique' => 'keepExisting',
        'conditions' => '',
        'fields' => '',
        'order' => '',
        'limit' => '',
        'offset' => '',
        'finderQuery' => '',
    )
);

My Accounts controller, though, was baked to produce code like:

$this->Administrator->find('list');

But of course there isn't an Administrator's controller, so I am left with:

Error: Call to a member function find() on a non-object
File: path/to/app/Controller/AccountsController.php

I have ACL set up independently; 'administrator' in this case isn't standing in for a class of user in terms of authorization, just association—it is, perhaps, more like 'owner'. I don't have reason to create a separate model for them, I just want to distinguish the one user who created the account from other users who will use it (and of course, the creator will almost certainly use it as well). I'd be happy to just leave the field as Accounts.user_id on the database if this won't cause problems, but it seems (to me) bound to.

An additional problem is that I can't seem to get my UsersController to work with
$this->User->saveAssociated($this->request->data)
.

Here's my view file:

<?php echo $this->Form->create('User', array('action' => "register/$token_id/$group_id/$account_id")); ?>
    <fieldset>
        <legend><?php echo __('Registration'); ?></legend>
    <?php
        echo $this->Form->input('email');
        echo $this->Form->input('password');
        echo $this->Form->input('firstname');
        echo $this->Form->input('lastname');
        echo $this->Form->input('claimed', array('type'=>'hidden', 'value'=>1));
        echo $this->Form->input('studentno');
        echo $this->Form->input('UsersAccount.account_id', array('type'=>'hidden', 'value'=>$account_id));
    ?>
    </fieldset>
<?php echo $this->Form->end(__('Submit')); ?>

It was my understanding that saveAssociated() was precisely for avoiding having to manually do something like:

$UserId = $this->User->getLastInsertID();
$AccountId = $this->request->data['UsersAccount']['id'];
$this->User->loadModel('UsersAccount');
$this->UsersAccount->create();
$this->UsersAccount->save(array('UsersAccount' => array(
                                        'user_id' => $UserId, 
                                        'account_id' => $AccountId)
));

I'm sure that my error is primarily with understanding the relationships (ie. there's a logical problem, here, not a syntax one). I've read the Cake manual pretty extensively, but I just can't figure out how Cake wants this done, and I'd rather learn the right way than build hacks that circumvent Cake's elegance. So, help hugely appreciated. :)


Solution

    • in your Accounts controller, expect you explicitly delcare your $uses array, you'd have to go through the Accounts model to pull your Administrator model..

      $this->Account->Administrator->find('list');

    • you'd have to set your deep key to true in the saveAssociated param for it to work with associations.

      $this->User->saveAssociated($this->request->data, array('deep' => true));

    NOTE: saveAssociated works on all associations except HABTM