CakePHP Translate: Getting group by latest date - php

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);

Related

COUNT function in sql counting whole data in database

I am trying to count the number of data in the database according to the category id and I have written the following lines of code:
public function getPodcastByCategoryId($catId){
$args = array(
'fields' => array(
'podcast.id',
'podcast.title',
'podcast.description',
'podcast.duration',
'podcast.audio',
'podcast.image',
'podcast.category',
'podcast.added_date',
'categories.title AS category_title',
'(SELECT users.full_name FROM users WHERE id = podcast.added_by) as author',
'(SELECT COUNT(category) FROM podcast WHERE category = podcast.category) as episodes'
),
'join' => "LEFT JOIN categories on podcast.category = categories.id",
'where' => array(
'category' => $catId
),
);
return $this->select($args);
}
However, the function getPodcastByCategoryId($catId) is giving episodes = 3 which is the count of data present in table podcast.
Data in database:
My expected episodes would be 2 in category id 2 and 1 in category id 1.
for episode you should use COUNT(*)
$args = array(
'fields' => array(
'podcast.id',
'podcast.title',
'podcast.description',
'podcast.duration',
'podcast.audio',
'podcast.image',
'podcast.category',
'podcast.added_date',
'categories.title AS category_title',
'(SELECT users.full_name FROM users WHERE id = podcast.added_by) as author',
'(SELECT COUNT(*) FROM podcast WHERE categories.category = podcast.category) as episodes'
),
'join' => "LEFT JOIN categories on podcast.category = categories.id",
'where' => array(
'category' => $catId
),
);

CakePHP Retrieving data from multiple tables with select conditions

I am building an application using CakePHP and I am stuck on a problem retrieving data using a series of joins. In the simplified example below the join with the alias Delivery could have more than record and I want to bring back the record with a max value in a particular field in that table.
$inv_conditions = array( 'Invoice.invoice_date >=' => $DateFormatter->dateForDB($dateFrom),
'Invoice.invoice_date <=' => $DateFormatter->dateForDB($dateTo),
'Invoice.id >' => 385380 );
$join = array(array(
'table' => 'jobs',
'alias' => 'Jobs',
'type' => 'LEFT',
'conditions' => array('Invoice.job_id = Jobs.JOB_ID' )
),
array(
'table' => 'functional',
'alias' => 'Delivery',
'type' => 'LEFT'
'conditions'=> array('AND ' => array('Invoice.job_id = Delivery.JOB',
'Delivery.TYPE_STAGE = 1')
)
)
);
$invoices = $this->Invoice->find("all", array(
"fields" => array(
'Invoice.id',
'Invoice.job_id',
'Invoice.invoice_no',
'Invoice.consolidated_type',
'Invoice.customer_id_tbc',
'Invoice.invoice_date',
'Invoice.invoice_reference',
'Invoice.invoice_req',
'Jobs.PAYMENT_TYPE',
'Jobs.CUSTOMER',
'Jobs.MOST_RELEVANT_LINE',
'Delivery.DEPARTURE_DATE',
'Delivery.CNOR_CNEE_NAME',
'Delivery.TOWN_NAME',
),
"conditions" => $inv_conditions,
"joins" => $join
)
);
}
I can do this with SQL no problem as follows:
SELECT
jobs.JOB_ID,
jobs.CUSTOMER,
functional.JOB_LINE_ORDER,
functional.CNOR_CNEE_NAME,
functional.TOWN_NAME
FROM jobs JOIN functional ON
jobs.JOB_ID = 'M201409180267'
AND
functional.JOB = jobs.JOB_ID
AND
functional.TYPE_STAGE = 0
AND
functional.JOB_LINE_ORDER =
(SELECT MAX(JOB_LINE_ORDER) FROM functional
WHERE functional.JOB = 'M201409180267' AND functional.TYPE_STAGE = 0)
I have tried using the following to the conditions array:
'conditions' => array('AND ' =>
array( 'Invoice.job_id = Delivery.JOB',
'Delivery.TYPE_STAGE = 1'
'Delivery.JOB_LINE_ORDER = MAXIMUM(Delivery.JOB_LINE_ORDER)' )
)
This does bring back results but not the correct ones and the resulting SQL generated by Cake does have a select in the where clause. Is there a way of doing this when retrieving data in cake where the sql statement created will have a select in the where clause.
Any suggestions would be greatly appreciated.
Thanks
Bas
You need to use subquery to generate the select in the where clause. You can create a method in your model that does this and call it from your controller.
$subQuery = $db->buildStatement(
array(
'fields' => array(''),
'table' => $db->fullTableName($this),
'alias' => 'aliasName',
'limit' => null,
'offset' => null,
'joins' => array(),
'conditions' => $conditionsArray,
'order' => null,
'group' => null
),
$this
);
$subQuery = ' <<YOUR MAIN QUERY CONDITION (' . $subQuery . ') >>';
$subQueryExpression = $db->expression($subQuery);
$conditions[] = $subQueryExpression;
return $this->Model->find('list', compact('conditions'));

CakePHP pagination - order by virtual field

Contributors have songs and songs have contributors. I want to be able to sort by the number of songs that a contributor has.
In my Controller:
public $paginate = array(
'fields' => array(
'Contributor.id',
'Contributor.name',
'COUNT(DISTINCT ContributorsSong.song_id) AS Contributor__TotalSongs',
),
'joins' => array(
array(
'alias' => 'ContributorsSong',
'table' => 'contributors_songs',
'type' => 'LEFT',
'conditions' => 'ContributorsSong.contributor_id = Contributor.id'
),
array(
'alias' => 'Song',
'table' => 'songs',
'type' => 'LEFT',
'conditions' => 'ContributorsSong.song_id = Song.id'
)
),
'group' => array('ContributorsSong.contributor_id')
);
And in my index method.
$this->Contributor->recursive = 0;
$this->Paginator->settings = $this->paginate;
$this->Contributor->virtualFields['TotalSongs'] = 0;
$items = $this->paginate();
echo '<pre>';print_r($items);echo '</pre>';
I'm trying to sort by the number of songs by using a virtual field, so when I go to
localhost/site/contributors/index/sort:TotalSongs/
I get this error:
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column '0' in 'order clause'
SQL Query:
SELECT Contributor.id,
Contributor.name,
COUNT(DISTINCT ContributorsSong.song_id) AS Contributor__TotalSongs
FROM db_songs2.contributors AS Contributor LEFT JOIN
db_songs2.contributors_songs AS ContributorsSong ON
(ContributorsSong.contributor_id = Contributor.id) LEFT JOIN
db_songs2.songs AS Song ON (ContributorsSong.song_id =
Song.id) WHERE 1 = 1 GROUP BY ContributorsSong.contributor_id
ORDER BY (0) desc LIMIT 18
I thought that TotalSongs would get turned into Contributor__TotalSongs in the query but it gets turned into 0. What is going on here? Thanks.
I replaced:
$this->Contributor->virtualFields['TotalSongs'] = 0;
with:
$this->Contributor->virtualFields['TotalSongs'] = 'COUNT(DISTINCT ContributorsSong.song_id)';
And deleted:
'COUNT(DISTINCT ContributorsSong.song_id) AS Contributor__TotalSongs',
And it works for ordering now, but I can't use it in the conditions. I think this is part of the Limitations of virtualFields? I'm trying to select only tables where TotalSongs > 0, but I think I can get this another way, by changing the join from LEFT to INNER.

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)')

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