My code for controller:
public $paginate = array(
'paramType' => 'querystring',
'limit' => 3,
);
$this->paginate['Comment'] = array(
'conditions' => array('Comment.post_id' => $this->params['id']));
$this->set('comments',$this->paginate('Commant'));
When I do
pr($this->paginate['Comment'] = array(
'conditions' => array('Comment.post_id' => $this->params['id']))); exit;
The output is
Array
(
[conditions] => Array
(
[Response.claim_id] => 4
)
)
But instead of finding comment related to post it shows all post from db
Once I tried
pr($this->paginate['Comment'] = array(
'conditions' => array('Comment.id' => $this->params['id']))); exit;
And the Result was same
Array
(
[conditions] => Array
(
[Response.id] => 4
)
)
Can any one suggest me why pagination is not woking
Use
public $paginate = array(
'paramType' => 'querystring',
'limit' => 3,
);
in controller
$this->set('comments',$this->paginate('Response',
array('Response.claim_id' => $id)));
in your function
And define Paginator component in controller
You don't need to index your $this->paginate array with the model.
As you are using a Controller variable $this->paginate means that it doesn't need to be indexed using the model.
Related
I'm stuck on this... Here's my code:
private function moderators () {
$this->loadModel('Moderator');
$this->Moderator->Behaviors->load('Containable');
$this->Paginator = $this->Components->load('Paginator');
$this->Paginator->settings = array(
'contain' => array(
'ModeratorPersonalData' => array(
'conditions' => array('language' => $this->params['language']),
),
'Thumb',
'Image',
),
'limit' => 1,
'conditions' => array('role' => 'moderator'),
);
$moderators = $this->Paginator->paginate('Moderator');
$this->set('moderators', $moderators);
}
The pagination works, when I type the url for page 2 manually it takes me there, but it isn't showing clickable links.
You did use Paginator helper in your view file, did't you?
http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper
I'm trying to get my associated models in CakePHP 2.3 to save properly, but I'm having issues. I'm storing posts, and I want to know what links are in those posts. For each of those links, I'd like to store anchor text if it is available. My database is set up as in the following diagram.
(source: derekperkins.com)
Anchor Model
class Anchor extends AppModel {
public $hasMany = array(
'PostsUrl' => array(
'className' => 'PostsUrl',
'foreignKey' => 'anchor_id',
'dependent' => false
)
);
public function save($data = NULL, $validate = true, $fieldList = array()) {
$id = Anchor::find('first', array(
'fields' => array('id'),
'recursive' => -1,
'conditions' => array('anchor' => $data['anchor'])
));
if( $id )
$data['id'] = $id['Anchor']['id'];
return parent::save($data, $validate, $fieldList);
}
}
URL Model
class Url extends AppModel {
public $hasMany = array(
'PostsUrl' => array(
'className' => 'PostsUrl',
'foreignKey' => 'url_id',
'dependent' => false
)
);
public function save($data = NULL, $validate = true, $fieldList = array()) {
$id = Url::find('first', array(
'fields' => array('id'),
'recursive' => -1,
'conditions' => array('url' => $data['url'])
));
if( $id )
$data['id'] = $id['Url']['id'];
return parent::save($data, $validate, $fieldList);
}
}
PostsUrl Model
class PostsUrl extends AppModel {
public $belongsTo = array(
'Post' => array(
'className' => 'Post',
'foreignKey' => 'post_id'
),
'Url' => array(
'className' => 'Url',
'foreignKey' => 'url_id'
'Anchor' => array(
'className' => 'Url',
'foreignKey' => 'anchor_id'
)*/
);
}
Post Model
class Post extends AppModel {
public $hasMany = array(
'PostsUrl' => array(
'className' => 'PostsUrl',
'foreignKey' => 'post_id',
'dependent' => false
)
);
public function save($data = NULL, $validate = true, $fieldList = array()) {
$id = Post::find('first', array(
'fields' => array('id'),
'recursive' => -1,
'conditions' => array('external_post_id' => $data['external_post_id'])
));
if( $id )
$data['id'] = $id['Post']['id'];
return parent::save($data, $validate, $fieldList);
}
}
Submitting Data
I've created a form to test my model. This is the code I'm using to save the array created by the form. I am getting a message saying that things saved successfully, but only the post saves. Nothing is entered into the other three tables. I'm also using DebugKit and no SQL calls reference any of that data.
$this->Post->saveAssociated($this->request->data, array('deep' => true))
Array
(
[Post] => Array
(
[external_post_id] => 12345
[sentiment_score] => 3.3
)
[URL] => Array
(
[url] => http://test.com
)
[Anchor] => Array
(
[anchor] => Test Anchor
)
)
I've also tried formatting my arrays to have the URL and Anchor underneath PostsUrl as a subarray, but that didn't work either.
My Model::save functions are there to keep me from duplicating data, and they work properly in other models I have used in the past (though I'm open to suggestions on a better way to do this, as this uses a database call for each check). I've also tried commenting them out, and it doesn't affect my code. How should I structure this to save properly?
First of all about your Model::save functions, for example:
public function save($data = NULL, $validate = true, $fieldList = array()) {
$id = Post::find('first', array(
'fields' => array('id'),
'recursive' => -1,
'conditions' => array('external_post_id' => $data['external_post_id'])
));
if( $id )
$data['id'] = $id['Post']['id'];
pr($data);
return parent::save($data, $validate, $fieldList);
}
it prints $data in this way :
Array
(
[id] => 3 //for example 3
[Post] => Array
(
[external_post_id] => 12345
[sentiment_score] => 3.3
)
[URL] => Array
(
[url] => http://test.com
)
[Anchor] => Array
(
[anchor] => Test Anchor
)
)
this $data is incorrect, the correct data is:
Array
(
[Post] => Array
(
[id] => 3 //for example 3
[external_post_id] => 12345
[sentiment_score] => 3.3
)
[URL] => Array
(
[url] => http://test.com
)
[Anchor] => Array
(
[anchor] => Test Anchor
)
)
You must change your Model::save function this way:
public function save($data = NULL, $validate = true, $fieldList = array()) {
$id = Post::find('first', array(
'fields' => array('id'),
'recursive' => -1,
'conditions' => array('external_post_id' => $data['external_post_id'])
));
if( $id )
$data[$this->name]['id'] = $id['Post']['id'];
return parent::save($data, $validate, $fieldList);
}
second , you can't save this data with single save, you should save your data this way:
$postData = array
(
'Post' => array
(
'external_post_id' => 12345
'sentiment_score' => 3.3
)
);
$this->Post->save($data);
$postUrlId = $this->PostsUrl->find('first', array(
'conditions' => array(
'post_id' => $this->Post->id
),
'fields' => array('id')
));
$urlAnchorData = array(
'URL' => array
(
'url' => 'http://test.com'
),
'Anchor' => array
(
'anchor' => 'Test Anchor'
),
'PostsUrl' => array(
'id' => $postUrlId['PostsUrl']['id']
)
);
$this->PostsUrl->saveAll('$urlAnchorData');
I have a model, Comment, that belongsto Module, Photo, Review, Article, and User. In turn, each of those models havemany Comment.
User belongsto Avatar and Avatar hasmany User
In the code below I successfully retrieve the latest 5 comments regardless of what model they come from (Photo, Article, Review) and I display some information about the User (username, online status).
$recentComments = $this->find('all',
array(
'limit' => '5',
'order' => array('Comment.id' => 'desc'),
'conditions' => array(
'Comment.page_id' => $page_id
),
'contain' => array(
'Photo' => array(
'fields' => array('Photo.id'),
'conditions' => array(
'Comment.module_id' => 8
),
),
'Review' => array(
'fields' => array('Review.title', 'Review.id'),
'conditions' => array(
'Comment.module_id' => 2
),
),
'Article' => array(
'fields' => array('Article.title', 'Article.id'),
'conditions' => array(
'Comment.module_id' => 3
),
),
'Module' => array(
'fields' => array('Module.model', 'Module.post_title'),
),
'User' => array(
'fields' => array('User.username', 'User.online'),
),
)
));
Unfortunately there are some issues with this. As you can see above I don't contain User->Avatar and all expected data is retrieved successfully.
$recentComments = Array
(
[0] => Array
(
[Comment] => Array
(
[id] => 179
[page_id] => 2
[module_id] => 3
[page] =>
[user_id] => 29
[post_id] => 9
[title] => test comment for article 9
[content] => here is the content for the comment
[created] => 2013-04-24 00:00:00
[redeemed] => 0
[status] => 0
)
[User] => Array
(
[username] => bowlerae
[online] => 0
)
[Module] => Array
(
[model] => Article
[post_title] => title
)
[Article] => Array
(
[title] => Test title for article 9
[id] => 9
[likes] => 0
[dislikes] => 0
[comments] => 2
)
[Photo] => Array
(
[id] =>
)
[Review] => Array
(
[title] =>
[id] =>
)
)
However, if I DO contain User->Avatar like this...
'User' => array(
'fields' => array('User.username', 'User.online'),
'Avatar' => array(
'fields' => array('Avatar.file')
)
),
Then the recursion basically becomes unlimited, ALL models related to Comment, User, Module, Article, Photo and Review are also retrieved which is A LOT.
Can anyone explain why this is happening? I will be happy to submit more code from some of the models if needed but I don't see any issues there.
Take a look at another example that works successfully...Below I am retrieving the 5 most recent articles. All information including the user Avatar is successfully retreived.
$this->set('articles_sidebar', ClassRegistry::init('Article')->find('all',
array(
'limit' => '5',
'order' => array('Article.id' => 'desc'),
'conditions' => array('
Article.page_id' => $page_id
),
'contain' => array(
'User' => array(
'fields' => array('User.username', 'User.online'),
'Avatar' => array(
'fields' => array('Avatar.file')
)
),
)
)));
Please note these two finds are performed in the AppController in the beforeRender given that $page_id > 0. $page_id is set in whatever the current controller is. I know people would probably ask about it but that's not what the issue is as I mentioned that example 2 retrieving the recent articles currently works.
EDIT: I discovered that it has something to do with my afterfind callback in the Article model. Is there a way I can tweak the queries in the afterfind and/or the $recentComments query so that they still work without breaking my contain? I don't need the likes, dislikes or comments virtual fields in my $recentComments query which is why they are not listed as one of the contained fields.
function afterFind($results, $primary = false) {
parent::afterFind($results, $primary);
foreach($results as $key => $val){
if (isset($val['Article']['id'])){
$results[$key]['Article']['likes'] = $this->Like->find('count', array('conditions' => array('Like.post_id' => $results[$key]['Article']['id'], 'Like.module_id' => 3, 'Like.status' => 0)));
$results[$key]['Article']['dislikes'] = $this->Like->find('count', array('conditions' => array('Like.post_id' => $results[$key]['Article']['id'], 'Like.module_id' => 3, 'Like.status' => 1)));
$results[$key]['Article']['comments'] = $this->Comment->find('count', array('conditions' => array('Comment.post_id' => $results[$key]['Article']['id'], 'Comment.module_id' => 3, 'Comment.status < 2')));
}
} // end for each
return $results;
} // end afterfind
My guess is your problem is this:
When using ‘fields’ and ‘contain’ options - be careful to include all
foreign keys that your query directly or indirectly requires.
Make sure you're including whatever field(s) are used to join User and Avatar models.
(read here)
For now I changed (in the Article model afterFind)
if (isset($val['Article']['id']))
to
if (isset($val['Article']['id']) && $val == "article")
Seems to be working in current controller but need further testing. Is there a better solution?
how to convert this statment to pagination with condition rules
$comment = $this->Article->Comment->find('all',
array('conditions' =>
array('Comment.article_id' => $id, 'Comment.status' => 1)));
i do this but i get parce error
var $paginate = array(
'limit' => 5,
'page' => 1,
'order' => array('Comment.created'=>'desc'),
'conditions' => array('Comment.article_id' => $id, 'Comment.status' => 1)
);
$this->Article->Comment->recursive = 1;
this->set('comment', $this->paginate());
Follow the step-by-step instructions here.
I had a recent question here regarding Finding records via conditions in habtm. Now, I could search for posts within a category I searched for.
Now, my questions is how to I retrieve the categories of each post. Sometimes a post has one or more categories in this query:
$this->set('posts', $this->Category->find(
'first',
array(
'conditions' => array(
'Category.uri' => $uri
),
'contain' => array('Post')
)
));
I'd imagine something like this:
$this->set('posts', $this->Category->find(
'first',
array(
'conditions' => array(
'Category.uri' => $uri
),
'contain' => array('Post' => array(
'contain' => 'Category'
))
)
));
Here's what my models look like.
// Category Model
class Category extends AppModel {
var $name = 'Category';
var $hasAndBelongsToMany = array(
'Post' => array(
'className' => 'Post'
)
);
var $actsAs = array('Containable');
}
// Post Model
class Post extends AppModel {
var $name = 'Post';
var $hasAndBelongsToMany = array(
'Category' => array(
'className' => 'Category'
)
);
var $actsAs = array('Containable');
var $virtualFields = array(
'date_posted' => 'DATE_SUB(Post.created, INTERVAL 7 DAY)'
);
}
A sample data would be like this:
categories
id name
1 holidays
2 destinations
posts
id title
1 I am a post
2 I am another post
categories_posts
post_id category_id
1 1
2 2
2 1
I am retrieving posts from holidays.
Array
(
[Category] => Array
(
[id] => 3
[name] => holidays
[uri] => holidays
[created] => 2010-11-25 20:43:03
[modified] => 2010-11-25 20:43:03
)
[Post] => Array
(
[0] => Array
(
[id] => 1
[title] => I am a post
),
[1] => Array
(
[id] => 2
[title] => I am a another post
)
)
)
The problem is that 1 of the posts are in two categories. I'd like to have that information also available.
Close. Try this:
$this->set('posts', $this->Category->find(
'first',
array(
'conditions' => array(
'Category.uri' => $uri
),
'contain' => array('Post' => array('Category'))
)
));
Tangent
Writing Controller code like this has problems:
It will bloat your controller methods and make your controllers less readable.
It prevents you from re-using business logic among your different controllers.
You can satisfactorily correct these problems by moving your find call to a model method:
// Categories Controller
$this->set('posts', $this->Category->get_posts_with_cats($uri));
// Category Model
function get_posts_with_cats($uri) {
$this->find('first', array(
'conditions' => array('Category.uri' => $uri),
'contain' => array('Post' => array('Category'))
));
}
This makes your code More Awesome:
The controller is nice and Skinny, and super readable.
Now you can call the same method from any associated model. So, for instance, in your PostsController you could call: $this->Post->Category->get_posts_with_cats($uri);. Your code is now DRY.
Tangent #2
After you learn how to nest containable models like I've shown you, you may be tempted in the future to do something like this:
$this->find('first', array(
'conditions' => array('Category.uri' => $uri),
'contain' => array(
'Post' => array(
'Category' => array(
'Tag' => array('Author')
)
)
)
));
The good news? You will get the desired model data from the above method (as long as you've defined all of these relationships).
The terrible news? CakePHP doesn't know how to optimize the queries for deep associations like this. So, as your database grows, you'll end up with dozens (if not hundreds (if not thousands)) of SELECT queries to the database, effectively bringing your application to a crawl (if not a halt.)
The good folk that design CakePHP are working on a better containable behavior for CakePHP 2.0, but you'll have to wait until then to get this level of precision.
I'm leaving my other answer as-is because it contains a lot of good info for those who may stumble upon it. As far as solving your problem more elegantly? You'll need to flip the find on it's head and search from the Post model with an ad-hoc join, like this:
// From the CategoriesController
$this->Category->Post->find('all', array(
'joins' => array(
array(
'table' => 'categories_posts',
'alias' => 'CategoryFilter',
'type' => 'inner',
'conditions' => array(
'CategoryFilter.post_id = Post.id'
)
),
array(
'table' => 'categories',
'alias' => 'CategoryUriFilter',
'type' => 'inner',
'conditions' => array(
'CategoryUriFilter.id = CategoryFilter.category_id'
)
)
),
'conditions' => array(
'CategoryUriFilter.uri' => $uri
),
'contain' => array('Category')
));
The array returned will be formatted a bit differently, but it should contain all of the data you were looking for.
I actually did this to get what I want:
function getPostsFromCategory($uri) {
return $this->populatePosts($this->find(
'first',
array(
'conditions' => array(
'Category.uri' => $uri
),
'contain' => array('Post' => array('Category'))
)
));
}
function populatePosts($categoryPosts) {
$posts = $categoryPosts['Post'];
$count = count($categoryPosts['Post']);
for ($i = 0; $i < $count; $i++) {
$categories = $this->Post->find('first', array('conditions' => array('Post.id' => $categoryPosts['Post'][$i]['id']),
'contains' => array('Category')));
// unset($categories['Post']);
foreach ($categories['Category'] as $cat) {
$categoryPosts['Post'][$i]['Categories'][] = $cat;
}
unset($categories);
}
return $categoryPosts;
}
I really don't know if this can be done in a more CakePHP-y way though:
I returns something like this:
Array
(
[Category] => Array
(
[id] => 3
[name] => festivals
[uri] => festivals
[created] => 2010-11-25 20:43:03
[modified] => 2010-11-25 20:43:03
)
[Post] => Array
(
[0] => Array
(
[id] => 28
[title] => A Post
[Categories] => Array
(
[0] => Array
(
[id] => 3
[name] => festivals
[uri] => festivals
[created] => 2010-11-25 20:43:03
[modified] => 2010-11-25 20:43:03
)
[1] => Array
(
[id] => 4
[name] => destinations
[uri] => destinations
[created] => 2010-11-28 13:04:52
[modified] => 2010-11-28 13:04:52
)
)
)
)