cmw
cmw

Reputation: 855

CakePHP -- conditions ignored when using paginate() twice in a single action

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

Answers (2)

Dave Hensley
Dave Hensley

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

cmw
cmw

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

Related Questions