Getting data from a $hasAndBelongsToMany relation at CakePHP 2.2 - cakephp

I have 3 tables:
- users
- posts
- Subscriptions (which should be by default posts_users (n-n))
The subscriptions table is the n-n table needed for this relation. I didnt want to call it "posts_users" as it is very confusing for my system because i have more relations between users and posts.
The, to create the hasAndBelongsToMany relation, i did this:
Post (model):
public $hasAndBelongsToMany = array(
'Subscriptions' => array(
'className' => 'User',
'joinTable' => 'subscriptions',
'foreignKey' => 'post_id',
'associationForeignKey' => 'user_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
User (model):
public $hasAndBelongsToMany = array(
'Post' => array(
'className' => 'Post',
'joinTable' => 'subscriptions',
'foreignKey' => 'user_id',
'associationForeignKey' => 'post_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
Lets say i want to find the subscribed posts for the user with id 3.
Is there any way to do a find to retrieve data of subscribed Posts for a user?
In which model should i do the query? How??
Thanks.

Ok. Finally i got it.
You can do the query from any of the two related models. In this case Post or User.
$this->Post->User->find('all', array(
'conditions' => array('User.id' => $this->Session->read('Auth.User.id')),
'contain' => array('Subscriptions')
));
This would return the subscribed tickets (in the subscriptions array) for the current user.

Related

CakePHP adding to multiple tables from one view

can someone provide an explanation for me?
I've got a database where I'm trying to update 4 tables from one page, Organizations, OrganizationsDetails, Account, AccountRoles. The add view itself would contain fields for the Account and the Organization. When I call the OrganizationsController add function, I want to then call add on AccountController, AccountRolesController (set Account as an administrator role), and OrganizationDetailsController (set that Account as the administrator for that specific Organization).
Should I be using components for this? When I call add() on the Organizations, I always intend to perform these steps - never solely create the Organizations entry below. Should I be doing this directly in that function or some other method?
Let me know if I need to explain more, thanks!
EDIT: Added the four models below:
Organization
class Organization extends AppModel {
public $hasMany = array(
'Account' => array(
'className' => 'Account',
'foreignKey' => 'organization_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
public $hasOne = array(
'OrganizationDetail' => array(
'className' => 'OrganizationDetail',
'foreignKey' => 'organization_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
OrganizationDetail
class OrganizationDetail extends AppModel {
public $belongsTo = array(
'Organization' => array(
'className' => 'Organization',
'foreignKey' => 'organization_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Account' => array(
'className' => 'Account',
'foreignKey' => 'account_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
AccountRole
class AccountRole extends AppModel {
public $belongsTo = array(
'Account' => array(
'className' => 'Account',
'foreignKey' => 'account_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Role' => array(
'className' => 'Role',
'foreignKey' => 'role_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
Account
class Account extends AppModel {
public $belongsTo = array(
'Organization' => array(
'className' => 'Organization',
'foreignKey' => 'organization_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
public $hasOne = array(
'OrganizationDetail' => array(
'className' => 'OrganizationDetail',
'foreignKey' => 'account_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
public $hasMany = array(
'AccountRole' => array(
'className' => 'AccountRole',
'foreignKey' => 'account_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
You've got wrong the whole MVC idea. Controllers should not call methods in other controllers (or at least not in this scenario).
The right approach would be to set up OrganizationsController->add() so that it saves all the data by independent calls to each Model->save($data), or by using
Organization->saveAssociated($data,array('deep' => true))
Your form fields need to be named properly for this to work. For example:
echo $this->Form->create('Organization', array('action' => 'add'));
echo $this->Form->input('Organization.name'));
echo $this->Form->input('Account.0.name', array('label' => 'Account name'));
echo $this->Form->input('OrganizationDetail.details', array('label' => 'Org. Details'));
echo $this->Form->input('Account.0.AccountRole.id', array('label' => 'Account Role'));
echo $this->Form->end('Add');
However, you may get into trouble if you try to go too many levels deep. If you find yourself in this situation, it's probably because you data model is flawed.
This is what your data model currently looks like:
This is what I believe it should look like:
It is critical to have your data model right before you start coding. It can save you many headaches!

HABTM associations with another table

I'm in trouble with a more complex situation of HABTM in Cakephp. Ina nutshel, the situation is: a have users and movies, the relationship between then is HABTM, but i also need a relationship types.
USERS - HABTM - MOVIES
|
RELATION_TYPES
The relationship between users and movies could be many different types: author type, public type, etc.
When i try to use the contain behavior, i get an error message that says:
Model "MoviesUser" is not associated with model "RelationType".
But all the models are associated.
These are the models:
User Model
public $hasAndBelongsToMany = array(
'Movie' => array(
'className' => 'Movie',
'joinTable' => 'movies_users',
'foreignKey' => 'user_id',
'associationForeignKey' => 'movie_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => 'Movie.title',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
Movie Model
public $hasAndBelongsToMany = array(
'User' => array(
'className' => 'User',
'joinTable' => 'movies_users',
'foreignKey' => 'movie_id',
'associationForeignKey' => 'user_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
MoviesUser Model
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Movie' => array(
'className' => 'Movie',
'foreignKey' => 'movie_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'RelationType' => array(
'className' => 'RelationType',
'foreignKey' => 'relation_type_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
In the MoviesController, i'm trying to get some movies and the relation with users and the type of the relation. Here is where i call the Contain on Movies Controller:
$this->Movie->contain(array(
'Person' => array(
'order' => array(
'Person.name'=>'ASC'
),
'Activity'
),
'Country' => array(
'order'=>array(
'Country.name'=>'ASC'
)
),
'User'=>array(
'conditions'=>array(
'User.id'=>$this->Session->read('Auth.User.id')
),
'MoviesUser'=>array(
'RelationType'
)
)
));
When i try to run this i get the message Model "MoviesUser" is not associated with the model "RelationType".
The fields of movies_user are:
id, user_id, relation_type_id, rate, comment
The fields of relation_types are:
id, title
And here my $has_many property on RelationType Model:
public $hasMany = array(
'MoviesUser' => array(
'className' => 'MoviesUser',
'foreignKey' => 'relation_type_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);

Overriding CakePHP conventions. One table with 2 columns of the same model

Suppose I have a table/model Products.
Suppose I have a table/model Equivalencies which has among its columns this 2:
original_id
equivalent_id
Both original_id and equivalent_id have as foreign keys the primary key of Products. So, I can't follow the convention of naming original_id as product_id, because I also have to deal with equivalent_id (which would also be product_id).
What should I do?
Currently, I have the Product model configured as this:
public $hasMany = array(
'Original' => array(
'className' => 'Equivalency',
'foreignKey' => 'original_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => '',
'table' => 'products',
),
'Equivalent' => array(
'className' => 'Equivalency',
'foreignKey' => 'equivalent_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
),
);
Could anyone tell me if this is how it's supposed to be done?
What I Want
I want to be able to find all the equivalencies of a given product, but with conditions over the relation (the one with original_id, equivalent_id).
This doesn't work:
$original = $this->Product->find('all',
array(
'contain' => 'Product.Original.deleted_equivalent = false',
'conditions' => array('Product.id' => $id)
));
Your model is fine, that's the way you should do it.
Your call to find is not.
Try this
$original = $this->Product->find('all',
array(
'contain' => array('Equivalent'),
'conditions' => array('Product.id' => $id)
));

CakePHP 2.1 saving HABTM fields

I have two models User and Movie.. Where those are associated with UsersWatchlist..
public $hasAndBelongsToMany = array('User' => array(
'className' => 'User',
'joinTable' => 'users_watchlists',
'foreignKey' => 'movie_id',
'associationForeignKey' => 'user_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
))
public $hasAndBelongsToMany = array(
'Movie' => array(
'className' => 'Movie',
'joinTable' => 'users_watchlists',
'foreignKey' => 'user_id',
'associationForeignKey' => 'movie_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
))
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Movie' => array(
'className' => 'Movie',
'foreignKey' => 'movie_id',
'conditions' => '',
'fields' => '',
'order' => ''
))
I created 'watchlist' action in UsersController.. The code is below
public function watchlist($id = null) {
$userid = 3;
if (!$id && $userid != 3) {
$this->Session->setFlash('Invalid Movie');
$this->redirect($this->referer(array('action' => 'listing')));
}
$this->request->data['User']['id'] = $userid;
$this->User->UsersWatchlist->create();
debug($this->User->UsersWatchlist->saveAll(array('user_id' => '2', 'movie_id' => 3)));
if ($this->User->UsersWatchlist->saveAll($this->request->data)) {
$this->Session->setFlash('The movie has been added to your watchlist', 'admin/flash_success');
$this->redirect($this->referer(array('action' => 'listing')));
} else {
$this->Session->setFlash('The movie could not be added to your watchlist. Please, try again.', 'admin/flash_error');
$this->redirect($this->referer(array('action' => 'listing')));
}
}
So i am getting an error while saving.. Please tell me the solution
Firstly your data should look like this (provided you want to save it through the user Model):
$this->request->data = array(
'User' => array(
'id' => '2',
),
'Movie' => array(
'Movie' => array(
(int) 0 => '3',
),
),
);
The main mistake I see is that you're trying to save through the Join Model, where you should be saving through the User model. So in the controller use:
$this->User->saveAll($this->request->data)
If the data is as described above it should go fine. I thought I've answered your question here.
Hope this helps.
Cheers!
Your associations seem interesting. Is there a reason you've associated the HABTM join model with the two models (user, movie)? You can achieve the same effect by putting the first $HABTM in the Movie model, and the second $HABTM in the User model. You don't need to create the intermediate join model, cake will do that for you, you only need the table.

CakePHP Syntax error or access violation

I have following relationships
Users(id, name, email, ...)
movies(id, name, ...)
users_watchlists(id, user_id, movie_id)
Its a HABTM relationship.
Error
Error: SQLSTATE[42000]: Syntax error or access violation: 1066 Not
unique table/alias: 'UsersWatchlist'
SQL Query: SELECT UsersWatchlist.id,
UsersWatchlist.first_name, UsersWatchlist.last_name,
UsersWatchlist.display_image, UsersWatchlist.email,
UsersWatchlist.password, UsersWatchlist.birthday,
UsersWatchlist.gender, UsersWatchlist.group_id,
UsersWatchlist.banned, UsersWatchlist.created,
UsersWatchlist.modified, UsersWatchlist.id,
UsersWatchlist.first_name, UsersWatchlist.last_name,
UsersWatchlist.display_image, UsersWatchlist.email,
UsersWatchlist.password, UsersWatchlist.birthday,
UsersWatchlist.gender, UsersWatchlist.group_id,
UsersWatchlist.banned, UsersWatchlist.created,
UsersWatchlist.modified FROM reelstubs.users AS
UsersWatchlist JOIN reelstubs.users AS UsersWatchlist ON
(UsersWatchlist.movie_id = 4 AND UsersWatchlist.user_id =
UsersWatchlist.id)
Notice: If you want to customize this error message, create
app\View\Errors\pdo_error.ctp
Users Model
public $hasAndBelongsToMany = array(
'UsersWatchlist' => array(
'className' => 'Movie',
'joinTable' => 'users_watchlists',
'foreignKey' => 'user_id',
'associationForeignKey' => 'movie_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
Movie Model
public $hasAndBelongsToMany = array(
'UsersWatchlist' => array(
'className' => 'User',
'joinTable' => 'users_watchlists',
'foreignKey' => 'movie_id',
'associationForeignKey' => 'user_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
UsersWatchlist
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Movie' => array(
'className' => 'Movie',
'foreignKey' => 'movie_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
Not sure why its trying to fetch name etc from UsersWatchlist
Not sure why its trying to fetch name etc from UsersWatchlist
Because you have a model for the relation datatable called UsersWatchlist and you named the relations between User and Movie with the same name.
Try to update those names:
User Model
public $hasAndBelongsToMany = array(
'Movie' => array(
...
Movie Model
public $hasAndBelongsToMany = array(
'User' => array(
...
These keys are used as aliases in the SQL query, making it fail in this case.
By the way, if you use the users_watchlists datatable only to maintain a link between Users and Movies (for example if you don't store any properties about the relation), you can probably just get rid of the UsersWatchlist model.

Resources