This is my choices table
id | question_id | content
1 1 bee
2 1 fly
3 1 dog
4 2 cat
5 2 bat
6 2 wasp
and this is my questions table
id | content
1 question1
2 item2
This is what i did.With this I could display the question with its choices..what I want to do now, is to paginate it,more like 1 question per page.How to do this?Your help will be greatly appreciated.Thanks
$options['fields'] = array('questions.id','questions.content');
$options['joins'] = array(
array(
'table' => 'generated_exam_items',
'alias' => 'GenExamItems',
'type' => 'inner',
'conditions' => array(
'GenExamItems.generated_examination_id' => 25
)
),
array(
'table' => 'questions',
'alias' => 'Questions',
'type' => 'inner',
'conditions' => array(
'Questions.id = GenExamItems.questions_id'
)
),
);
$options['conditions']=array('GenExamItems.generated_examination_id' => 25,'Question.id=GenExamItems.questions_id');
$question_detail = $this->Question->find('all',$options);
$this->set('questions',$question_detail);
Someone please help me.!!I dont how to do it and im stuck in this for almos a week..
you can get using relation ship with each other model
as here i am just adding skeleton code you need to verify it to you actual code with real classname
In Question model write this relation
class Question extends AppModel {
public $hasMany = array(
'Choice' => array(
'className' => 'Choice',
)
);
And in Choice model you can define like
class Choice extends AppModel {
public $belongsTo = 'Question';
}
And in controller if you want to fetch for speacific Question with its choices you can write query like below
$question_detail = $this->Question->find('first', array(
'conditions' => array('Question.id' => $question_id)
));
let me know if i can help you more
Let's assume you have the correct associations on both models.
I'll show the code for just one question in the view.
If you use the automagic way cake provides (read the "Creating form elements" part)
In your controller, you have to get the choices
$this->set('choices', $this->Question->Choice->find('list',
array('conditions'=>
array('question_id'=>$your_question_id))));
And in your view, you have to do
echo $this->Form->input('choice_id', array('type'=>'radio'));
That should automatically display the choices as radios (not really sure what type of input you wanted)
If by some reason the automagic way doesn't work (be sure to check your model's associations if that happens), then you can pass the array of choices (get them as showed before on the controller) and pass it as a parameter
echo $this->Form->input('choice_id', array('type'=>'radio',
'options'=>$choices));
Note: If the radio labels are showing the ids instead of the content, you'll have to change the displayField of choices.
This what i did.It returns the question and corresponding choices.I hope I will be able to someone with this.Thank you.
public function take($id=null) {
$this->request->params['named']['page'] = (isset($this->request->params['page'])) ? $this->request->params['page'] : 1;
$options['fields'] = array('questions.id','questions.content');
$options['joins'] = array(
array(
'table' => 'generated_exam_items',
'alias' => 'GenExamItems',
'type' => 'inner',
'conditions' => array(
'GenExamItems.generated_examination_id' => 40
)
),
array(
'table' => 'questions',
'alias' => 'Questions',
'type' => 'inner',
'conditions' => array(
'Questions.id = GenExamItems.questions_id'
)
),
);
//$options['contains']='Choice';
$options['conditions']=array('Question.id = GenExamItems.questions_id');
$options['order']=array('rand(Question.id)');
$options['limit']=1;
$this->paginate=$options;
$data = $this->paginate('Question');
$this->set('questions',$data);
}
Related
I'd like to understand joins in CakePHP (v 2.4.5) a bit better by solving the following example:
Post hasMany Comment
Post.id == Comment.post_id
Comment.published can be 1 or 0
I need to find all Posts that have at least one published Comment
I want to write the query from the Post model. In order not to break pagination and so I can add order/conditions based on Post
I do not want to filter out results afterwards in PHP (in order not to break pagination)
You might suggest to approach this issue from the Comment model like here:
https://stackoverflow.com/a/3890461/155638
But this is about understanding joins better, so I'd like to set a requirement to write the query from the Post model.
I have roughly the following idea, hoping that the RIGHT join would exclude all non conforming Posts:
$this->Post->find('all', array(
'joins' => array(
array(
'table' => 'comments',
'alias' => 'CommentsJoined',
'type' => 'RIGHT',
'conditions' => array(
'Post.id = CommentsJoined.post_id',
'CommentsJoined.published = true'
)
)
),
'contain' => array(
'Comment' => array(
'conditions' => array(
'Comment.published' => 1
)
)
)
);
But it did not work for me yet.
Currently my query returns 19 times the same Post, instead of 19 unique Posts.
How to go from here? Is the approach the right one?
Kind regards!
Bart
It seems I was on the right track. The final step was to remove the duplicate Posts.
This is done by adding 'group' => 'Post.id' as an attribute to the query.
Like this:
$this->Post->find('all', array(
'joins' => array(
array(
'table' => 'comments',
'alias' => 'CommentsJoined',
'type' => 'RIGHT',
'conditions' => array(
'Post.id = CommentsJoined.post_id',
'CommentsJoined.published = true'
)
)
),
'group' => 'Post.id',
'contain' => array(
'Comment' => array(
'conditions' => array(
'Comment.published' => 1
)
)
)
);
I'm facing a problem with cakephp associations in Models.
I have to Select records which have atleast one hasMany reation row
Model
class Category extends AppModel
{
public $hasMany = array(
'Product' => array(
'className' => 'Product',
'foreignKey' => 'CategoryId',
)
);
}
Query
$categories = $this->Category->find('all');
I only needed the categories which have atleast one product entry
Categories Like : Shirts, Footwear, Glasses etc
Products like :
Small, medium, large (Shirts)
With Frame, UV protected (Glass)
So, i jus want to get Shirts and Glasses Categories only because for the above example there is no products for Footwear
Use counterCache or joins
Please refer to CakePHP - Find and count how many associated records exist
The most simple way with the best performance would be using a properly indexed counter cache field as shown in the linked answer.
Sice the linked answer is not an exact duplicate with respect to the join, here's some additional info, instead of using HAVING COUNT with the join you'd use a IS NOT NULL condition. Here's an (untested) example:
$this->Category->find('all', array(
'joins' => array(
array(
'table' => 'products',
'alias' => 'Product',
'type' => 'LEFT',
'conditions' => array('Category.id = Product.CategoryId')
)
),
'conditions' => array(
'Product.CategoryId IS NOT NULL'
)
'group' => 'Category.id'
));
Depending on the used DBMS and version you might get better performance using an inner join:
$this->Category->find('all', array(
'joins' => array(
array(
'table' => 'products',
'alias' => 'Product',
'type' => 'INNER',
'conditions' => array('Category.id = Product.CategoryId')
)
),
'group' => 'Category.id'
));
I have model relations like this:
Project hasMany SubProject hasMany Item
I want to set up a containable array so that I can find all of the Items which belong to a particular Project, and paginate the results. So, in my ItemsController I have:
public $paginate = array(
'Item' => array(
'limit' => 10,
'order' => array('
'Item.create_time' => 'desc'
),
'contain' => array(
'SubProject' => array(
'Project'
)
)
)
);
Somewhere, obviously, I need to place a condition like "SubProject.project_id = $pid", but nothing I've tried yields the correct results. The best I can manage is results that look like this:
Array
(
[0] => Array
(
[Item] => Array
(
[id] => 13
[file_name] => foo.tar.gz
.... other keys ...
[create_time] => 2013-01-23 14:59:49
[subProject_id] => 4
)
[SubProject] => Array
(
[id] => 4
[name] => foo
[project_id] => 2
..... other keys ....
[Project] => Array
(
)
)
)
[1] => Array
.....
Edit: It is quite correctly omitting the Project record that doesn't match; I want to skip any Item records with out a matching Project record.
It has crossed my mind to manually specify my joins, but I feel like that shouldn't be necessary.
It seems like this should be obvious, but alas, the solution escapes me.
I did eventually solve this problem, so I thought I'd explain what I did in the hope it might help someone else.
After reading this blog post by Mark Story (which is from the days of 1.2 but still relevant) I decided that the thing to do was create a custom find type in my Item model that binds the Project model directly. This gives a first-level association that Containable can filter correctly.
So, in the Items model, I have something like the following (see the documentation on custom find types).
public $findMethods = array('byProject' => true);
public function _findByProject($state, $query, $results=array()) {
if ($state == 'before') {
$this->bindModel(array(
'hasOne' => array(
'Project' => array(
'foreignKey' => false,
'conditions' => array('Project.id = SubProject.project_id')
)
)
));
return $query;
}
return $results;
}
Note that setting foreignKey to false is necessary to prevent CakePHP from trying to automatically use a non-existent database key. In the ItemsController, the pagination options now look like this:
public $paginate = array(
'Item' => array(
'findType' => 'byProject',
'limit' => 10,
'order' => array(
'Item.create_time' => 'desc'
),
'contain' => array(
'SubProject',
'Project'
),
'conditions' => array('Project.id' = $pid)
),
);
...where $pid is the id of the project to display. Some minor tweaks in the view code to accomodate the slightly different results array structure, and I was all set.
EDIT ===============
public $paginate = array(
'limit' => 10,
'order' => 'Item.create_time DESC', //'order' => array(''Item.create_time' => 'desc'),
'contain' => array(
'SubProject' => array(
'Project' => array(
'conditions' => array(
'id' => $project_id // Passed parameter
)
)
)
)
);
=================================================
Have you tried using conditions as in the following? Also, I did not write the 'order' section of the code the way you have it.
public $paginate = array(
'Item' => array(
'limit' => 10,
'order' => 'Item.create_time DESC', //'order' => array(''Item.create_time' => 'desc'),
'contain' => array(
'SubProject' => array(
'Project' => array(
'conditions' => array(
'id' => $project_id // Passed parameter
)
)
)
)
)
);
I know the solutions is simple and might have something to do with the Containable behavior but I can't get it working. Without all the tries
This is the case. I'd like to display the Event details of an Event (eg. a conference). Each event takes place in a EventVenue and each EventVenue is located in a Country.
So in the Country Model the following is present:
public $hasMany = array(
'EventVenue' => array(
'className' => 'EventVenue',
'foreignKey' => 'country_id'
))
In the EventVenue model a BelongsTo association is made
public $belongsTo = array(
'Country' => array(
'className' => 'Country',
'foreignKey' => 'country_id'
))
And in the Event model a hasOne association is made
public $hasOne = array(
'EventVenue' => array(
'className' => 'EventVenue',
'foreignKey' => 'event_id',
))
What I want is to display the country name on the page that is renderd in the EventsController. I do get all the Event and EventVenue data but the associated Country for the venue is not retrieved.
The data is retrieved in the following way
$item = $this->Event->findBySlug($slug);
How can I also get the country name (eg. Netherlands) retrieved from the database? I tried something like this but that did not work:
$item = $this->Event->findBySlug($slug,array(
'contain' => array(
'Event' => array(
'EventVenue' => array(
'Country'
)
)
)
)
Try this:
$item = $this->Event->findBySlug($slug,array(
'contain' => array(
'EventVenue' => array(
'Country'
)
)
);
Update
Turns out findBy does not support Containable. You could use this to get the desired result:
$item = $this->Event->find('first',array(
'conditions' => array(
'Event.slug' => $slug
),
'contain' => array(
'EventVenue' => array(
'Country'
)
)
);
Oh and make sure you have this in the model: public $actsAs = array('Containable');
try this method:
$this->Event->recusive = 2;
$item = $this->Event->findBySlug($slug);
You need to set the recursive to 2 before you make the find, something like this
$this->Event->recursive = 2;
with this, you'll get the Event, the EventVenue and the Country on one shot
Hope it helps
i have multiple habtm like these :
// User model
var $hasMany = array('Post');
// Post model
var $hasAndBelongsToMany = array('Category', 'Tag');
// Category model
var $hasAndBelongsToMany = array('Post');
// Tag model
var $hasAndBelongsToMany = array('Post');
I tried to fetch all post along with its user and tags (within a certain category), somehow if i fetch tags, the result was wrong.
$this->paginate = array
(
'Post' => array
(
'limit' => 2,
'fields' => array(
'Post.title', 'Post.content', 'Post.slug', 'Post.created',
'Tag.name',
'User.username', 'User.created', 'User.post_count', 'User.avatar_file_name'),
'joins' => array
(
array(
'table' => 'categories_posts',
'alias' => 'CategoriesPost',
'type' => 'inner',
'conditions'=> array('CategoriesPost.post_id = Post.id')
),
// FETCH USER
array(
'table' => 'users',
'alias' => 'User',
'type' => 'inner',
'conditions'=> array('Post.user_id = User.id')
),
// FETCH TAGS
array(
'table' => 'posts_tags',
'alias' => 'PostsTag',
'type' => 'inner',
'conditions'=> array('PostsTag.post_id = Post.id')
),
array(
'table' => 'tags',
'alias' => 'Tag',
'type' => 'inner',
'conditions'=> array('Tag.id = PostsTag.tag_id')
),
array(
'table' => 'categories',
'alias' => 'Category',
'type' => 'inner',
'conditions'=> array('Category.id = CategoriesPost.category_id', 'Category.slug' => $slug)
)
)
)
);
$posts = $this->paginate();
could anyone gimme a solution since i'm a newbie?
many thanks...
I've run into a similar problem, when trying to paginate a resultset using an associated model. I ended up having to manually bind my models together, run the query, and then unbind them in order to get Cake to contain the right data together. ( http://book.cakephp.org/view/86/Creating-and-Destroying-Associations-on-the-Fly )
You could also try the containable behaviour which will allow you to specify which models you want to include in your result set. Containable is core in 1.2+ ( http://book.cakephp.org/view/474/Containable ), otherwise you'll need to grab it yourself.
I'm not too sure on why you have such a gargantuan query there though. I would be more inclined to do something similar to the following.
$this->Model->recursive = 2;
$this->Model->paginate();
And let Cake get all the related data for me through my associations. Then I would adjust the return using a conditions array ( http://api.cakephp.org/class/controller#method-Controllerpaginate ) to specify the category.
Sorry it's not a defacto solution, but I'm a CakePHP amateur myself! You might find it easier to view the queries, results etc, using DebugKit, http://github.com/cakephp/debug_kit