CakePHP query with AND OR - php

I have the following query which should be finding:
A row where the user_id matches userId and friend_id matches friendId
OR a row where the user_id matches friendId and friend_id matches userId
AND both also have status of 1
My code is:
public function isFriend($userId, $friendId)
{
return $search = $this->Friend->find('first', array(
'conditions'=>array(
'OR' => array(
'AND'=>array(
'Friend.user_id'=>$userId,
'Friend.friend_id'=>$friendId
),
'AND'=>array(
'Friend.user_id'=>$friendId,
'Friend.friend_id'=>$userId
)
),
'AND' => array(
'Friend.status'=>1
)
)
)
);
}
However it's not working... I've looked around and seems it's to do with getting the arrays correctly when dealing with the two AND calls, but I don't get it. Can anyone help me out?
Thanks

You can't use the same array key twice as mark said, so you have to encapsulate the ANDs in separate arrays
'OR' =>
array(
array('AND'=>array(
'Friend.user_id'=>$userId,
'Friend.friend_id'=>$friendId
)
),
array('AND'=>array(
'Friend.user_id'=>$friendId,
'Friend.friend_id'=>$userId
)
)
),
'AND' => array(
'Friend.status'=>1
)

Related

Sql query to cakephp query using associations

Here is my correct sql query:
SELECT * FROM `messages` WHERE ( (sender_id=3 AND user_id=40) OR (sender_id=40 AND user_id=3)) AND offer_id=1
I want to use this in Cakephp syntax:
$this->Message->find('all',array('conditions'=>array(
'AND'=>array(
'OR'=>array(
'Message.offer_id'=>$offer_id,
'Message.sender_id'=>$sender_id,
'Message.user_id'=>$this->Auth->user('id'),
),
'OR'=>array(
'Message.offer_id'=>$offer_id,
'Message.user_id'=>$sender_id,
'Message.sender_id'=>$this->Auth->user('id')
)
)
),
'recursive'=>2
));
Is there anyone who can help me to figure out the issue. Basically I want to get all the messages whether I sent or received for an particular offer.
You should move $offer_id out of or conditions and move it to and conditions.
Why? Lets look at your first or array:
That conditions will return true if:
sender_id is 3
OR user_id is 40
OR offer_id is 1
So, that condition may return true event if offer_id != 1
That should be written this way (as precisely as possible according to original query):
$query = $this
->Messages
->find('all' , [
'conditions' => [
'or' => [
[
'sender_id' => $sender_id,
'user_id' => $this->Auth->user('id')
], [
'sender_id' => $this->Auth->user('id'),
'user_id' => $sender_id
]
],
'offer_id' => $offer_id,
]
]);
dump($query);
In dump we can see something like this:
"SELECT * FROM messages Messages WHERE (((sender_id = :c0 AND user_id = :c1) OR (sender_id = :c2 AND user_id = :c3)) AND offer_id = :c4)
asterisk in sql query dump for more readability
You have the AND and OR operators reversed.
'OR'=>array(
'AND'=>array(
'Message.offer_id'=>$offer_id,
'Message.sender_id'=>$sender_id,
'Message.user_id'=>$this->Auth->user('id'),
),
'AND'=>array(
'Message.offer_id'=>$offer_id,
'Message.user_id'=>$sender_id,
'Message.sender_id'=>$this->Auth->user('id')
)
)

how to use IN query in cakephp to find multiple values

I'm using LIKE query but when specialist_id's order change it say no records found,
if specialist_id array have [1,2,3] in it then all the users with three specialties should be in results regardless of sequence, sequence array may have [3,2,1] or [2,1,3] but the users with these three specialties should be in results, here is my code with LIKE query:
$field = $this->User->find(
'all', array(
'conditions'=>array(
'User.specialist_id LIKE' =>'%'.$value.'%',
'User.role'=>'careproviderRole',
'User.gender'=>$this->request->data['gender'])
)
);
foreach ($field as $key => $value)
{
$field[$key]['Specialist'] = $user;
}
I have also tried with FIND_IN_SET:
$field = $this->User->find(
'all', array(
'conditions' => array(
'User.role' => 'careproviderRole',
'FIND_IN_SET(\''.$this->request->data['specialist_id'].'\', User.specialist_id)')
)
);
When you use LIKE with mask '%some%', it find all results with substring 'some'.
If you need to get some specific set of ids from table (1, 2 or 3), then use sql operator IN.
Example:
SELECT `specialist_id` FROM `User` WHERE `specialist_id` IN (1,2,3);
In your code it will be:
$field = $this->User->find(
'all', array(
'conditions'=>array(
'User.specialist_id' => $arrayOfSpecialistIds,
'User.role'=>'careproviderRole',
'User.gender'=>$this->request->data['gender'])
)

CakePHP not joining the correct tables in find query

OK, another cake question from me.
I have rather a complex table structure with quite a few joins. Here's the jist of it:
So, in summary:
RiskCategory $hasMany Client [Client $belongsTo RiskCategory]
Client $hasMany Account [Account $belongsTo Client]
Account $hasMany Holding [Holding $belongsTo Account]
In my RiskCategory model I want to run a query that basically returns the sum of the holdings in each riskCategory id, grouped by date. There's a few other conditions but here is what I've put together:
$findParams = array(
'recursive' => 4,
'fields' => array(
'Holding.holding_date',
'SUM(Holding.value) AS risk_category_value'
),
'group' => array('Holding.holding_date'),
'order' => 'Holding.holding_date ASC'
);
$findParams['conditions'] = array(
'Client.active' => true,
'Client.model' => true,
'Client.currency' => 'GBP',
'OR' => array(
'Holding.holding_date' => $this->end_date,
array(
'Holding.holding_date = LAST_DAY(Holding.holding_date)',
'MONTH(Holding.holding_date)' => array(3,6,9,12)
)
)
);
$valuations = $this->Client->Account->Holding->find( 'all', $findParams );
When I run the above cake is giving me an error saying various fields are not present in the query. The raw query created is as follows:
SELECT `Holding`.`holding_date`,
Sum(`Holding`.`value`) AS risk_category_value
FROM `ips_client_db`.`holdings` AS `Holding`
LEFT JOIN `ips_client_db`.`accounts` AS `Account`
ON ( `Holding`.`account_id` = `Account`.`id` )
LEFT JOIN `ips_client_db`.`sedols` AS `Sedol`
ON ( `Holding`.`sedols_id` = `Sedol`.`id` )
WHERE `client`.`active` = '1'
AND `client`.`model` = '1'
AND `client`.`currency` = 'GBP'
AND ( ( `Holding`.`holding_date` = '2013-10-01' )
OR (( ( `Holding`.`holding_date` = Last_day(
`Holding`.`holding_date`) )
AND ( Month(`Holding`.`holding_date`) IN ( 3, 6, 9, 12 ) ) )
) )
GROUP BY `Holding`.`holding_date`
ORDER BY `Holding`.`holding_date` ASC
It looks as though cake is not doing all the joins. It is only joining Account to Holding and then Holding to Sedol (which is another joined table in the database but is not needed for this query so I've omitted it from the diagram)
Why are the joins not being made properly and how to acheive this? I'd like to avoid writing a raw statement if possible.
EDIT: The joins should be as follows:
...
FROM risk_categories
LEFT JOIN ((clients
LEFT JOIN accounts
ON clients.id = accounts.client_id)
LEFT JOIN holdings
ON accounts.id = holdings.account_id)
ON risk_categories.id = clients.risk_category_id
...
CakePHP does not perform deep joins for associations that are more than 1 level deep. That is why you're getting an error for references to the Client table in the conditions.
For these types of problems it's easier to use the Containable behavior. I usually add this behavior to my AppModel as the default handler for associations.
Containable allows you to define the associations (only those already defined) with their fields and conditions as part of the find operation. This is done by adding a new contain key in the find parameters. Below I've taking a guess at what you might need.
$findParams = array(
'fields' => array(
'Holding.holding_date',
'SUM(Holding.value) AS risk_category_value'
),
'conditions'=>array(
'OR' => array(
'Holding.holding_date' => $this->end_date,
array(
'Holding.holding_date = LAST_DAY(Holding.holding_date)',
'MONTH(Holding.holding_date)' => array(3,6,9,12)
)
)
),
'group' => array('Holding.holding_date'),
'order' => 'Holding.holding_date ASC',
'contain'=>array(
'Account'=>array(
'Client'=>array(
'RiskCategory',
'conditions'=>array(
'Client.active' => true,
'Client.model' => true,
'Client.currency' => 'GBP',
)
)
)
)
);
$valuations = $this->Client->Account->Holding->find( 'all', $findParams );

CakePHP query - complex AND/OR conditions

I'm trying to get my head around the complex find conditions of CakePHP and have read the docs but am struggling with this one query.
SELECT field1,
field2
WHERE id = 123456
AND ((holding_date = Last_day(holding_date)
AND Month(holding_date) IN(3, 6, 9, 12))
OR (holding_date = '2013-09-15'))
To produce the above conditions what would my conditions array look like?
CakePHP conditions and sql expressions
While the conditions in the question are not that complex, they touch on a few points which mean they can be tricky to define correctly. Some of the things to know when defining cakephp conditions:
Conditions are defined as an array of key => value pairs, as such the same key cannot be defined twice on the same level
an array element which has a numeric key is interpreted as an sql expression
The default join mode is "AND" - it's not necessary to specify "AND" => ... in conditions
An OR conditions must have more than one elements. There's no error if it has only one but otherwise: OR what?
Bearing in mind the above notes, the conditions in the question can be expressed as:
$foo->find('all', array(
'fields' => array(
'field1',
'field2'
),
'conditions' => array(
'id' => 123456,
'OR' => array(
array(
'holding_date = LAST_DAY(holding_date)',
'MONTH(holding_date)' => array(3,6,9,12)
),
'holding_date' => '2013-09-15'
)
)
));
Which results in:
WHERE
`id` = 123456
AND
(
(
(holding_date = LAST_DAY(holding_date))
AND
(MONTH(holding_date) IN (3, 6, 9, 12)))
)
OR
(`holding_date` = '2013-09-15')
)
Note: whitespace is quite important =) I misread the question originally solely because of the inconsistent whitespace in the question's sql.
OK I have solved it:
$findParams['conditions'] = array(
'Account.client_id' => '12345',
'AND' => array(
'OR' => array(
'Holding.holding_date' => '2013-09-15',
'AND' => array(
'Holding.holding_date = LAST_DAY(Holding.holding_date)',
'MONTH(Holding.holding_date)' => array(3,6,9,12)
)
)
)
);
Try this:
$params['conditions'] = array(
'`id`' => 123456,
'AND' => array(
'`holding_date`' => 'LAST_DAY(`holding_date`)',
'AND' => array(
'MONTH(holding_date)' => array(3, 6, 9, 12),
'OR' => array(`holding_date` => '2013-09-15')
)
)
);

Zend subquery arguments

The query below represents what I am trying to do, I need to pull in a list of blog_posts and also join with a users table.
What it is also doing is pulling in a random 'picture_filename' from blog_updates_pictures. It needs blog_updates as a join to reference the blog_update_id.
What I'd like to do now is also COUNT the number of blog_updates for each blog_post. I think this is a subquery but every implementation fails. It would also be good to have the count accept arguments (ie. blog_updates where date = ?). Also, there may be no updates or pictures to a blog_post.
$select = $db->select ();
$select->from ( array ('b' => 'blog_posts' ), array('headline', 'date_created'));
$select->join ( array ('u' => 'users' ), 'u.user_id = b.user_id', array ( 'email' ) );
$select->joinLeft ( array ('bu' => 'blog_updates' ), 'bu.blog_id = b.blog_id', array () );
$select->joinLeft ( array ('bup' => 'blog_updates_pictures' ), 'bu.blog_update_id = bup.blog_update_id', array ('picture_filename' ) );
Can someone show me the way?
Thanks
What I'd like to do now is also COUNT the number of blog_updates for each blog_post.
You can achieve that using aggregation - use GROUP BY bu.blog_id, and as additional column COUNT(bu.blog_id) AS blog_updates_count. It should work.
Create subselects as:
$subselect = $db->select()
->from(
array ('bu' => 'blog_updates' ),
array(
'blog_id',
'updates' => 'count(*)'
)
)
->group("bu.blog_id");
And then join the $subselect with your main $select as:
$select->join(
array( 't' => $subselect),
"t.blog_id = b.blog_id",
array( 'updates' )
);
If we had the table structure you might get a more complete answer

Categories