Cakephp using group by in find method - php

I'm trying to use Group by in find method for my relational database I want to get the latest record with unique receiver_id out of result set filtered by user_id and below is my code:
$this->loadModel('Chat');
$lastChat = $this->Chat->find('all', array(
'conditions' => array(
'Chat.user_id' => $user_id['User']['id']
),
'fields' => array(
'Chat.id',
'Chat.chat',
'Chat.user_id',
'Chat.receiver_id',
'Chat.read',
'Chat.created'
),
'group' => array('Chat.receiver_id'),
'order' => array('Chat.created DESC')
));
However, this does not seem to work. I'm not sure why but I'm only getting one result...
How can I get multiple results with rules based on above.

Try the following:
$db = $this->Chat->getDataSource();
$chats = $db->query("SELECT * , (Chat.user_id + Chat.receiver_id) AS dist
FROM (
SELECT *
FROM chats t
WHERE ( t.user_id =$id OR t.receiver_id =$id )
ORDER BY t.id DESC
) Chat
GROUP BY dist
ORDER BY Chat.id DESC");

Related

data escaping remove for specific filed in cakephp

I am using subquery for id field.
$db = $this->AccountRequest->getDataSource();
$subQuery = $db->buildStatement(
array(
'fields' => array('MAX(id)'),
'table' => $db->fullTableName($this->AccountRequest),
'alias' => 'MaxRecord',
'limit' => null,
'offset' => null,
'order' => null,
'group' => array("user_id")
),
$this->AccountRequest
);
$searching_parameters = array(
#"AccountRequest.id IN " => "(SELECT MAX( id ) FROM `account_requests` GROUP BY user_id)"
"AccountRequest.id IN " => "(".$subQuery.")"
);
$this->Paginator->settings = array(
#'fields' => array('AccountRequest.*'),
'conditions' => $searching_parameters,
'limit' => $limit,
'page' => $page_number,
#'group' => array("AccountRequest.user_id"),
'order' => array(
'AccountRequest.id' => 'DESC'
)
);
$data = $this->Paginator->paginate('AccountRequest');
This structure is producing a query is:
SELECT
`AccountRequest`.`id`,
`AccountRequest`.`user_id`,
`AccountRequest`.`email`,
`AccountRequest`.`emailchange`,
`AccountRequest`.`email_previously_changed`,
`AccountRequest`.`first_name`,
`AccountRequest`.`first_namechange`,
`AccountRequest`.`f_name_previously_changed`,
`AccountRequest`.`last_name`,
`AccountRequest`.`last_namechange`,
`AccountRequest`.`l_name_previously_changed`,
`AccountRequest`.`reason`,
`AccountRequest`.`status`,
`AccountRequest`.`created`,
`AccountRequest`.`modified`
FROM
`syonserv_meetauto`.`account_requests` AS `AccountRequest`
WHERE
`AccountRequest`.`id` IN '(SELECT MAX(id) FROM `syonserv_meetauto`.`account_requests` AS `MaxRecord` WHERE 1 = 1 GROUP BY user_id)'
ORDER BY
`AccountRequest`.`id` DESC
LIMIT 25
In the subquery, its add an extra single quote so it's producing an error.
So, How can I remove these single quotes from this subquery?
Thanks
What are you trying to achieve with the sub query?
The MAX(id) just means it will pull the id with the largest value AKA the most recent insert. The sub query is completely redundant when you can just ORDER BY id DESC.
using MAX() will return only one record, if this is what you want to achieve you can replicate by adding LIMIT 1
If the sub query is just an example and is meant to be from another table I would just run the query that gets the most recent id before running the main query. Getting the last inserted id in a separate query is very quick and I cant see much of a performance loss. I think it will result in cleaner code that`s easier to follow to.
edit 1: From the comments it sounds like all your trying to get is a particular users latest account_requests.
You dont need the sub query at all. My query below will get the most recent account record for the user id you choose.
$this->Paginator->settings = array(
'fields' => array('AccountRequest.*'),
'conditions' => array(
'AccountRequest.user_id' => $userID // you need to set the $userID
)
'page' => $page_number,
'order' => array(
'AccountRequest.id DESC' //shows most recent first
),
'limit' => 1 // set however many you want the maximum to be
);
The other thing you cold be meaning is to get multiple entries from multiple users and display them in order of user first and then the order of recent to old for that user. MYSQL lets you order by more than one field, in that case try:
$this->Paginator->settings = array(
'conditions' => array(
'AccountRequest.user_id' => $userID // you need to set the $userID
)
'page' => $page_number,
'order' => array(
'AccountRequest.user_id', //order by the users first
'AccountRequest.id DESC' //then order there requests by recent to old
)
);
If the example data you have added into the question is irrelevant and you are only concerned about how to do nested subqueries it has already been answered here
CakePHP nesting two select queries
However I still think based on the data in the question you can avoid using a nested query.

CakePHP Translate: Getting group by latest date

How do I translate below to cakePHP code? The code below comes from a solution here MySQL order by before group by.
Getting the author's latest post using group by.
SELECT p1.*
FROM wp_posts p1
INNER JOIN
(
SELECT max(post_date) MaxPostDate, post_author
FROM wp_posts
WHERE post_status='publish'
AND post_type='post'
GROUP BY post_author
) p2
ON p1.post_author = p2.post_author
AND p1.post_date = p2.MaxPostDate
WHERE p1.post_status='publish'
AND p1.post_type='post'
order by p1.post_date desc
Below is still similar situation:
SELECT t1.* FROM payment_status t1
JOIN (SELECT payment_id, MAX(created) max_created
FROM payment_status
GROUP BY payment_id
) t2
ON t1.payment_id = t2.payment_id AND t1.created = t2.max_created;
I need some cakePHP translation for either the two mySQL statements.
------------------------------------------------------------------------------------
I did something like the code below but it gives me error
Error: SQLSTATE[42000]: Syntax error or access violation: 1059 Identifier name 'SELECT max(dateEncoded) maxDate, findings FROM maintain GROUP BY computer_id Office.main_office LIKE' is too long
How do I fix it?
$this->Computer->unbindModel(array(
'belongsTo' => array('Office'),
'hasMany' => array('Brand','Maintain')
));
$model_view = $this->Computer->bindModel(array(
'hasOne' => array(
'Office' => array(
'foreignKey' => false,
'conditions' => array('Office.id = Computer.office_id')
),
'Maintain' => array(
'foreignKey' => false,
'conditions' => array('Computer.id = Maintain.computer_id'),
)
)
)
);
$main_office = trim($this->request->data['Office']['office_id']);
$joins = array(
array(
'table' => "SELECT max(dateEncoded) maxDate, findings FROM maintain GROUP BY computer_id",
'alias' => 'P2',
'type' => 'INNER',
'conditions' => array('Maintain.findings = p2.findings','Maintain.dateEncoded = p2.maxDate')
)
);
$conditions=array("Office.main_office LIKE"=>"%$main_office%");
$result = $this->Computer->find('all',array(
$model_view,
'joins'=>$joins,
'conditions'=>$conditions,
'order' => array('Office.description'),
'group' => 'Computer.id'
));
This can be written something like this :-
$joins = array(
array(
'table' => 'SELECT max(post_date) MaxPostDate, post_author FROM wp_posts WHERE post_status='publish' AND post_type='post'GROUP BY post_author',
'alias' => 'P2',
'type' => 'INNER',
'conditions' => array('WpPost.post_author = p2.post_author','WpPost.post_date = p2.MaxPostDate')
)
);
$conditions=array("WpPost.post_status='publish'","WpPost.post_type='post'");
$this->WpPost->find('all',array('fields'=>array('WpPost.*'),'joins'=>$joins,'conditions'=>$conditions);

CakePHP: adding DISTINCT to find causes an associated to get omitted

We build a fairly complex set of $conditions, then we search using Paginator:
$this->Paginator->settings = array( 'conditions'=>$conditions,
'joins'=>$joins,
'limit'=>25,
//'fields' => array('DISTINCT (User.id)')
);
$resume_display_array = $this->Paginator->paginate('User');
You'll see the 'fields' is commented out. When we uncomment that field, it causes associated model Resume to get dropped from the results array (without it, each result returned includes a [Resume] key.
Why is this happening?
Here's the SQL dump WITH the DISTINCT option in use:
SELECT DISTINCT (`User`.`id`), `User`.`id`
FROM `sw`.`users` AS `User`
LEFT JOIN `sw`.`taggings_users` AS `TaggingUser`
ON (`User`.`id`= `TaggingUser`.`user_id`)
LEFT JOIN `sw`.`taggings` AS `Tagging`
ON (`TaggingUser`.`tagging_id`= `Tagging`.`id`)
LEFT JOIN `sw`.`resumes` AS `Resume`
ON (`Resume`.`user_id` = `User`.`id`)
WHERE `User`.`first_name` LIKE '%jordan%' AND NOT (`Tagging`.`tag_name` = ('done'))
LIMIT 25
Here's the SQL dump WITHOUT the DISTINCT option:
SELECT `User`.`id`, `User`.`username`, `User`.`password`, `User`.`role`, `User`.`created`, `User`.`modified`, `User`.`email`, `User`.`wordpress_user_id`, `User`.`first_name`, `User`.`last_name`, `User`.`group_id`, `Resume`.`id`, `Resume`.`user_id`, `Resume`.`wordpress_resume_id`, `Resume`.`wordpress_user_id`, `Resume`.`has_file`, `Resume`.`is_stamped`, `Resume`.`is_active`, `Resume`.`file_extension`, `Resume`.`created`, `Resume`.`modified`, `Resume`.`is_deleted`
FROM `sw`.`users` AS `User`
LEFT JOIN `sw`.`taggings_users` AS `TaggingUser`
ON (`User`.`id`= `TaggingUser`.`user_id`)
LEFT JOIN `sw`.`taggings` AS `Tagging`
ON (`TaggingUser`.`tagging_id`= `Tagging`.`id`)
LEFT JOIN `sw`.`resumes` AS `Resume`
ON (`Resume`.`user_id` = `User`.`id`)
WHERE `User`.`first_name` LIKE '%jordan%' AND NOT (`Tagging`.`tag_name` = ('done'))
LIMIT 25
Update
Here is a var_dump of $joins:
array(
(int) 0 => array(
'table' => 'taggings_users',
'type' => 'LEFT',
'alias' => 'TaggingUser',
'conditions' => array(
(int) 0 => 'User.id= TaggingUser.user_id'
)
),
(int) 1 => array(
'table' => 'taggings',
'type' => 'LEFT',
'alias' => 'Tagging',
'conditions' => array(
(int) 0 => 'TaggingUser.tagging_id= Tagging.id'
)
)
)
If you specify the fields option, you need to specify all fields that you want to retrieve.
By setting it ONLY to User.id, you're telling it that that is the ONLY field you want returned.
You could also try something like below as a catchall (not sure if that works, but if not, you get the idea - you're limiting your own results):
'fields' => array('*', 'DISTINCT (User.id)')

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

Cakephp joining tables via find option not working

I'm trying to join a Users table to my curent hasMany Through table which has Interest and User model id's.
Below is the find query with options:
$params = array(
'fields' => array('*', 'COUNT(DISTINCT(InterestsUser.interest_id)) as interest_count'),
'limit' => 15,
'recursive' => -1,
'offset' => $offset,
'conditions' => array('InterestsUser.interest_id' => $conditions),
'group' => array('InterestsUser.user_id'),
'order' => array('interest_count DESC', 'InterestsUser.user_id ASC', 'InterestsUser.interest_id ASC'),
'joins' => array(
array('table' => 'users',
'alias' => 'User',
'type' => 'LEFT',
'conditions' => array(
'User.id' => 'InterestsUser.user_id',
)
)
)
);
$results = $this->InterestsUser->find('all', $params);
This returns InterestsUser table fine but does not return any values for Users table. It only returns field names.
What could be wrong?
UPDATE:
OK, above is generating below SQL which I got from Cake's datasources sql log:
SELECT *, COUNT(DISTINCT(InterestsUser.interest_id)) as interest_count
FROM `interests_users` AS `InterestsUser`
LEFT JOIN users AS `User` ON (`User`.`id` = 'InterestsUser.user_id')
WHERE `InterestsUser`.`interest_id` IN (3, 2, 1)
GROUP BY `InterestsUser`.`user_id`
ORDER BY `interest_count` DESC, `InterestsUser`.`user_id` ASC, `InterestsUser`.`interest_id` ASC
LIMIT 15
Why is users table values returning NULL only for all fields?
UPDATE:
OK I tried below but this is working fine...What am I missing here!!??
SELECT * , COUNT( DISTINCT (
interests_users.interest_id
) ) AS interest_count
FROM interests_users
LEFT JOIN users ON ( users.id = interests_users.user_id )
WHERE interests_users.interest_id
IN ( 1, 2, 3 )
GROUP BY interests_users.user_id
ORDER BY interest_count DESC
LIMIT 15
The array syntax for join conditions should be like the following
array('User.id = InterestsUser.user_id')
as opposed to array('User.id' => 'InterestsUser.user_id'). For more, see http://book.cakephp.org/view/1047/Joining-tables.

Categories