edit - the code that uses contain.
$this->paginate = array(
'limit' => 20,
'order' => array(
'Individual.last_name' => 'asc',
'Individual.first_name' => 'asc'
),
'contain' => array(
'AccountsTrailersIndividual' => array(
'fields' => array('id', 'accounts_trailer_id', 'individual_id', 'account_holder_id'),
'AccountsTrailer' => array(
'fields' => array('id', 'account_id', 'trailer_id', 'charge_off_date', 'closed_date'),
'Balance' => array(
'fields' => array('id', 'accounts_trailer_id', 'balance')
),
'Account' => array(
'fields' => array('id', 'account_number'),
'conditions' => array('account_number' => $search)
),
),
'AccountHolder' => array(
'fields' => array('id', 'name')
)
),
),
'fields' => array('id', 'first_name', 'middle_initial', 'last_name', 'suffix'),
);
I am trying to contain my data to only pull what I need and it is not working. I have everything set up correctly I believe but it is giving me errors. What I have is in my appModel.php file
public $actsAs = array('Containable');
the errors I am getting are as follows:
Model "AccountsTrailersIndividual" is not associated with model "AccountsTrailer" [CORE\Cake\Model\Behavior\ContainableBehavior.php, line 339]
Model "AccountsTrailersIndividual" is not associated with model "AccountHolder" [CORE\Cake\Model\Behavior\ContainableBehavior.php, line 339]
I have checked the associations in the stated models and the associations are there.
for AccountsTrailersIndividual it looks like this
public $belongsTo = array(
'Individual' => array(
'className' => 'Individual',
'foreignKey' => 'individual_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'AccountsTrailer' => array(
'className' => 'AccountsTrailer',
'foreignKey' => 'accounts_trailer_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'AccountHolder' => array(
'className' => 'AccountHolder',
'foreignKey' => 'account_holder_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
for AccountsTrailer
public $hasAndBelongsToMany = array(
'Individual' => array(
'className' => 'Individual',
'joinTable' => 'accounts_trailers_individuals',
'foreignKey' => 'accounts_trailer_id',
'associationForeignKey' => 'individual_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
and for the AccountHolder
public $hasMany = array(
'AccountsTrailersIndividual' => array(
'className' => 'AccountsTrailersIndividual',
'foreignKey' => 'account_holder_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
I know that I must be missing something but I can't find it.
Any help is greatly appreciated.
Related
I have 5 tables: tournaments, rounds, videos, rounds_tournaments, rounds_videos
Relations:
Tournament hasMany Video
Video belongsTo a Tournament
Tournament HABTM Round
Round HABTM Tournament
Video HABTM Round
Round HABTM Video
What I want to achieve:
Now I only want the videos that belong to a specific Tournament and Round.
So when clicking on a Tournament I only want the rounds associated with the clicked Tournament. And when clicking on a Round I only want the get the videos associated with the clicked Round and the Tournament.
I've tried everything but can't get it to work. I've played with several views as well (the index, view actions of Round/Tournament/Video). I'm not sure where to put the the JOIN statement, but I think it's inside the controller.
The Problem I've got atm is that when clicking on a Round it gives me the videos of that Round but for all the Tournaments and not for one Tournament.
Also looked here:
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#joining-tables
CakePHP find HABTM
Round Model:
public $hasAndBelongsToMany = array(
'Tournament' => array(
'className' => 'Tournament',
'joinTable' => 'rounds_tournaments',
'foreignKey' => 'round_id',
'associationForeignKey' => 'tournament_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
),
'Video' => array(
'className' => 'Video',
'joinTable' => 'rounds_videos',
'foreignKey' => 'round_id',
'associationForeignKey' => 'video_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
)
);
Video Model:
public $belongsTo = array(
'Tournament' => array(
'className' => 'Tournament',
'foreignKey' => 'tournament_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
public $hasAndBelongsToMany = array(
'Round' => array(
'className' => 'Round',
'joinTable' => 'rounds_videos',
'foreignKey' => 'video_id',
'associationForeignKey' => 'round_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
)
);
Tournament Model:
public $hasMany = array(
'Video' => array(
'className' => 'Video',
'foreignKey' => 'tournament_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
public $hasAndBelongsToMany = array(
'Round' => array(
'className' => 'Round',
'joinTable' => 'rounds_tournaments',
'foreignKey' => 'tournament_id',
'associationForeignKey' => 'round_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
),
);
RoundsVideo model:
public $belongsTo = array(
'Round' => array(
'className' => 'Round',
'foreignKey' => 'round_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Video' => array(
'className' => 'Video',
'foreignKey' => 'video_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
RoundsTournament model:
public $belongsTo = array(
'Round' => array(
'className' => 'Round',
'foreignKey' => 'round_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Tournament' => array(
'className' => 'Tournament',
'foreignKey' => 'tournament_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
What I have tried:
index action of VideosController
$this->Video->recursive = -1;
$options['joins'] = array(
array('table' => 'rounds_videos',
'alias' => 'RoundsVideo',
'type' => 'inner',
'conditions' => array(
'Video.video_id = RoundsVideo.video_id'
)
),
array('table' => 'rounds',
'alias' => 'Round',
'type' => 'inner',
'conditions' => array(
'RoundsVideo.round_id = Round.round_id'
)
)
);
$this->set('videos', $this->Paginator->paginate());
index action of TournamentsController
$this->Tournament->recursive = -1;
$options['joins'] = array(
array('table' => 'rounds_tournaments',
'alias' => 'RoundsTournament',
'type' => 'inner',
'conditions' => array(
'Tournament.tournament_id = RoundsTournament.tournament_id'
)
),
array('table' => 'rounds',
'alias' => 'Round',
'type' => 'inner',
'conditions' => array(
'RoundsTournament.round_id = Round.round_id'
)
)
);
$this->set('tournaments', $this->Paginator->paginate());
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' => ''
)
);
I am using these three relations User, Movie and UsersWatchlist.. Where users_watchlists contains movie_id and user_id as attributes.. I have this structure
array(
'User' => array(
'id' => '3'
),
'UsersWatchlist' => array(
'UsersWatchlist' => array(
(int) 0 => '3'
)
)
)
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']['User'][] = $userid;
$this->request->data['Movie']['Movie'][] = $id;
debug($this->request->data);
if ($this->User->saveAll($this->request->data)) {
debug("saved");
$this->Session->setFlash('The movie has been added to your watchlist', 'admin/flash_success');
//$this->redirect($this->referer(array('action' => 'listing')));
} else {
debug("saved bo");
$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')));
}
}
I used saveAll method on User but its not saving.. Please provide me solution to save the data..
In Movie model,
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' => ''
)
);
In User model,
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' => ''
)
);
In UsersWatchlist model,
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Movie' => array(
'className' => 'Movie',
'foreignKey' => 'movie_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
The data array you're trying to save is wrong.
If you're saving throung the User model your structure should be:
array(
'User' => array(
'id' => '3'
),
'Movie' => array(
'Movie' => array(
(int) 0 => '3'
)
)
<?php
class Product extends AppModel {
var $name = 'Product';
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $belongsTo = array(
'Category' => array(
'className' => 'Category',
'foreignKey' => 'category_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Brand'
);
var $hasOne = array(
'PhotoSmall' => array(
'className' => 'Photo',
'foreignKey' => 'product_id',
'dependent' => true,
'conditions' => 'PhotoSmall.tipo = "small"',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
),
);
var $hasMany = array(
'PhotoBig' => array(
'className' => 'Photo',
'foreignKey' => 'product_id',
'dependent' => true,
'conditions' => 'PhotoBig.tipo = "big"',
'fields' => '',
'order' => 'PhotoBig.order',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
),
'Rating' => array(
'className' => 'Rating',
'foreignKey' => 'product_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
I need all my finds (except in the admin area) to retrieve only products with images.
In the case of PhotoSmall is easy, I can do PhotoSmall.id IS NOT NULL in a condition because CakePhp generates a left join, but i can not find a way to require a least on PhotoBig because CakePhp does two queries to return its results.
The requirement is the following, I can only show products with a PhotoSmall and a least one PhotoBig system wide (except for the admin section of the site), the solution should work with CakePhp pagination
I've tried the following code without success, it only returns product data, not photo data:
$this->Product->recursive = -1;
$conditions = array('Product.category_id'=> $cats);
$joins = array(
array('table' => 'photos',
'alias' => 'PhotoBig',
'type' => 'INNER',
'conditions' => array(
'Product.id = PhotoBig.product_id',
)
),
array('table' => 'photos',
'alias' => 'PhotoSmall',
'type' => 'INNER',
'conditions' => array(
'Product.id = PhotoBig.product_id',
)
)
);
$this->paginate = array(
'limit' => 12,
'conditions' => $conditions,
'joins' => $joins,
);
you need to additionally use contain - at least that worked for me
(due to your recursive=-1):
'contain' => array('PhotoSmall', 'PhotoBig'),
the joins are only the setup configuration. without specifying your data you will only get the one model's data.
do you use the containable behavior? would be the easiest solution.
if not, try to set recursive to 0 or 1.
PS: and your second alias is wrong inside the 'conditions' (should be PhotoSmall there).
Well firstly you're setting recursive to -1 which should just give you Product data no matter what kind of find you do.
Why don't you do an AND condition in a find on the Product model like so:
$this->Product->find('all', array('recursive' => 1, 'conditions' => array(
"NOT" => array('PhotoSmall.id' => NULL, 'PhotoBig.id' => NULL))));
Note: recursive will need to be at least one because you want related data.
I know the title doesn't describe the best what I want to achieve, but I couldn't think of anything else.
Let's say I have the following array:
[Sender] => Array
(
[0] => Array
(
[id] => 1
[username] => admin
[PrivateMessagesUser] => Array
(
[id] => 1
[private_message_id] => 1
[sender_id] => 1
[recipient_id] => 3
[sender_status] => 1
[recipient_status] => 0
[random_key] =>
)
)
[1] => Array
(
[id] => 1
[username] => admin
[PrivateMessagesUser] => Array
(
[id] => 3
[private_message_id] => 1
[sender_id] => 1
[recipient_id] => 2
[sender_status] => 1
[recipient_status] => 0
[random_key] =>
)
)
)
I am using the ContainableBehaviour to cut off extra data.
My problem is: how do I filter out the Senders after a key which is present in PrivateMessagesUser? Say I need only the Sender where PrivateMessagesUser.recipient_id = 3.
I tried with
'Sender' => array(
'PrivateMessagesUser' => array(
)
),
but it didn't work. Looks like I have to get over the [0], [1]... keys somehow and I don't know how.
Any help would be appreciated!
EDIT
Here's how I obtain the array posted above:
$this->paginate = array(
'conditions' => array(
'PrivateMessage.id' => $this->__getUserPrivateMessagesInbox()
),
'contain' => array(
'Sender' => array(
'fields' => array('id', 'username'),
'PrivateMessagesUser' => array(
'fields' => array('PrivateMessagesUser.id')
//'conditions' => array('PrivateMessagesUser.recipient_id' => $this->Auth->user('id'))
)
),
'Recipient' => array(
'fields' => array('id', 'username'),
'conditions' => array('Recipient.id' => $this->Auth->user('id'))
)
)
);
How can I edit these lines in order to achieve the desired result?
EDIT 2
I am calling the paginate() from the PrivateMessagesController.
Associations:
User Model:
var $hasAndBelongsToMany = array(
'PrivateMessageSent' => array(
'className' => 'PrivateMessage',
'joinTable' => 'private_messages_users',
'foreignKey' => 'sender_id',
'associationForeignKey' => 'private_message_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
),
'PrivateMessageReceived' => array(
'className' => 'PrivateMessage',
'joinTable' => 'private_messages_users',
'foreignKey' => 'recipient_id',
'associationForeignKey' => 'private_message_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
PrivateMessage Model:
var $hasAndBelongsToMany = array(
'Sender' => array(
'className' => 'User',
'joinTable' => 'private_messages_users',
'foreignKey' => 'private_message_id',
'associationForeignKey' => 'sender_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
),
'Recipient' => array(
'className' => 'User',
'joinTable' => 'private_messages_users',
'foreignKey' => 'private_message_id',
'associationForeignKey' => 'recipient_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
),
);
PrivateMessagesUser Model:
var $belongsTo = array(
'PrivateMessage' => array(
'className' => 'PrivateMessage',
'foreignKey' => 'private_message_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Sender' => array(
'className' => 'User',
'foreignKey' => 'sender_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Recipient' => array(
'className' => 'User',
'foreignKey' => 'recipient_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
And the database tables look like:
users: id, username, ...
private_messages: id, subject, ...
private_messages_users: id, private_message_id, sender_id, recipient_id, status, ...
There are three ways to achieve this.
Looking at your returned data array, I'm guessing PrivateMessagesUser belongsTo Sender.
1. Querying the PrivateMessagesUser model
Sometimes, all it takes is querying the right model. You can apply the conditions directly to the model you want to apply your restrictions on.
$this->Sender->PrivateMessagesUser->find('all', array(
'conditions' => array('PrivateMessagesUser.recipient_id' => 3),
'contain' => array('Sender')
));
2. Applying conditions to related models
This doesn't work all the time, because CakePHP's SQL generation doesn't always work the way you want it to. But in simple cases like a hasMany relationship, you can use this:
$this->Sender->find('all', array(
'conditions' => array('PrivateMessagesUser.recipient_id' => 3),
'contain' => array('PrivateMessagesUser')
));
Again, this will work only if Cake executes a JOIN for the Containable parameters AND uses the conditions right.
3. Using joins
This method is foolproof but a little messy since it's highly customizable.
$this->Sender->find('all', array(
'joins' => array(
array(
'table' => 'private_messages_users',
'alias' => 'PrivateMessagesUser',
'type' => 'inner',
'conditions' => array(
'PrivateMessagesUser.sender_id = Sender.id',
'PrivateMessagesUser.recipient_id' => 3
)
)
)
));
My last piece of advice for you is to place this code into the Model. Follow the principle of "fat models, skinny controllers" whenever possible.
In the Sender model:
function fetchByRecipientId($recipientId) {
...
}
Good luck!
EDIT: In order to make this work for pagination, follow the methods above but use the following format:
$this->paginate = array(
'PrivateMessagesUser' => array(
'conditions' => array('PrivateMessagesUser.recipient_id' => 3),
'contain' => array('Sender'),
// 'order' => ... other keys if you wish
)
);
$this->set('senders', $this->paginate());
You can put conditions inside Containable, like so:
$this->Sender->find('all', array(
'contain' => array(
'PrivateMessagesUser' => array('conditions' => array('recipient_id' => 3))
)
));
EDIT:
Try modifying your $this->paginate as follow:
$this->paginate = array(
'conditions' => array(
'PrivateMessage.id' => $this->__getUserPrivateMessagesInbox()//array of message id's
),
'fields' => array('PrivateMessagesUser.id'),
'contain' => array(
'Sender' => array(
'fields' => array('id', 'username'),
),
'Recipient' => array(
'fields' => array('id', 'username'),
'conditions' => array('Recipient.id' => $this->Auth->user('id'))
)
)
)
Then call your paginate $this->paginate($this->PrivateMessage)