Paginate ignores conditions - cakephp

when I try to run this code, it will not change my query, the condition is just not taken:
$this->paginate = array(
'conditions' => array(
'campaign_id' => $this->request->data['Campaign']['campaign_id']
)
);
$this->set('products', $this->Paginator->paginate());
the query looks like this
SELECT
`Product`.`id`, `Product`.`campaign_id`, `Campaign`.`id`, `Campaign`.`title`, `Campaign`.`text`
FROM
`db`.`products` AS `Product`
LEFT JOIN
`db`.`campaigns` AS `Campaign` ON (`Product`.`campaign_id` = `Campaign`.`id`)
WHERE
1 = 1
LIMIT
20
Is there anything wrong in my syntax?
(CakePHP 2.5.1)

What #ndm means is replace your existing code with this:
$this->Paginator->settings['conditions'] = array(
'Product.campaign_id' => $this->request->data['Campaign']['campaign_id']
)
$this->set('products', $this->Paginator->paginate());

Related

I want Cakephp code in my query

I am Cakephp Beginner.
I want this query with cakephp..
Original Query..
SELECT `Advertisement`.*, `Gallery`.`image` FROM `moenatmi_moen`.`advertisements`
AS `Advertisement` LEFT JOIN `moenatmi_moen`.`galleries` AS `Gallery` ON
(`Gallery`.`advertise_id` = `Advertisement`.`id` AND `Gallery`.`main_ad_image`
= 1) WHERE `Advertisement`.`status` = 1 AND `Advertisement`.`category_id` = 14 AND
((`Advertisement`.`expiry_date` >= '2014-11-18') OR (`Advertisement`.`expiry_date`
IS NULL)) GROUP BY `Advertisement`.`id` ORDER BY `Advertisement`.`id` desc
LIMIT 10
I Want Like this query..
SELECT `Advertisement`.*, `Gallery`.`image` FROM `moenatmi_moen`.`advertisements`
AS `Advertisement` LEFT JOIN `moenatmi_moen`.`galleries` AS `Gallery` ON
(`Gallery`.`advertise_id` = `Advertisement`.`id` AND `Gallery`.`main_ad_image` = 1)
WHERE `Advertisement`.`status` = 1 AND `Advertisement`.`category_id` IN (select id from
categories where id=14 or parent_id=14) AND ((`Advertisement`.`expiry_date` >=
'2014-11-18') OR (`Advertisement`.`expiry_date` IS NULL)) GROUP BY
`Advertisement`.`id` ORDER BY `Advertisement`.`id` desc LIMIT 10
Original Cakephp Code..
$this->paginate['conditions'] = array(
'Advertisement.status'=>1,
'Advertisement.category_id'=>$cat_id,
'OR'=>array(
'Advertisement.expiry_date >='=>date('Y-m-d'),
'Advertisement.expiry_date'=>null
)
);
Help me..
Thanks You..
this should be a painless switch. This first query calls the category ids that you're wanting within your main query.
$cat_id is the same variable you're using in the original query.
$ids = $this->Category->find('all', array(
'conditions' => array(
'id' => $cat_id,
'parent_id' => $cat_id
)
)
);
The important thing to note here, this query will output your typical nested array..
array(
array(),
array(),
array()
);
The classicExtract function will sift through the mess for you and give you a single array with a list of 'id' which is exactly what you want for your query. EX:
array(14, 15, 16, 18, 19);
Finally, your main query.
$this->paginate['conditions'] = array(
'Advertisement.status'=>1,
'Advertisement.category_id'=> Set::classicExtract($ids, '{n}.Category.id'),
'OR'=>array(
'Advertisement.expiry_date >=' => date('Y-m-d'),
'Advertisement.expiry_date'=>null
)
);
Conclusion
All together your new conditions will look just like this:
$ids = $this->Category->find('all', array(
'conditions' => array(
'id' => $cat_id,
'parent_id' => $cat_id
)
)
);
$this->paginate['conditions'] = array(
'Advertisement.status'=>1,
'Advertisement.category_id'=> Set::classicExtract($ids, '{n}.Category.id'),
'OR'=>array(
'Advertisement.expiry_date >=' => date('Y-m-d'),
'Advertisement.expiry_date'=>null
)
);

how to pass raw sql query into paginator component in cakephp

I have row sql query and i want to paginate this query with the help of cakephp paginator component.How could i ?
$q = "SELECT
`UserList`.`id`,`UserList`.`user_id`,`UserList`.`list_user_id`,
`User`.`username` ,`User`.`id` ,
`UserDetail`.`sex`,`UserDetail`.`image`,`UserDetail`.`display_name`,`UserDetail`.`country`,`UserDetail`.`height`,`UserDetail`.`weight`,`UserDetail`.`hair_color`,`UserDetail`.`eye_color`
from `user_lists` as `UserList`
inner join `users` as `User` on `UserList`.`list_user_id` = `User`.`id`
inner join `user_details` as `UserDetail` on `UserList`.`list_user_id` = `UserDetail`.`user_id`
where ((`UserList`.`user_id`=".$user_id.") and (`UserList`.`in_list`='short')) order by `UserList`.`created` desc limit 1";
$this->paginate = array(
'conditions' => /*Pass $q here */,
'limit' => 15,
)
);
thanks in advance.
You don't
That just doesn't work, from the query in the question though, all you need is to use pagination as normal:
class UserListsController extends AppController {
public $paginate = array(
'contain' => array(
'User',
'UserDetail'
)
);
public function index($userId = null) {
$conditions = array(
'user_id' => $userId,
'in_list' => 'short'
);
$data = $this->paginate($conditions);
$this->set('data', $data);
}
}

CakePHP - How to use SQL NOW() in find conditions

I am having trouble getting any find operations to work using the SQL function NOW() in my conditions.
I am effectively trying to build a find query that says:
Desired SQL:
WHERE (NOW() BETWEEN Promotion.start AND Promotion.end) AND Promotion.active = 1
I have tried many combinations but no matter what I do when using NOW() in the condition, it doesn't work because the query Cake builds puts ' quotation marks around the model fields so they are interpreted by MySQL as a string.
$this->find('all', array(
'conditions' => array(
'(NOW() BETWEEN ? AND ?)' => array('Promotion.start', 'Promotion.end'),
'Promotion.active' => 1
)
));
CakePHP created SQL:
Notice the single quotes around the model fields in the BETWEEN(), so they are treated as strings.
WHERE (NOW() BETWEEN 'Promotion.start' AND 'Promotion.end') AND `Promotion`.`active` = '1'
This doesn't work either.
$this->find('all', array(
'conditions' => array(
'NOW() >=' => 'Promotion.start',
'NOW() <=' => 'Promotion.end',
'Promotion.active' => 1
)
));
I know why these solutions don't work. It's because the model fields are only treated as such if they are the array key in the conditions, not the array value.
I know I can get this to work if I just put the whole BETWEEN() condition as a string:
$this->find('all', array(
'conditions' => array(
'NOW() BETWEEN Promotion.start AND Promotion.end',
'Promotion.active' => 1
)
));
Another example of the same problem is, which is simpler to understand:
Desired SQL:
WHERE Promotion.start > NOW() AND Promotion.active = 1
So I try this:
$this->find('all', array(
'conditions' => array(
'Promotion.start >' => 'NOW()',
'Promotion.active' => 1
)
));
And again it doesn't work because Cake puts ' quotations around the NOW() part.
CakePHP created SQL:
WHERE `Promotion`.`start` > 'NOW()' AND `Promotion`.`active` = '1''
$this->find('all', array(
'conditions' => array(
'NOW() BETWEEN Promotion.start AND Promotion.end',
'Promotion.active' => 1
)
));
Better to not use NOW() as its a function and functions don't use indexes. A better solution would be:
$this->find('all', array(
'conditions' => array(
"'" . date('Y-m-d') . "' BETWEEN Promotion.start AND Promotion.end",
'Promotion.active' => 1
)
));

sum() function in cakephp query

I am using this query, but it is not returning ctotal. Please help.
$total = $this->RequestedItem->find('all',
[
'sum(cost * quantity) AS ctotal',
'conditions' => [
'RequestedItem.purchase_request_id' => $_GET['po_id']
]
]
);
You should not be using PHP superglobals directly in CakePHP. You should instead use Model.field naming so that you do not get ambiguous field errors.
Virtual fields is the way to go but that is not your problem, you need to read the book some more.
$total = $this->RequestedItem->find('all', array(array('fields' => array('sum(Model.cost * Model.quantity) AS ctotal'), 'conditions'=>array('RequestedItem.purchase_request_id'=>$this->params['named']['po_id'])));
should work fine, with the virtualFields it would be
var $virtualFields = array('total' => 'SUM(Model.cost * Model.quantity)');
$total = $this->RequestedItem->find('all', array(array('fields' => array('total'), 'conditions'=>array('RequestedItem.purchase_request_id'=>$this->params['named']['po_id'])));
Fields go in the 'fields' key, just like conditions go in the 'conditions' key. See http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#find
This works too, worked fine for me
$sum = $this->Modelname->find('all', array(
'conditions' => array(
'Modelname.fieldname' => $conditions),
'fields' => array('sum(Modelname.fieldname) as total_sum'
)
)
);
Temporarily set the virtualFields prior to doing a find.
$this->MaterialScan->virtualFields = array(
'total_qty' => 'COUNT(MaterialScan.id)',
'total_lbs' => 'SUM(MaterialScan.weight)'
);
$materialScans = $this->MaterialScan->find('all',array(
'conditions' => array(
'MaterialScan.id' => $scans
),
'group' => array('MaterialScan.part_number')
));
This avoids having the [0] elements in the returned array.
You can use virtualFields:
var $virtualFields = array(
'the_sum' => 'SUM(Model.cost * Model.quantity)'
);

Complex find query for hasMany relationship in cakePHP 1.3

I have been busy with the cakePHP framework for a couple of months now and I really love it. At the moment I'm working on a very new project and it does the job like it should (I think ...) but I feel uncomfortable with some code I wrote. In fact I should optimize my paginate conditions query so I get immediately the right results (right now I manipulate the result set by a bunch of Set::extract method calls.
I'll sketch the relevant aspects of the application. I have a model 'Site' who has a hasMany relationship with the model 'SiteMeta'. This last table looks as follow: id, site_id, key, value, created.
In this last model I record several values of the site at various periods. The name of the key I want to store (e.g. alexarank, google pagerank, ...), and off course also the value. At a given interval I let my app update this database so I can track evolution of this values.
Now my problem is this.
On the overview page of the various websites (controller => Sites, action => index) I'd like to show the CURRENT pagerank of the website. Thus I need one exact SiteMeta record where the 'created' field is the highest and the value in 'key' should be matching the word 'pagerank'. I've tried several things I read on the net but got none of them working (containable, bindmodel, etc.). Probably I'm doing something wrong.
Right now I get results like this when I do a $this->paginate
Array
(
[0] => Array
(
[Site] => Array
(
[id] => 1
[parent_id] => 0
[title] => test
[url] => http://www.test.com
[slug] => www_test_com
[keywords] => cpc,seo
[language_id] => 1
)
[SiteMeta] => Array
(
[0] => Array
(
[id] => 1
[site_id] => 1
[key] => pagerank
[value] => 5
[created] => 2010-08-03 00:00:00
)
[1] => Array
(
[id] => 2
[site_id] => 1
[key] => pagerank
[value] => 2
[created] => 2010-08-17 00:00:00
)
[2] => Array
(
[id] => 5
[site_id] => 1
[key] => alexa
[value] => 1900000
[created] => 2010-08-10 17:39:06
)
)
)
To get the pagerank I just loop through all the sites and manipulate this array I get. Next I filter the results with Set::extract. But this doens't feel quite right :)
$sitesToCheck = $this->paginate($this->_searchConditions($this->params));
foreach($sitesToCheck as $site) {
$pagerank = $this->_getPageRank($site['Site']);
$alexa = $this->_getAlexa($site['Site']);
$site['Site']['pagerank'] = $pagerank;
$sites[] = $site;
}
if (isset($this->params['named']['gpr']) && $this->params['named']['gpr']) {
$rank = explode('-', $this->params['named']['gpr']);
$min = $rank[0];$max = $rank[1];
$sites = Set::extract('/Site[pagerank<=' . $max . '][pagerank>=' . $min .']', $sites);
}
$this->set(compact('sites', 'direction'));
Could you guys please help me to think about a solution for this? Thanks in advance.
Thanks for the contributions. I tried these options (also something with bindmodel but not working also) but still can't get this to work like it should be. If I define this
$this->paginate = array(
'joins'=> array(
array(
'table'=>'site_metas',
'alias'=>'SiteMeta',
'type' =>'inner',
'conditions' =>array('Site.id = SiteMeta.site_id')
)
),
);
I get duplicate results
I have a site with 3 different SiteMeta records and a site with 2 different record.
The paginate method returns me 5 records in total. There's probably an easy solution for this, but I can't figure it out :)
Also I tried to write a sql query myself, but seems I can't use the pagination magic in that case. Query I'd like to imitate with pagination options and conditions is the following. The query returns exactly as I would like to get.
$sites = $this->Site->query('SELECT * FROM sites Site, site_metas SiteMeta WHERE SiteMeta.id = (select SiteMeta.id from site_metas SiteMeta WHERE Site.id = SiteMeta.site_id AND SiteMeta.key = \'pagerank\' order by created desc limit 0,1 )');
As you are trying to retrieve data in a hasMany relationship, cakephp doesn't join the tables by default. If you go for joins you can do something like:
$this->paginate = array(
'joins'=>array(
array(
'table'=>'accounts',
'alias'=>'Account',
'type' =>'inner',
'conditions' =>array('User.id = Account.user_id')
)
),
'conditions'=> array('OR' =>
array(
'Account.name'=>$this->params['named']['nickname'],
'User.id' => 5)
)
);
$users = $this->paginate();
$this->set('users',$users);
debug($users);
$this->render('/users/index');
You have to fit this according to your needs of course. More on joins, like already mentioned in another answer.
Edit 1: This is because you are missing the second 'conditions'. See my code snippet. The first 'conditions' just states where the join happens, whereas the second 'conditions' makes the actual selection.
Edit 2: Here some info on how to write conditions in order to select needed data. You may want to use the max function of your rdbms on column created in your refined condition.
Edit 3: Containable and joins should not be used together. Quoted from the manual: Using joins with Containable behavior could lead to some SQL errors (duplicate tables), so you need to use the joins method as an alternative for Containable if your main goal is to perform searches based on related data. Containable is best suited to restricting the amount of related data brought by a find statement. You have not tried my edit 2 yet, I think.
Edit 4: One possible solution could be to add a field last_updated to the table Sites. This field can then be used in the second conditions statement to compare with the SiteMeta.created value.
Try something like this:
$this->paginate = array(
'fields'=>array(
'Site.*',
'SiteMeta.*',
'MAX(SiteMeta.created) as last_date'
),
'group' => 'SiteMeta.key'
'conditions' => array(
'SiteMeta.key' => 'pagerank'
)
);
$data = $this->paginate('Site');
Or this:
$conditions = array(
'recursive' => 1,
'fields'=>array(
'Site.*',
'SiteMeta.*',
'MAX(SiteMeta.created) as last_date'
),
'group' => 'SiteMeta.key'
'conditions' => array(
'SiteMeta.key' => 'pagerank'
)
);
$data = $this->Site->find('all', $conditions);
If that does not work check this and this. I am 100% sure that it is possible to get the result you want with a single query.
Try something like this (with containable set up on your models):
$this->Site->recursive = -1;
$this->paginate = array(
'conditions' => array(
'Site.title' => 'title') //or whatever conditions you want... if any
'contain' => array(
'SiteMeta' => array(
'conditions' => array(
'SiteMeta.key' => 'pagerank'),
'limit' => 1,
'order' => 'SiteMeta.created DESC')));
I use containable so much that I actually have this in my app_model file so it applies to all models:
var $actsAs = array('Containable');
Many thinks to all who managed to help me through this :)
I got it fixed after all hehe.
Eventually this has been the trick for me
$this->paginate = array(
'joins'=> array(
array(
'table'=>'site_metas',
'alias'=>'SiteMeta',
'type' =>'inner',
'conditions' => array('Site.id = SiteMeta.site_id'))
),
'group' => 'Site.id',
'contain' => array(
'SiteMeta' => array(
'conditions' => array(
'SiteMeta.key' => 'pagerank'),
'limit' => 1,
'order' => SiteMeta.created DESC',
)));
$sites = $this->paginate();

Resources