cakePHP :Find all query conditions questions - cakephp

I'm trying to format a find condition to generate a set of data to be later printed to an excel view via PhPExcel. The intended functionality is that if the start_date and the end_date fields match for the entry, the entry is included in the array. If the fields do not match, it's excluded.
$my_data = $this-> RecordsMaster-> find("all", array(
'conditions' => array(
"RecordsMaster.start_date" == "RecordsMaster end_date"
)
));
The problem lies in the fact that the statement above doesn't actually do anything. Rows from mySQL table with start and end dates that differ are included in the data dump to excel.

The conditions in the question don't mean much
The conditions in the question are equivalent to:
$my_data = $this-> RecordsMaster-> find("all", array(
'conditions' => array(
false
)
));
Which will return no records, since it will generate the following sql:
WHERE 0;
Don't use key => value to match fields
Correcting the typos in the code in the question would still not generate desired sql - it would generate:
WHERE `RecordsMaster`.`start_date` = "RecordsMaster end_date"
I.e. where start_date is that litteral string.
The intended functionality is that if the start_date and the end_date fields match for the entry
To generate WHERE field = otherfield you can specify as a string:
$my_data = $this-> RecordsMaster-> find("all", array(
'conditions' => array(
"RecordsMaster.start_date = RecordsMaster.end_date"
)
));

Related

CakePHP find joined records with conditions on each table

I want to see all the records from a join, setting a WHERE condition on each side of the join.
For example, I have LOAN and BORROWER (joined on borrower.id = loan.borrower_id). I want the records where LOAN.field = 123 and BORROWER.field = 'abc'.
The answers here (this one, for example) seem to say that I should use Containable.
I tried that. Here's my code:
$stuff = $this->Borrower->find('all', array(
'conditions' => array(
'Borrower.email LIKE' => $this->request->data['email'] // 'abc'
),
'contain'=>array(
'Loan' => array(
'conditions' => array('Loan.id' => $this->request->data['loanNumber']) // 123
)
)
));
I expected to have a single result because in my data, there is only one joined record with both of those conditions. Instead, I get two results,
Result 1 is {Borrower: {field:abc, LOAN: {field: 123} } // correct
Result 2 is {Borrower: {field:abc, LOAN: {NULL} } // incorrect
When I look at the SQL that CakePHP used, I don't see a join. What I see is two separate queries:
Query 1: SELECT * from BORROWER // (yielding 2 IDs),
Query 2: SELECT * FROM LOAN WHERE borrower_id in (IDs)
This is not what I want. I want to join the tables, then apply my conditions. I could easily write the SQL query, but am trying to do it the Cake way since we've adopted that framework.
Is it possible?
Try to do something like this:
$options['conditions'] = array(
'Borrower.email LIKE' => $this->request->data['email'] // 'abc',
'loan.field' => '123' )
$options['joins'] = array(
array('table' => 'loans',
'alias' => 'loan',
'type' => 'INNER',
'conditions' => array(
'borrower.id = loan.borrower_id')
)
);
$options['fields'] = array('borrower.email', 'loan.field');
$test = $this->Borrower->find('all', $options);
You should see a SQL statement like:
SELECT borrower.email, loan.field
FROM borrowers AS borrower
INNER JOIN loans AS loan
ON borrower.id = loan.borrower_id
AND loan.field = '123'
WHERE borrower.email = 'abc'
Your results will be in an array
{Borrower: {field:abc} LOAN: {field: 123} }
You will find more information in this document.
I think I'll accept Jose's answer because it's exactly what I want. But I did notice that I didn't need any fancy tricks -- no joins or contains -- if I used the other model as my starting point.
A Borrower hasMany Loans, and a Loan belongsTo a Borrower. Using Loan as my model, Cake will automatically join the tables, but not using Borrower.
$this->Loan->find('all', array( // Not $this->Borrower->find() !
'conditions' => array(
'Borrower.field' => 'abc',
'Loan.field' => 123
)
));

findall ignoring order parameter?

I'm trying to use the following:
$this->Chapter->recursive=1;
$chaps = $this->Chapter->find('all', array(
'order'=> array('sequence_number' => 'ASC')
));
$this->set('chapters', $chaps );
to retrieve all my chapters by increasing order, but CakePHP seems to be ignoring the 'order' parameter. I believe that I have the syntax correct (based on view-source:http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#creating-custom-find-types, which says the following should work:
public function index() {
$articles = $this->Article->find('available', array(
'order' => array('created' => 'desc')
));
}
). The SQL for the table looks like:
CREATE TABLE chapters (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
sequence_number INT UNSIGNED,
title VARCHAR(50)
);
and I'm not getting any syntax or run-time errors. However, the SQL generated by Cake to actually get the chapter records is:
SELECT `Chapter`.`id`, `Chapter`.`sequence_number`, `Chapter`.`title`
FROM `Tutorial`.`chapters` AS `Chapter` WHERE 1 = 1
Clearly I'm doing something wrong, but I don't know what it is.
As a work-around I'm happy to put an order property on the Model. Since I typically want to retrieve chapters by sequence number I'm fine with adding this to the model:
public $order = 'Chapter.sequence_number ASC';
Once I do that Cake generates
SELECT `Chapter`.`id`, `Chapter`.`sequence_number`, `Chapter`.`title`
FROM `Tutorial`.`chapters` AS `Chapter` WHERE 1 = 1
ORDER BY `Chapter`.`sequence_number` ASC
What if you try something like this, instead:
$chaps = $this->Chapter->find('all', array(
'order' => array('Chapter.sequence_number ASC')
));
The main difference being this part: Chapter.sequence_number ASC

How to use expression with '<' and '>' in CakePHP Model

How to generate this statement correctly from CakePHP?
DELETE FROM table as Table WHERE Table.expire < NOW();
I have tried this but this is not working:
$this->deleteAll(array(
'Table.expire <' => $this->getDataSource()->expression('NOW()')
));
//It complains:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Table.expire <' in 'where clause'
SQL Query:
SELECT `Table`.`id` FROM `database`.`table` AS `Table` WHERE `Table.expire <` = NOW()
The only way to make it working now is by making the whole condition as a string:
$this->deleteAll('Table.expire < NOW()');
:: OR ::
$this->deleteAll(array('Table.expire < NOW()'));
I've tested your situation, because I was a bit surprised that CakePHP didn't properly 'quote'
Table.expire <
to
`Table`.`expire` <
(which it normally does).
This may be related to the fact that $this->getDataSource()->expression('NOW()') Returns a stdObject with 'NOW()' as a 'value' property. Maybe this is a 'special' case in the logic of CakePHP, I'm not really sure.
Array-notation assumes 'literal' values, not 'expressions'
Anyway, in general, the 'array notation' will regard the value passed a 'literal' value, not an expression, and therefore quote it as such, e.g.
'conditions' => array('Table.expire <' => 'some value')
'conditions' => array('Table.expire < ?' => 'another value')
'conditions' => array('Table.expire <' => 'NOW()')
Will all be regarded 'literal' values, resulting in these SQL conditions;
WHERE `Table`.`expire` < 'some value'
WHERE `Table`.`expire` < 'another value'
WHERE `Table`.`expire` < 'NOW()'
The values are thus regarded a string and 'NOW()' will not be seen as 'the current date/time' by the database.
Options
This will leave you with some options;
Specify the condition as a String, not a key/value array
You already provided this option in your question:
'conditions' => array('Table.expire < NOW()')
This solution should be safe in your situation (no user input is included in the statement), however, if user input will be included in such statement, it should be properly sanitized. Also, NOW() may not be valid cross-database (ANSI SQL uses CURRENT_TIMESTAMP for the current date/time)
Specify the current date/time as a ISO-date, not as an 'expression'
This is the alproach I normally use;
If you prefer the array-notation, you may consider to provide a 'literal' date for the condition, preferably as an ISO-date, which is handled properly by most databases;
'conditions' => array('Table.expire <' => date('Y-m-d H:i:s'))
// or
'conditions' => array('Table.expire < ?' => date('Y-m-d H:i:s'))
note I've created all my examples to be used as the 'conditions' key for a find(), however, the same will apply for a deleteAll()
Hope this helps!
Try this
$this->delete("all", array('conditions'=>array(
'Table.expire < ' => $this->getDataSource()->expression('NOW()')))
);

CakePHP Search between 2 Date Records

I am building a small Web App that lets users reserve Office Rooms and Equipment. For the Reservation they enter a Start and an End Date.
When a user tries to find out if any (for example) car is available on 2012-10-23, and the database holds reservation date records of Start: 2012-10-20 and End: 2012-10-25 for (lets say) all the cars, how do I include all the dates between my date entries in the search?
The $date variable gets it's value from the Date Search Form Field.
This, unfortunately does not work, and I can't figure out how to use daysAsSql for this query:
$conditions = array(
'conditions' => array(
'? BETWEEN ? AND ?' => array($date,'Equipment.date_start','Equipment.date_end'),
)));
$this->set('equipments', $this->Equipment->find('all', $conditions));
There is a simpler solution to this, but thanks for the help:
(As a condition in the find:)
array('Equipment.date_start <= ' => $date,
'Equipment.date_end >= ' => $date
),
In case if you have one date and you want to use BETWEEN
$date_start = start date;
$date_end = date_end;
$conditions = array(
'conditions' => array(
'date(Equipment.create) BETWEEN ? AND ?' => array($date_start, $date_end),
)));
$this->set('equipments', $this->Equipment->find('all', $conditions));
This is the case if you have From AND To
in your case
$date_start = start date;
$date_end = date_end;
$conditions = array(
'conditions' => array(
'Equipment.start_date >=' => array($date_start),
'Equipment.end_date <=' => array($date_end)
));
$this->set('equipments', $this->Equipment->find('all', $conditions));
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
$start = date('Y-m-d');
$end = date('Y-m-d', strtotime('+1 month'));
$conditions = array('Event.start <=' => $end, 'Event.end >=' => $start);
$this->Event->find('all', array('conditions' => $conditions));
If you are on cakephp 2.0 this is the answer http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::daysAsSql
If you are on cakephp 1.3 you can use TimeHelper, just import it and use the same function as per example in documentation here http://book.cakephp.org/1.3/en/view/1471/Formatting
You cannot use database columns with this BETWEEN-syntax. If you assign strings in the array, CakePHP will quote them. Same for numeric values depending on your database setup.
CakePHP will quote the numeric values depending on the field type in your DB.
– see http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#complex-find-conditions
If you still want to use the BETWEEN, you can write your queries into the array key because CakePHP will not escape the keys, but only the values.
CakePHP only escapes the array values. You should never put user data into the keys. Doing so will make you vulnerable to SQL injections.
– see http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#complex-find-conditions
Concerning your problem:
$this->Model->find('all', array(
'conditions' => array(
'"YYYY-MM-DD" BETWEEN Model.date_start AND Model.date_end',
),
));
You can even work with MySQL date and time functions:
$this->Model->find('all', array(
'conditions' => array(
'CURDATE() BETWEEN Model.date_start AND Model.date_end',
),
));
If you use date/time functions, do not quote them. If you use a specific date put it in quotes.

convert query() to find() cakephp

$v = array(1,11.38,15.8);
$sortByPrice = $this->Product->query
(
"SELECT *,
CASE `currency`
WHEN '1' THEN $v[0]
WHEN '2' THEN $v[1]
WHEN '3' THEN $v[2]
END AS 'ratio'
FROM products
ORDER BY price*ratio DESC
"
);
i want to convert what is above to a find function
i tried something like that(but it do not work)..
$v = array(1,11.38,15.8);
$bla = $this->Product->find('all', array(
'conditions' => array(
'Product.currency',
'((
CASE WHEN
Product.currency=1 THEN $v[0]
Product.currency=2 THEN $v[1]
Product.currency=3 THEN $v[2]
END
)) AS ratio'),
'order' => 'ratio',
'limit' => 10
));
can somebody to convert query into find
You are putting into the conditions key. Move it into the fields key or make it a virtualField - that may work.
Edit: also $v[0] in single quotes will not actually replace it with the variable - it will just appear as that text.

Resources