I want to write this query in CakePHP:
SELECT * FROM pm_management.chk_master_xs_tasks
where (eq_model = 'D11 R' OR eq_model = 'TODOS') AND (pm_type = 'XD' OR pm_type = 'XS');
I have tried severals ways like this:
$this->ChkMasterXsTask->find('all', array(
'conditions' => array('AND' => array(
array('OR' => array('ChkMasterXsTask.eq_model' => $eq_model,
'ChkMasterXsTask.eq_model' => 'TODOS')),
array('OR' => array('ChkMasterXsTask.pm_type' => $pm_type,
'ChkMasterXsTask.pm_type' => 'XS'))
)
),
));
But the result is not the expected. Also I saw related questions but doesn't work for me.
Thank you so much for your answers.
Your problem arises from this code (and this problem is repeated for eq_model)
array('ChkMasterXsTask.pm_type' => $pm_type,
'ChkMasterXsTask.pm_type' => 'XS')
which tries to assign 2 values to the same key of that associative array. You could rewrite your code to this:
array(array('ChkMasterXsTask.pm_type' => $pm_type),
array('ChkMasterXsTask.pm_type' => 'XS'))
Another option which improves readability, uses SQL's IN function. The equivalent SQL becomes
SELECT *
FROM pm_management.chk_master_xs_tasks
WHERE eq_model IN ('D11 R', 'TODOS')
AND pm_type IN ('XD', 'XS');
Which should make your find a little easier write:
$this->ChkMasterXsTask->find('all', array(
'conditions' => array(
'ChkMasterXsTask.eq_model' => array($eq_model, 'TODOS'),
'ChkMasterXsTask.pm_type' => array($pm_type, 'XS'))
));
just I see a error y your code, there is a comma, that shouldn't be there.
$this->ChkMasterXsTask->find(
'all', array(
'conditions' => array(
'AND' =>
array('OR' => array(
array('ChkMasterXsTask.eq_model' => $eq_model),
array('ChkMasterXsTask.eq_model' => 'TODOS')
),
array('OR' =>
array('ChkMasterXsTask.pm_type' => $pm_type),
array('ChkMasterXsTask.pm_type' => 'XS')
)
))
));
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 have model relations like this:
Project hasMany SubProject hasMany Item
I want to set up a containable array so that I can find all of the Items which belong to a particular Project, and paginate the results. So, in my ItemsController I have:
public $paginate = array(
'Item' => array(
'limit' => 10,
'order' => array('
'Item.create_time' => 'desc'
),
'contain' => array(
'SubProject' => array(
'Project'
)
)
)
);
Somewhere, obviously, I need to place a condition like "SubProject.project_id = $pid", but nothing I've tried yields the correct results. The best I can manage is results that look like this:
Array
(
[0] => Array
(
[Item] => Array
(
[id] => 13
[file_name] => foo.tar.gz
.... other keys ...
[create_time] => 2013-01-23 14:59:49
[subProject_id] => 4
)
[SubProject] => Array
(
[id] => 4
[name] => foo
[project_id] => 2
..... other keys ....
[Project] => Array
(
)
)
)
[1] => Array
.....
Edit: It is quite correctly omitting the Project record that doesn't match; I want to skip any Item records with out a matching Project record.
It has crossed my mind to manually specify my joins, but I feel like that shouldn't be necessary.
It seems like this should be obvious, but alas, the solution escapes me.
I did eventually solve this problem, so I thought I'd explain what I did in the hope it might help someone else.
After reading this blog post by Mark Story (which is from the days of 1.2 but still relevant) I decided that the thing to do was create a custom find type in my Item model that binds the Project model directly. This gives a first-level association that Containable can filter correctly.
So, in the Items model, I have something like the following (see the documentation on custom find types).
public $findMethods = array('byProject' => true);
public function _findByProject($state, $query, $results=array()) {
if ($state == 'before') {
$this->bindModel(array(
'hasOne' => array(
'Project' => array(
'foreignKey' => false,
'conditions' => array('Project.id = SubProject.project_id')
)
)
));
return $query;
}
return $results;
}
Note that setting foreignKey to false is necessary to prevent CakePHP from trying to automatically use a non-existent database key. In the ItemsController, the pagination options now look like this:
public $paginate = array(
'Item' => array(
'findType' => 'byProject',
'limit' => 10,
'order' => array(
'Item.create_time' => 'desc'
),
'contain' => array(
'SubProject',
'Project'
),
'conditions' => array('Project.id' = $pid)
),
);
...where $pid is the id of the project to display. Some minor tweaks in the view code to accomodate the slightly different results array structure, and I was all set.
EDIT ===============
public $paginate = array(
'limit' => 10,
'order' => 'Item.create_time DESC', //'order' => array(''Item.create_time' => 'desc'),
'contain' => array(
'SubProject' => array(
'Project' => array(
'conditions' => array(
'id' => $project_id // Passed parameter
)
)
)
)
);
=================================================
Have you tried using conditions as in the following? Also, I did not write the 'order' section of the code the way you have it.
public $paginate = array(
'Item' => array(
'limit' => 10,
'order' => 'Item.create_time DESC', //'order' => array(''Item.create_time' => 'desc'),
'contain' => array(
'SubProject' => array(
'Project' => array(
'conditions' => array(
'id' => $project_id // Passed parameter
)
)
)
)
)
);
I am trying to get an array structure of a database and a few of its fields from my controller.
The fields I want are: Document.id, Document.name, Document.submission_date, and the Requester.name which is joined with: Requester.id = Document.requester_id
In my Controller:
Method A:
$documents = $this->Document->find('list', array('fields' => array('Document.name', 'Requester.name', 'Document.submission_date')));
Can't seem to find 'Requester.name'
Method B:
$documents = $this->Document->find('list', array('fields' => array('Document.name', 'Requester.name', 'Document.submission_date'), 'recursive' => 0));
Gives me:
array(
'2012-08-17' => array(
'Document_A' => 'Requester_Z'
),
'2012-08-05' => array(
'Document_B' => 'Requester_Y'
),
'2012-07-09' => array(
'Document_C' => 'Requester_X'
)
)
But I need it to be in format:
array(
(int) 0 => array(
'id' => '16'
'submission_date' => '2012-08-17'
'name' => 'Document_A',
'requester_name' => 'Requester_Z'
),
(int) 1 => array(
'id' => '41'
'submission_date' => '2012-08-05'
'name' => 'Document_B',
'requester_name' => 'Requester_Y'
),
(int) 2 => array(
'id' => '213'
'submission_date' => '2012-07-09'
'name' => 'Document_C',
'requester_name' => 'Requester_X'
),
)
I can't seem to figure it out after going through the 2.0 CakeBook and on StackOverflow...
Any help would be appreciated? Sorry - I'm still a n00b with CakePHP (but REALLY loving it so far!) Thanks!
You need to do a find('all') instead of a find('list') and you can use the fields key like you already are to limit the amount of info that's returned.
It won't come back exactly as you need the data, each field will be in a key of the model it belongs to - if you really need it in that format you can get away with using a virtual field, a custom find, or an afterFind callback to modify the data.
Find list is generally for an id => label formatted array.
You can try this:
$documents = $this
->Document-
>find('all',
array(
'fields' => array('Document.name', 'Requester.name', 'Document.submission_date'),
'joins' => array(
'table' => 'requesters',
'alias' => 'Requester',
'type' => 'left',
'conditions' => array('Requester.id = Document.requester_id')
)
)
);
I have a query that I am running through paginate. This query contains a model ("PaymentException") that has an afterfind method that tacks on a copy of the last "ExceptionWorkflowLog", and calls it "LastWorkflowLog".
The query being passed to paginate:
$this->paginate = array(
'fields' => array(
'PaymentException.*', 'Procedure.id', 'Procedure.cpt',
'Procedure.expected_amount', 'Procedure.allowed_amount', 'Procedure.difference_amount',
'Claim.id', 'Claim.number', 'Payer.abbr'
),
'limit' => 50,
'joins' => array(
array(
'table' => 'procedures',
'alias' => 'Procedure',
'conditions' => array('Procedure.id = PaymentException.procedure_id')
),
array(
'table' => 'claims',
'alias' => 'Claim',
'conditions' => array('Claim.id = Procedure.claim_id')
),
array(
'table' => 'payers',
'alias' => 'Payer',
'conditions' => array('Payer.id = Procedure.payer_id')
),
array(
'table' => 'groups',
'alias' => 'Groups',
'conditions' => array('Groups.id = Claim.group_id')
)
),
'conditions' => $conditions,
'contain' => array('ExceptionWorkflowLog')
);
The resulting array (from the query that combines both "PaymentException", "ExceptionWorkflowLog", and "LastWorkflowLog") looks like below:
0 =>
'PaymentException' => array(fields and values),
'ExceptionWorkflowLog' => array(of ExceptionWorkflowLogs),
'LastWorkflowLog' => array(fields and values of the last indexed ExceptionWorkflowLog)
1 => ...
ExceptionWorkflowLog is mapped to PaymentException by PaymentException.id. It's a many to one relationship (thus the array of results under the ExceptionWorkflowLog).
I would like to use paginate to sort on the "updated" field on either the last indexed ExceptionWorkflowLog or the LastWorkflowLog.
Is there a way to do this with paginate? Currently, if I set the table heading to point to "LastWorkflowLog.updated", the query returns false because the query doesn't know what "LastWorkflowLog" is.
Since this has a couple hundred views, I figured I'd come back and post what I did. CakePHP's handling of joins is absolutely terrible. I rewrote the query to not use joins, but use contains. That seems to have solved it. I feel dirty.
I've been working with cakephp paginations options for 2 days. I need to make a INNER Joins to list a few fields, but I have to deal with search to filter results.
This is portion of code in which I deal with search options by $this->passedArgs
function crediti() {
if(isset($this->passedArgs['Search.cognome'])) {
debug($this->passedArgs);
$this->paginate['conditions'][]['Member.cognome LIKE'] = str_replace('*','%',$this->passedArgs['Search.cognome']);
}
if(isset($this->passedArgs['Search.nome'])) {
$this->paginate['conditions'][]['Member.nome LIKE'] = str_replace('*','%',$this->passedArgs['Search.nome']);
}
and after
$this->paginate = array(
'joins' => array(array('table'=> 'reservations',
'type' => 'INNER',
'alias' => 'Reservation',
'conditions' => array('Reservation.member_id = Member.id','Member.totcrediti > 0' ))),
'limit' => 10);
$this->Member->recursive = -1;
$this->paginate['conditions'][]['Reservation.pagamento_verificato'] = 'SI';
$this->paginate['fields'] = array('DISTINCT Member.id','Member.nome','Member.cognome','Member.totcrediti');
$members = $this->paginate('Member');
$this->set(compact('members'));
INNER JOIN works good, but $this->paginations ignore every $this->paginate['conditions'][] by $this->passedArgs and I cannot have idea how I can work it out.
No query in debug, just the original INNER JOIN.
Can someone helps me ?
Thank you very much
Update:
No luck about it.
I've been dealing with this part of code for many hours.
If I use
if(isset($this->passedArgs['Search.cognome'])) {
$this->paginate['conditions'][]['Member.cognome LIKE'] = str_replace('*','%',$this->passedArgs['Search.cognome']);
}
$this->paginate['conditions'][]['Member.sospeso'] = 'SI';
$this->Member->recursive = 0;
$this->paginate['fields'] = array(
'Member.id','Member.nome','Member.cognome','Member.codice_fiscale','Member.sesso','Member.region_id',
'Member.district_id','Member.city_id','Member.date','Member.sospeso','Region.name','District.name','City.name');
$sospesi = $this->paginate('Member');
everything goes well, and from debug I receive the first condition and the conditions from $this->paginate['conditions'][]['Member.cognome LIKE'], as you can see
array $this->passedArgs
Array
(
[Search.cognome] => aiello
)
Array $this->paginate['conditions'][]
(
[0] => Array
(
[Member.cognome LIKE] => aiello
)
[1] => Array
(
[Member.sospeso] => NO
)
But, if I write the joins with paginate , $this->paginate['conditions'][] will ignore all the stuff, and give me from debug, just $this->paginate['conditions'][]['Reservation.pagamento_verificato'] = 'SI';
Another bit of information.
If I put all the stuff dealing with $this->paginate['conditions'][]['Reservation.pagamento_verificato'] = 'SI';
before the $this->paginate JOIN, nothing will be in $this->paginate['conditions'][].
This is an old question, so I'll just review how to do a JOIN in a paginate for others who got here from Google like I did. Here's the sample code from the Widget's Controller, joining a Widget.user_id FK to a User.id column, only showing the current user (in conditions):
// Limit widgets shown to only those owned by the user.
$this->paginate = array(
'conditions' => array('User.id' => $this->Auth->user('id')),
'joins' => array(
array(
'alias' => 'User',
'table' => 'users',
'type' => 'INNER',
'conditions' => '`User`.`id` = `Widget`.`user_id`'
)
),
'limit' => 20,
'order' => array(
'created' => 'desc'
)
);
$this->set( 'widgets', $this->paginate( $this->Widget ) );
This makes a query similar to:
SELECT widgets.* FROM widgets
INNER JOIN users ON widgets.user_id = users.id
WHERE users.id = {current user id}
And still paginates.
I'm not sure if you need those [] - try just doing this:
$this->paginate['conditions']['Reservation.pagamento_verificato'] = 'SI';
I use the conditions when I call paginate method.
$this->paginate($conditions)
This works ok for me, I hope it works for you!
If you have setted previous params, you may use:
$this->paginate(null,$conditions)
This might be help full to someone....
This is how I did complicated joins with pagination in cakephp.
$parsedConditions['`Assessment`.`showme`'] = 1;
$parsedConditions['`Assessment`.`recruiter_id`'] = $user_id;
$this->paginate = array(
'conditions' => array($parsedConditions ),
'joins' => array(
array(
'alias' => 'UserTest',
'table' => 'user_tests',
'type' => 'LEFT',
'conditions' => '`UserTest`.`user_id` = `Assessment`.`testuser_id`'
),
array(
'alias' => 'RecruiterUser',
'table' => 'users',
'type' => 'LEFT',
'conditions' => '`Assessment`.`recruiter_id` = `RecruiterUser`.`id`'
)
,
array(
'alias' => 'OwnerUser',
'table' => 'users',
'type' => 'LEFT',
'conditions' => '`Assessment`.`owner_id` = `OwnerUser`.`id`'
)
),
'fields' => array('Assessment.id', 'Assessment.recruiter_id', 'Assessment.owner_id', 'Assessment.master_id', 'Assessment.title', 'Assessment.status', 'Assessment.setuptype','Assessment.linkkey', 'Assessment.review', 'Assessment.testuser_email', 'Assessment.counttype_2', 'Assessment.bookedtime', 'Assessment.textqstatus', 'Assessment.overallperc', 'UserTest.user_id', 'UserTest.fname', 'UserTest.lname', 'RecruiterUser.company_name', 'OwnerUser.company_name'),
'limit' => $limit,
'order'=> array('Assessment.endtime' => 'desc')
);