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
)
));
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 am doing a find on a model as shown below however the SQL being generated is wrong due to which I am getting incorrect results.
$bookings = $this->Booking->find('all', array(
'conditions' => array(
'Booking.created BETWEEN ? AND ?' => array(
date('Y-m-d H:i:s', strtotime('-24 hours')),
date('Y-m-d H:i:s', strtotime('-12 hours'))
),
'OR' => array(
'Booking.payment_status' => 3,
'Booking.payment_status' => 2
)
)
));
WHERE condition being generated by this find is (as shown by cake debugkit)
WHERE `Booking`.`created` BETWEEN '2013-03-27 11:01:57' AND '2013-03-27 23:01:57' AND `Booking`.`payment_status` = 2
And the where condition I am trying to create is
WHERE `Booking`.`created` BETWEEN '2013-03-27 11:01:57' AND '2013-03-27 23:01:57' AND (`Booking`.`payment_status` = 2 OR `Booking`.`payment_status` = 3)
Any ideas what I am doing wrong here. Quick help would be highly appreciated. Thanks!
A frequently made PHP beginner mistake - using a key in an array more than once:
'OR' => array(
'Booking.payment_status' => 3,
'Booking.payment_status' => 2
)
It should read
'OR' => array(
array('Booking.payment_status' => 3),
array('Booking.payment_status' => 2)
)
Im trying to replicate the following query in cakephp:
SELECT *
FROM uploads, proposals
WHERE proposals.id = uploads.proposal_id AND proposals.tender_id = 10
Im using the find method in the Upload model with the following conditions:
$conditions = array(
'Proposal.id' => $id,
'AND' => array(
'Upload.proposal_id' => 'Proposal.id'
)
);
return($this->find('list', array('conditions' => $conditions)));
but im getting this query instead
SELECT `Upload`.`id`, `Upload`.`title`
FROM `kumalabs_lic`.`uploads` AS `Upload`
WHERE `Proposal`.`id` = 10 AND `Upload`.`proposal_id` = 'Proposal.id'
as you can see, the proposals table is missing, can somebody explain me how can i make this query?
Thanks :)
I would recommend you use the linkable behaviour for this. It is much easier than the default way of doing joins in CakePHP. It works with the latest version of CakePHP, as well as 1.3.
CakePHP Linkable Behavior
You would then modify your find to look like this:
return($this->find('list', array(
'link' => array('Proposal'),
'conditions' => array(
'Proposal.id' => $id,
),
'fields' => array(
'Upload.*',
'Proposal.*',
),
)));
CakePHP will automatically join on your primary / foreign key, so no need to have the
'Upload.proposal_id' => 'Proposal.id'
condition.
Though you don't need that condition, I also want to point out that you are doing your AND wrong. This is how you do AND and OR in CakePHP
'conditions' => array(
'and' => array(
'field1' => 'value1', // Both of these conditions must be true
'field2' => 'value2'
),
'or' => array(
'field1' => 'value1', // One of these conditions must be true
'field2' => 'value2'
),
),
If the model have association, CakePHP automatically join the table by 'contain' keyword. Try code bellow:
public function getProposalsFromTender($id){
$data = $this->find('all', array(
'conditions' => array('Proposal.id' => $id),
'fields' => array('Upload.*', 'Proposal.*'),
'contain' => array('Proposal')
));
return($data);
}
Note:
CakePHP use explicit join instead of implicit join like ...from proposals, uploads...
I'm not familiar with that JOIN syntax but I believe it equals this:
SELECT *
FROM uploads
INNER JOIN proposals ON proposals.id = uploads.proposal_id
WHERE proposals.tender_id = 10
... so you need something similar to this:
// Untested
$conditions = array(
'Proposal.id' => $id,
'joins' => array(
array(
'alias' => 'Proposal',
'table' => 'proposals',
'type' => 'INNER',
'conditions' => 'Proposal.id = Upload.proposal_id',
),
),
);
Of course, this is a direct translation of your JOIN. If your models are properly related, it should all happen automatically.
I have a start and an end date in my database and a $date variable from a form field. I am now trying to query all the rows where $date is either = start/end date in the db, or ANY date between those two.
It's kind of the opposite of what is described in the docs of how daysAsSql works. I can't figure out how to get it to work. The following line does not work as a find condition in the controller:
'? BETWEEN ? AND ?' => array($date, 'Item.date_start', 'Item.date_end'),
Any help is greatly appreciated. This is driving me crazy.
Here is the complete Query and corresponding SQL:
$conditions = array(
'conditions' => array(
'and' => array(
'? BETWEEN ? AND ?' => array($date, 'Item.date_start', 'Item.date_end'),
'Item.title LIKE' => "%$title%",
'Item.status_id =' => '1'
)));
$this->set('items', $this->Item->find('all', $conditions));
WHERE (('2012-10-06' BETWEEN 'Item.date_start' AND 'Item.date_end') AND (`Item`.`title` LIKE '%%') AND (`Item`.`status_id` = 1))
$conditions = array(
'conditions' => array(
'and' => array(
array('Item.date_start <= ' => $date,
'Item.date_end >= ' => $date
),
'Item.title LIKE' => "%$title%",
'Item.status_id =' => '1'
)));
Try the above code and ask if it not worked for you.
Edit:
As per #Aryan request, if we have to find users registered between 1 month:
$start_date = '2013-05-26'; //should be in YYYY-MM-DD format
$this->User->find('all', array('conditions' => array('User.reg_date BETWEEN '.$start_date.' AND DATE_ADD('.$start_date.', INTERVAL 30 DAY)')));
Here is CakePHP BETWEEN query example.
I'm defining my arrays as variables, and then using those variables in my CakePHP find function call:
// just return these two fields
$fields = array('uri', 'page_views');
// use this "between" range
$conditions = array('Event.date BETWEEN ? and ?' => array($start_date, $end_date));
// run the "select between" query
$results = $this->Event->find('all',
array('fields'=>$fields,
'conditions'=>$conditions));
Ref from
General Example For CakePHP 2.x Query
$_condition = array("TABLENAME.id" => $id, "TABLENAME.user_id" => array_unique($_array), 'date(TABLENAME.created_at) BETWEEN ? AND ?' => array($start_date, $end_date));
$result_array = $this->TABLENAME->find("all", array(
'fields' => array("TABLENAME.id", "TABLENAME.user_id", 'TABLENAME.created_at'),
"conditions" => $_condition,
"group" => array("TABLENAME.id"), //fields to GROUP BY
'joins' => array(
array(
'alias' => 'T2',
'table' => 'TABLENAME2',
'type' => 'LEFT',
'conditions' => array('TABLENAME.t_id = TABLENAME2.t_id')
),
array(
'alias' => 'T3',
'table' => 'TABLENAME3',
'type' => 'LEFT',
'conditions' => array(
'IF(
TABLENAME.t3_id > 0,
T2.f_id = T3.f_id,
TABLENAME.ff_id = T2.ff_id
)'
)
),
),
'recursive' => 0
)
);
This is more efficient and understandable IN BETWEEN query in cakephp 2.x
$testing_log_device_site_name = $testingLogData['TestingLogDevice']['Siteid'];
$conditions = array('TestingLogDevice.dateee BETWEEN ? and ?' => array($start_date, $end_date));
$results = $this->TestingLogDevice->find('all',
array(
'fields'=>array('dateee','timeee','Siteid'),
'conditions'=>array($conditions, 'TestingLogDevice.Siteid'=>$testing_log_device_site_name)
)
);
pr($results);
$data=$this->post->find('all')->where([ 'id'=>$id,
'price between'=>$price1,'and'=>$price2])->toArray();
This query works as following:
select * from post where id=$id and price between $price1 and $price2;
" -- 'price between'=>$price1 --" become "price between $price1"
Use this
$today = new DateTime( date('Y-m-d'));
$fiveYearsBack = $today->sub(new DateInterval('P5Y'));
This is pretty straightforward, but I guess I've never bumped into it before. I have a Page model that hasMany Comment. I'd like to pull all of the pages that have at least 1 comment, but eliminate pages with none. As I look at it, I realize that I'm not sure how to do that. I guess I could use ad hoc joins, but I'd rather use Containable, if that's possible. I've tried testing not null in the Comment conditions and one or 2 other things that were unlikely to work, but it seems like this should be possible.
What I get now, of course, is all pages and some of those page records have an empty Comment member. Be nice to skip passing around all of the extra cruft if I can do so.
My find call:
$pages = $this->Folder->Page->find(
'all',
array(
'contain' => array(
'Comment' => array(
'order' => array( 'Comment.modified DESC' ),
),
'Folder' => array(
'fields' => array( 'Folder.id' ),
),
),
'conditions' => array(
'Folder.group_id' => $id,
),
)
);
Thanks.
You have several approaches available other than ad-hoc joins:
Denormalize your dataset and add a has_comment flag to your pages table. Add 'Page.has_comment' => 1 to your conditions.
Run your query against Comment, with DISTINCT page_id.
$comments = $this->Folder->Page->Comment->find('all', array(
'conditions' => array('DISTINCT Comment.page_id'
'contain' => array(
'Page' => array(
'Folder'
)
)
);
First grab a distinct set of page ids from the comments table.
$page_ids = $this->Folder->Page->Comment->find('list', array(
'fields' => array('id', 'page_id'),
'conditions' => array('DISTINCT Comment.page_id'),
);
$pages = $this->Folder->Page->find('all', array(
'conditions' => array('Page.id' => $page_ids),
'contain' => array(
...
)
);