Reputation: 855
I have a photoblog built on CakePHP 2.0 with a data structure that looks like:
POSTS <-habtm-> TAGS <-habtm-> IMAGES
I am building an AJAX-based feature to find all blog posts and images that match a given tag. Page 1 of Posts and page 1 of Images are loaded into adjacent panels when a tag is first selected. After that, they can be paged through independently. For the most part this is working fine, except when I am fetching the initial pages of data.
I am using paginate()
twice in the first action -- once to get my Posts and a second time to get the Images. The problem is that the conditions I assign to paginate()
for the second model in the sequence are completely ignored. Individually they both work fine, and switching their order has confirmed it's a sequence-dependent problem for me, rather than restricted to one of the models or the other.
I've searched to see if anyone else has encountered similar problems in the past, but this is either an unusual design choice on my part or I'm not finding the right search query.
My basic $paginate
array is declared as follows in my TagsController.php
:
public $paginate = array(
"PostsTag" => array(
"limit" => 4,
"order" => "Post.id DESC",
"contain" => array(
"Tag",
"Post" => array("fields" => array(
"id", "title", "created"))
),
"group" => array("Post.id")
),
"ImagesTag" => array(
"limit" => 4,
"order" => "Image.id DESC",
"contain" => array(
"Tag",
"Image" => array("fields" => array(
"id", "title", "url", "created", "gallery"))
),
"group" => array("Image.id")
)
);
From my main search action I call two private functions:
$posts = $this->post_pagination($tagIds);
$images = $this->image_pagination($tagIds);
which add the limiting conditions to $paginate
and look like this:
private function post_pagination($tags, $page = 1) {
$this->paginate['PostsTag']['conditions'] = array(
"status" => 1,
"OR" => array("tag_id" => $tags)
);
$this->paginate['PostsTag']['page'] = $page;
return $this->paginate("PostsTag");
}
private function image_pagination($tags, $page = 1) {
$this->paginate['ImagesTag']['conditions'] = array(
"gallery" => 1,
"OR" => array("tag_id" => $tags)
);
$this->paginate['ImagesTag']['page'] = $page;
return $this->paginate("ImagesTag");
}
Cake is respecting limit
, order
, contain
, etc. without issue, but drops the ball on conditions
specifically for whichever model I try to paginate over second. It feeds me back the first 4 results ordered properly, but completely unfiltered. I do not think my somewhat complicated conditions
are at fault either -- as long as I don't break syntax, I can type completely random strings into conditions
for the second paginate()
and get back identical results.
Any help or suggestions are greatly appreciated.
[edit] Here is an SQL dump of the second paginate()
query:
SELECT `PostsTag`.`id`, `PostsTag`.`post_id`, `PostsTag`.`tag_id`,
`Tag`.`id`, `Tag`.`name`, `Post`.`id`, `Post`.`title`, `Post`.`created`
FROM `posts_tags` AS `PostsTag`
LEFT JOIN `tags` AS `Tag` ON (`PostsTag`.`tag_id` = `Tag`.`id`)
LEFT JOIN `posts` AS `Post` ON (`PostsTag`.`post_id` = `Post`.`id`)
WHERE 1 = 1
GROUP BY `Post`.`id`
ORDER BY `Post`.`id`
DESC LIMIT 4
As you can see, Cake is generating a WHERE 1 = 1
in place of my conditions.
Upvotes: 0
Views: 1091
Reputation: 91
DEAR PEOPLE FROM THE FUTURE: Here's what we've figured out so far...
OP is correct that YourController::$paginate
is only fed into the PaginatorComponent
once. If you need to call YourController::paginate()
again with different options, you'll need to unload the component first, e.g.:
$this->Components->unload('Paginator');
Then, the next time you call YourController::paginate()
, it will reload whatever's in the YourController::$paginate
property.
Upvotes: 3
Reputation: 855
So upon some more poking around I discovered the following:
Any alterations made to $paginate
after an initial paginate()
call is made are not carried through to the Paginator
component. This applies to conditions
, order
, limit
, etc.
So doing this:
$this->paginate['<model1>']['conditions'] = array( ... );
$model1Results = $this->paginate("<model1>");
$this->paginate['<model2>']['conditions'] = array( ... );
$model2Results = $this->paginate("<model2>");
Will return results for <model1>
that obey the new conditions/order/limit/whatever you've applied, but your results for <model2>
will be based on the original conditions defined for it in $paginate
. Your controller will see the updates to $paginate
just fine, but it appears $paginate
can only be grabbed by Paginator
once.
The workaround I have found is to make any and all changes to $paginate
BEFORE the first paginate()
call, so:
$this->paginate['<model1>']['conditions'] = array( ... );
$this->paginate['<model2>']['conditions'] = array( ... );
$model1Results = $this->paginate('<model1>');
$model2Results = $this->paginate('<model2>');
I've been poking around in PaginatorComponent.php
to figure out why things work this way, and any further insight would, of course, be appreciated.
Upvotes: 2