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'
)
);
Related
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.
i want to get multiple rows value base on multiple id value and i m getting id in array.
id output array
pr($currentSessionData['Category']);
Array
(
[Category] => Array
(
[0] => 1
[1] => 24
[2] => 25
)
)
Below code for getting rows value but not working
$this->YourModelName->find('all', array(
'conditions' => array(
"YourModelName.id" => array($currentSessionData['Category'])
)
));
dont use array() .eg array($currentSessionData['Category'])
Try this.
$cat_ids=array(0=>10,1=>51,2=>51,3=>6561,4=>1,5=>561);
$this->YourModelName->find('all', array(
'conditions' => array(
"YourModelName.id" => cat_ids /*dont use array() */
)
));
i hope its helpful for you :)
Is there a way to format the $this->find('all') array into the $this->find('list') in the view? The reason I ask is so that I can pass that new array into the form helper options and then use the $this->find('all') array to build some additional things I need?
Array (
[0] => Array ( [School] => Array ( [id] => 0 [name] => Nature [address] => 112 Main [max_students] => 25 [application_level] => 5 ) )
[1] => Array ( [School] => Array ( [id] => 1 [name] => Math [address] => 112 Smith [max_students] => 25 [application_level] => 0 ) )
[2] => Array ( [School] => Array ( [id] => 2 [name] => Art [address] => 112 Lane [max_students] => 25 [application_level] => 0 ) )
)
So this is the array I get when I do a find('all'). I want to build the array so it looks like:
Array (
[0] => 'Nature'
[1] => 'Math'
[2] => 'Art'
)
This is usually done by the $this->find('list') function. Though the reason I want the whole array is because I need to add the application_level into $this->Form->input() function. This is because I need to add the option of class with the application level attached so I show only the shows with the application level based on the previous selection.
EDIT: Can't I just do $this->find('list', [insert parameters here?]);? I just don't understand how you set up the additional parameters?
If your query isn't overly complicated and isn't going to return a excessive number of results, just run it twice (once for find all and once for find list).
Find all, list, first, whatever are all the same in terms of the paramaters you pass in. E.g.:
$this->Model->find('all', array(
'conditions' => array(
'field' => 500,
'status' => 'Confirmed'
),
'order' => 'id ASC'
));
... you literally replace all with list. In your case, probably easiest to do it twice, once for each. Like this:
$parameters = array(
'conditions' => array(
'field' => 500,
'status' => 'Confirmed'
),
'order' => 'id ASC'
);
$alldata = $this->Model->find('all', $parameters);
$listdata = $this->Model->find('list', $parameters);
Otherwise, you can loop through it and populate your own list:
$list = array();
foreach($findall as $row) {
$id = $row['id'];
$name = $row['name'];
$list[$id] = $name;
}
$this->set('listdata', $list);
Short answer to your question is that there's no quick, easy way to select all and list from the same query, but you can re use your parameters (conditions, order etc) by passing them in as predefined arrays, or populate your own list.
An alternative answer to creating the results formatted like find('list') from results from find('all') using CakePHP's hash utility:
//where $data is the result of find all
App::uses('Hash', 'Utility');
$ids = Hash::format($data, array('{n}.Model.id'), '{0}'); //ids in an array.
$names = Hash::format($data, array('{n}.Model.name'), '{0}'); //names in an array
$dataAsList = array_combine($ids, $names);
To improve on kai's answer. The Hash class has a method called combine that can do what you're trying to do in only one line
$list = Hash::combine($data,'{n}.Model.id','{n}.Model.name');
the $list will be a flat array like data from find('list');
I'm using Set class of Cakephp to format the find returned array but cannot seem to find a way to get the counter starting at zero and auto-increment for array keys so it is like
[0] => 3
[1] => 6
[2] => 12
I'm currently using below query to get the data from my HasAndBelongsToMany table.
$interest_ids = Set::combine($this->User->Interestsub->find('threaded', array
(
'conditions' => array
(
'Interestsub.name' => $interests
),
//'fields' => array('Interestsub.id'),
'recursive' => -1
)
),
'{n}.Interestsub.id',
'{n}.Interestsub.id'
);
The reason why I need this is that I'm currently trying to get the returned array as part of bigger parent array preparing to be saved for SaveAll function. To be formatted properly, I need below nested array coming out:
[0] => Array
(
[interestssub_id] => 12
[user_id] => 2
)
[1] => Array
(
[interestssub_id] => 22
[user_id] => 2
)
[2] => Array
(
[interestssub_id] => 32
[user_id] => 2
)
Is there a way we can use Combine class to format the returned array like above?
There's no real reason to use the Set class in this case. Just use good old fashioned php:
$threaded = $this->User->Interestsub->find('threaded', array(
'conditions' => array(
'Interestsub.name' => $interests
),
'recursive' => -1
));
$interest_ids = array();
foreach ($threaded as $thread) {
$interest_ids[] = array(
'interestssub_id' => $thread['Interestsub.id'],
'interestssub_id' => $thread['Interestsub.user_id']
);
}
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