I have a pretty simple relational model setup. When using $this->model->find('all',$params), the results do not return the complete relational data set. I'm pulling my hair out here.
Here's my table setup:
Table qs_skus:
id (AI, PK)
sku_name
profile
...
Table net_lengths_in_skus:
id (AI,PK)
quick_ship_skus_id
net_lengths_id
Table net_lengths
id (AI,PK)
name
The way the models are configured are:
Model QuickShipSku $hasMany=array('NetLengthsInSku')
Model NetLengthsInSku $hasOne='NetLength'
All models have $actAs = array('Containable')
When doing the following, I get only the first relationship queried, the last relationship is completely ignored:
$model = $this->QuickShipSku->find('all',
array(
'contain' => array(
'NetLengthsInSku' => array('NetLength')
)
);
Output:
Array
(
[0] => Array
(
[QuickShipSku] => Array
(
[id] => 3
[sku_name] => 1112-8
[product_name] => Product A
[sku_specie_id] => 1
[members_ft] => 8
[profile] => Profile Description
)
[NetLengthsInSku] => Array
(
[0] => Array
(
[id] => 10
[quick_ship_skus_id] => 3
[quick_ship_net_length_id] => 1
)
)
)
)
For each NetLengthsInSku there should be a NetLength, but it's not even being queried.
Any ideas?
What you have here is a many to many relationship. That means an association called hasAndBelongsToMany.
If you are using Cake 2.x you should have something like this:
class QsSku extends AppModel {
public $hasAndBelongsToMany = array(
'NetLength' =>
array(
'className' => 'NetLength',
'joinTable' => 'net_lengths_in_skus',
'foreignKey' => 'quick_ship_skus_id',
'associationForeignKey' => 'net_lengths_id',
)
);
}
You should do something similar to your NetLength model as well.
Related
It's a little complicated to explain the way that I got the data from the view, but it arrives correctly to the controller.
I have 3 models: TourOpcione (this always save only one record), and TourOpcionOficina and TourOpcionServicio (these can save one or more records at time, and they have a foreign key to TourOpcione).
My problem is that I don't know what's the correct way to arrange the data in my controller before to save that. I want to avoid to use a loop, so I wish to save it with saveAll, or saveAssociated, or if this is not the case, accept any other way.
The explains around internet are confused.
I'm using CakePHP 2.6
Actually this is the array that I had created:
array
(
[TourOpcione] => Array
(
[opcion] => 1
[numeroPersonas] => 4
[total] => 2038
[tour_prospecto_id] => 12
)
[TourOpcionOficina] => Array
(
[0] => Array
(
[oficina_id] => 1623
)
[1] => Array
(
[oficina_id] => 1624
)
)
[TourOpcionServicio] => Array
(
[0] => Array
(
[servicio_id] => 6068
)
[1] => Array
(
[servicio_id] => 6067
)
)
)
Thanks in advice.
UPDATE
At this point, the TourOpcione is the only model who save the data. The other 2 doesn't.
Where can be my problem?
My models are the next (each class in his own model):
class tour_opcione extends AppModel {
public $name = 'TourOpcione';
public $hasMany = array(
'TourOpcionServicio' => array(
'className' => 'TourOpcionServicio',
'foreignKey' => 'tour_opcione_id',
),
'TourOpcionOficina' => array(
'className' => 'TourOpcionOficina',
'foreignKey' => 'tour_opcione_id',
)
);
}
class tour_opcion_servicio extends AppModel {
public $name = 'TourOpcionServicio';
public $belongsTo = array(
'TourOpcione' => array(
'className' => 'TourOpcione',
'foreignKey' => 'tour_opcione_id'
)
);
}
class tour_opcion_oficina extends AppModel {
public $name = 'TourOpcionOficina';
public $belongsTo = array(
'TourOpcione' => array(
'className' => 'TourOpcione',
'foreignKey' => 'tour_opcione_id'
)
);
}
I have two tables
messages
users
users has roles Doctor and user.
messages has doc_id & user_id.
How do I make an association, so that it returns me both data doc_id & user_id from users.
For the Role association on User you will need a join table, thats if i am right in believing a User can have many roles?
You'r Message model should only have a BelongsTo User, then when your doing loop ups you will use deep associations in your find methods to bring back some like...
// Find with deep assosiations
$this->Message->find('all', array());
Array
(
[Message] => Array
(
[id] => 2
[body] => example body...
)
[User] => Array
(
[username] => John
[Role] => Array
(
[0] => 2
[1] => 6
)
)
)
class Message extends AppModel {
public $hasMany=array('MessageDetail');
public $belongsTo = array(
'User'=>array(
'className'=>'User',
'foreignKey'=>'user_id',
),
'Doctor'=>array(
'className'=>'User',
'foreignKey'=>'doc_id'
),
'Petprofiles'=>array(
'className'=>'PetProfiles',
'foreignKey'=>'petprofiles_id'
)
);
}
Someone can tell me why it doesn't work:
$data = $this->Question->find('all', array(
'conditions' => array(
'Question.id' => $qid
),
'contain' => array(
'Slide' => array('Training')
)
));
Relation is as follows:
Training -> has_many -> Slide -> has_many -> Question
So Why result is something like that:
[0] => Array
(
[Question] => Array
(
[id] => 1
)
[Slide] => Array
(
[id] => 1320
)
)
You're calling #find on the Question model. So, you need to define the relationships from Question to Slide and from Slide to Training to include them in your results. Try adding Question belongsTo Slide and Slide belongsTo Training.
So I'm linking two of my models together and it's returning something like:
Array
(
[Submission] => Array
(
[id] => 47
[user_id] => 0
[title] => asdfasdfsa dfasdf asdfa sfa fadf
[source] => http://www.aol.com
[slug] =>
[category] => health
[created] => 2012-06-25 11:30:16
)
[User] => Array
(
[id] => 2
[username] => john
)
[SubmissionsVote] => Array
(
[0] => Array
(
[id] => 247
[user_id] => 2
[submission_id] => 47
[vote_type] => up
)
)
)
Sometimes, however, the number of votes will be from [0] - [n]. This is for my $newestSubmissions. The query I'm doing to give me that is:
public function newestSubmissions() {
$this->unBindModel(
array('hasMany' => array('Comment')));
return $this->find('all', array(
'fields' => array(
'Submission.id',
'Submission.user_id',
'Submission.title',
'Submission.source',
'Submission.slug',
'Submission.category',
'Submission.created',
'User.id',
'User.username'
),
'order' => 'Submission.created DESC'
));
}
What I'd like to do is to retrieve all votes throughout the $newestSubmissions array object as well as all of the votes (query below) on each of the $newestSubmissions to calculate the actual score so I can send a single array object called $newestSubmissions to my view, instead of $newestSubmissions and $submissionScore
public function getVoteType($userId, $submissionId) {
$voteType = $this->find('all', array(
'conditions' => array(
'User.id' => $userId,
'Submission.id' => $submissionId),
'fields' => array(
'SubmissionsVote.vote_type'),
'limit' => '1'
));
return $voteType[0]['SubmissionsVote']['vote_type'];
}
Basically I want to send a single score to my view along with all the other information in the array object instead of having to send an inner indexed array object within my parent array object (which is what I'm currently getting).
How can I do this?
OK... I tried to post this just as a comment, but it was too long. It's not really an answer as such.
Do you know about the containable behaviour? Check it out - it's good for determining what data you get back: http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html
Also, if you were to break your submissions vote table into two tables - upvotes and downvotes, then you could also use Cake's counterCache behaviour to keep track of the number of up/down votes directly in your 'submissions' table (you'd need to add upvote_count and downvote_count columns to your submissions table to do so).
This would make calculating submission score easier - and you wouldn't have to fetch all votes from the database to do it each time. For doco, search for counterCache on this page: http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html It could be worth considering.
return $this->find('all', array(
'fields' => array(
'Submission.id',
'Submission.user_id',
'Submission.title',
'Submission.source',
'Submission.slug',
'Submission.category',
'Submission.created',
'User.id',
'User.username',
'submissionsvotes.vote_type'
),
'joins' => array(
array(
'table' => 'SubmissionsVote',
'alias' => 'submissionsvotes',
'type' => 'LEFT',
'conditions' => array
('User.id = SubmissionsVotes.user_id','Submission.id'=>'SubmissionsVotes.submission_id')
),
)
'group'=>'submissionsvotes.vote_type',
'order' => 'Submission.created DESC'
)
);
Like the example in cakephp manual, http://book.cakephp.org/view/1323/Containable#Containing-deeper-associations-1325, i need to fetch data from a model through a condition on its association model.
I have:
Model Language:
class Language extends AppModel {
var $name = 'Language';
var $actsAs = array('Containable');
var $hasMany = array(
'LanguageTranslation' => array(
'className' => 'LanguageTranslation',
'foreignKey' => 'language_id'
)
);
}
And the association, ModelTranslation
class LanguageTranslation extends AppModel {
var $name = 'LanguageTranslation';
var $belongsTo = array(
'Language'
);
}
when i do:
$language_array = $this->controller->Language->find('all', array(
'contain' => "LanguageTranslation.id = 1"
));
i receive all the languages, not only one (because id in LanguageTranslation is unique). The result need to be one!
SO, with
debug($language_array);
result is:
Array
(
[0] => Array
(
[Language] => Array
(
[id] => 1
[code] => it
[locale] => ita
)
[LanguageTranslation] => Array
(
[0] => Array
(
[id] => 1
[language_id] => 1
[language] => italiano
)
)
)
[1] => Array
(
[Language] => Array
(
[id] => 2
[code] => en
[locale] => eng
)
[LanguageTranslation] => Array
(
)
)
[2] => Array
(
[Language] => Array
(
[id] => 3
[code] => de
[locale] => ger
)
[LanguageTranslation] => Array
(
)
)
)
Why i don't catch only the Language with id = 1?
linkable behavior will do the trick.
You can download it on:
https://github.com/rafaelbandeira3/linkable
MODEL
var $actsAs = array('Linkable');
CONTROLLER
$language_array = $this->Language->find('all', array(
'link' => array('LanguageTranslation'),
'conditions' => array("LanguageTranslation.id = 1")
));
The conditions in containable apply only to the models inside the containment. Because the main query doesn't have any conditions it fetches every row in the table. In general you don't use containable to restrict the main query but to limit the way the query goes through the containment tree (very handy when you need to go through 3-4 levels of recursiveness where the results can get quite bloated if not contained).
In this case in particular if you are trying to get the data of a certain Language/LanguageTranslation pair, you can just pull the data from the LanguageTranslation model.
$this->Language->LanguageTranslation->find(
'first',
array(
'conditions' => array( 'LanguageTranslation.id' => 1 ),
'recursive' => 1
)
);
In regards to Juhana's answer: (which initially calls find on the Language Model), You can supply conditions for the parent model which target child models.
Contain in a separate call.
<?php
$this->Language->contain(array(
'LanguageTranslation'
));
$lang = $this->Language->find('first', array(
'conditions' => array('LanguageTranslation.id' => 1)
));
?>
Contain in one call.
<?php
$lang = $this->Language->find('first', array(
'conditions' => array('LanguageTranslation.id' => 1),
'contain' => array('LanguageTranslation')
));
?>
If you need to apply conditions to the child models just add the conditions array to the child model's index in the contain call. For example:
<?php
$this->Language->contain(array(
'LanguageTranslation' => array(
'conditions' => array('<LanguageTranslationConditionHere>')
)
));
?>
Hope this helps!
-Andrew