I'm trying to retrieve a specific users unread posts total from a phpbb3 database directly. I'm using ezsql to make my life easier, and I've written the following code based on the following post: https://www.phpbb.com/community/viewtopic.php?f=46&t=2107403#p12881167
$unreadposts always seems to return a number thats higher than the actual number of unread posts. I've been working on this the last 12 hours and its tearing my hair out :)
Any help or suggestions would be most appreciated!
// Step 1: get all topics the user has access to. Assuming all of them are unread until we prove otherwise
$usertopicsallowed = $forumdb->get_results("SELECT DISTINCT t.topic_id, t.forum_id, t.topic_last_post_time FROM $forumdbname.phpbb_users u
INNER JOIN $forumdbname.phpbb_user_group ug ON u.user_id = ug.user_id
INNER JOIN $forumdbname.phpbb_groups g ON g.group_id = ug.group_id
INNER JOIN $forumdbname.phpbb_acl_groups acl ON acl.group_id = ug.group_id
INNER JOIN $forumdbname.phpbb_forums f ON f.forum_id = acl.forum_id
INNER JOIN $forumdbname.phpbb_topics t ON f.forum_id = t.forum_id
WHERE u.user_id = " . $forumuserid . ";");
$usertopicsallowedcnt = count($usertopicsallowed);
// Step 2: Return any topics for this user in topics_track
$usertopicstrack = $forumdb->get_results("SELECT topic_id, mark_time FROM phpbb_topics_track WHERE user_id = " . $forumuserid . " ;");
if (!empty($usertopicsallowed))
{
foreach($usertopicsallowed as $key => $usertopicallowed)
{
if (!empty($usertopicstrack))
{
foreach($usertopicstrack as $key2 => $usertopictrack)
{
if ($usertopicsallowed[$key]->topic_id == $usertopicstrack[$key2]->topic_id)
{
if ($usertopicsallowed[$key]->topic_last_post_time < $usertopicstrack[$key2]->mark_time)
{
unset($usertopicsallowed[$key]);
}
}
}
}
}
}
$usertopicsallowed2 = array_values($usertopicsallowed);
$usertopicsallowed2cnt = count($usertopicsallowed2);
// Step 3a: eturn any topics for this user in forums_track
$userforumstrack = $forumdb->get_results("SELECT forum_id, mark_time FROM phpbb_forums_track WHERE user_id = " . $forumuserid . ";");
// Step 3b: remove all topics before the forum tracks lastmark time
if (!empty($usertopicsallowed2))
{
foreach($usertopicsallowed2 as $key => $usertopicsallow2)
{
if (!empty($userforumstrack))
{
foreach($userforumstrack as $key2 => $userforumtrack)
{
if ($usertopicsallowed2[$key]->forum_id == $userforumstrack[$key2]->forum_id)
{
if ($usertopicsallowed2[$key]->topic_last_post_time < $userforumstrack[$key2]->mark_time)
{
unset($usertopicsallowed2[$key]);
}
}
}
}
}
}
$usertopicsallowed3 = array_values($usertopicsallowed2);
$usertopicsallowed3cnt = count($usertopicsallowed3);
// Step 4: remove all topics before the user's lastmark time
if (!empty($usertopicsallowed3))
{
foreach($usertopicsallowed3 as $key => $usertopicsallow3)
{
if ($usertopicsallowed3[$key]->topic_last_post_time < $forumuserlastmark)
{
unset($usertopicsallowed3[$key]);
}
}
}
$usertopicsallowed4 = array_values($usertopicsallowed3);
$usertopicsallowed4cnt = count($usertopicsallowed4);
$unreadposts = count($usertopicsallowed4);
OK so as I said in my comment above, after 12 hours failing to get this to work I found an answer within 15 minutes of posting here! Noting this reply so maybe it will help someone else in the future, and I'll also reply on the link as well.
This link gives the answer (https://www.phpbb.com/community/viewtopic.php?f=46&t=2092813#p12800435) but doesn't allow for permissions, so you need to add the code I wrote below in addition. The full code is therefore:
// Step 1: get all topics the user has access to. Assuing all of them are unread until we prove otherwise
$usertopicsallowed = $forumdb->get_results("SELECT DISTINCT t.topic_id, t.forum_id, t.topic_last_post_time FROM $forumdbname.phpbb_users u
INNER JOIN $forumdbname.phpbb_user_group ug ON u.user_id = ug.user_id
INNER JOIN $forumdbname.phpbb_groups g ON g.group_id = ug.group_id
INNER JOIN $forumdbname.phpbb_acl_groups acl ON acl.group_id = ug.group_id
INNER JOIN $forumdbname.phpbb_forums f ON f.forum_id = acl.forum_id
INNER JOIN $forumdbname.phpbb_topics t ON f.forum_id = t.forum_id
WHERE u.user_id = " . $forumuserid . ";");
// Step 2: Calculate unread posts in all forums (regardless of permissions)
$phpbbv2 = $forumdb->get_results("SELECT t.topic_id, t.topic_last_post_time, tt.mark_time as topic_mark_time, ft.mark_time as forum_mark_time FROM (phpbb_topics t)
LEFT JOIN phpbb_topics_track tt ON (tt.user_id = " . $forumuserid . " AND t.topic_id = tt.topic_id)
LEFT JOIN phpbb_forums_track ft ON (ft.user_id = " . $forumuserid . " AND t.forum_id = ft.forum_id)
WHERE ( (tt.mark_time IS NOT NULL AND t.topic_last_post_time > tt.mark_time)
OR (tt.mark_time IS NULL AND ft.mark_time IS NOT NULL AND t.topic_last_post_time > ft.mark_time)
OR (tt.mark_time IS NULL AND ft.mark_time IS NULL AND t.topic_last_post_time > " . $forumuserlastmark . ") ) AND t.topic_moved_id = 0 AND t.topic_approved = 1 ORDER BY t.topic_last_post_time DESC;");
// Step 3: Loop through step 2 and only increment the counter for every topic the user has permission to view
$unreadcounter=0;
if (!empty($phpbbv2))
{
foreach($phpbbv2 as $key => $phpbbv2a)
{
if (!empty($usertopicsallowed))
{
foreach($usertopicsallowed as $key2 => $usertopicallowed)
{
if ($phpbbv2[$key]->topic_id == $usertopicsallowed[$key2]->topic_id)
{
$unreadcounter++;
}
}
}
}
}
$unreadpost=$unreadcounter;
I'm sure it could be better, I'm not the most experienced in php but its working for me.
Related
I'm trying to get the results from 'comments' table with the exception of the ones in the 'readsbaby' table.
The result i'm getting is from comments but no effect of the NOT EXISTS statement, so the result is all the comments.
Both tables have common data that should not be included in the result.
I checked the data and the syntax many times.
Still this query will return all comments without taking in consideration the AND NOT EXISTS close.
public function get_user_comments($post_id)
{
$user_id = $this->session->userdata('id');
$group_id = $this->session->userdata('group_id');
$sql = "SELECT *
FROM comments
WHERE DATE(created_on) > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
AND comments.group_id = " . $group_id . "
AND comments.user_id != " . $user_id . "
AND NOT EXISTS ( SELECT *
FROM readsbaby
WHERE comments.id = readsbaby.notification_id
AND comments.group_id = readsbaby.group_id
AND readsbaby.user_id = " . $this->session->userdata('id') . "
AND comments.nature1 = readsbaby.notification_type
) ";
return $data=$this->db->query($sql)->result_array();
}
Expecting to get the result filtered by the NOT EXISTS close.
I'd change the query this way, to have only the rows without any match in the table readsbaby:
public function get_user_comments($post_id)
{
$user_id = $this->session->userdata('id');
$group_id = $this->session->userdata('group_id');
$sql = "SELECT *
FROM comments
LEFT OUTER JOIN readsbaby ON comments.id = readsbaby.notification_id
AND comments.group_id = readsbaby.group_id
AND readsbaby.user_id = " . $this->session->userdata('id') . "
AND comments.nature1 = readsbaby.notification_type
WHERE DATE(created_on) > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
AND comments.group_id = " . $group_id . "
AND comments.user_id != " . $user_id . "
AND ISNULL(readsbaby.notification_id )";
return $data=$this->db->query($sql)->result_array();
}
This is easier to accomplish using the correct mysql JOIN.
From your post, I have gathered that you want to retrieve all the values from comments where they don't exist in readsbaby. The id column in comments associates with the notification_id in readsbaby for any 'matching' (duplicate) entries.
On this assumption, you can do this at a simplistic level like so
SELECT c.* FROM comments c
LEFT JOIN readsbaby r ON c.id = r.id WHERE r.notification_id IS NULL;
...which should translate to your context as something like:
SELECT c.* FROM comments c
LEFT JOIN readsbaby r ON c.id = r.notification_id
WHERE
r.notification_id IS NULL
AND DATE(c.created_on) > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
AND c.group_id = " . $group_id . "
AND c.user_id != " . $user_id . "
If, I have this wrong and you're looking to only get the data that isn't present in both, you'd need to use something like:
SELECT c.* FROM comments c
LEFT JOIN readsbaby r ON c.id = r.notification_id
WHERE
r.notification_id IS NULL
AND DATE(c.created_on) > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
AND c.group_id = " . $group_id . "
AND c.user_id != " . $user_id . "
UNION
SELECT r.* FROM readsbaby r
LEFT JOIN comments c ON c.id = r.notification_id
WHERE
c.id IS NULL
AND r.group_id = " . $group_id . "
AND r.user_id != " . $user_id . "
This may need tweaking to suit, as there's very little to explain the data and table relationship.
Note: Do not inject SQL as you've done. Consider parameterising your query with PDO or any other prepared statements (e.g. http://php.net/manual/en/pdo.prepare.php)
I am currently refactoring a legacy application and converting piece by piece over to zend framework 1.12.
I am scratching my head as to how to convert this over to zend db, is there a way that this can be done in one query?
Right now I see that it fetches a list of folders first and then runs an additional query per folder...
Running this as one query will improve performance, right?
$folders_query = DB::Query("select * from contacts_folders order by sort_order, name");
while($folders = DB::FetchArray($folders_query)){
$counts_total = DB::QueryOne("SELECT count(cm.messages_id) AS total
FROM contacts_basics cb, contacts_messages cm
WHERE cb.contacts_id = cm.contacts_id
AND cm.folders_id = '" . $folders['folders_id'] . "'
AND cm.status = '1'
AND cm.mark = '0'");
if ($counts_total >0){
$folders_name = '<strong>' . $folders['name'] . ' (' . $counts_total . ')</strong>';
} else {
$folders_name = $folders['name'];
}
echo '<li><a href="messages.php?fID=' . $folders['folders_id'] . '">';
echo $folders_name;
echo '</a></li>';
}
Yes, you can do both in the same query
SELECT cf.*, count(cm.messages_id) AS total
FROM contacts_folders cf left outer join
contacts_messages cm
on cf.id = cm.folders_id and
cm.status = '1' AND cm.mark = '0' left outer join
contacts_basics cb
on cb.contacts_id = cm.contacts_id
group by cf.folders_id
order by cf.sort_order, cf.name;
This uses a left outer join to be sure that you get all folders, even if there are no messages (which is how the original code works). Because of the left outer join, the conditions need to be moved into on clauses.
It also fetches all the information from the folders as well as the total. If there are no messages, then it should return 0 for that folder.
There was a minor bug in Gordon's answer but I figured it out thanks to him.
I changed
contacts_basics cb left outer join
To:
left outer join contacts_basics cb
The following code works as expected:
public function getMenuCounts(){
$raw = "SELECT cf.*, count(cm.messages_id) AS total
FROM contacts_folders cf left outer join
contacts_messages cm
on cf.folders_id = cm.folders_id and
cm.status = '1' AND cm.mark = '0'
left outer join contacts_basics cb
on cb.contacts_id = cm.contacts_id
group by cf.folders_id
order by cf.sort_order, cf.name;";
$db = Zend_Db_Table::getDefaultAdapter();
$stmt = $db->query($raw);
return $stmt->fetchAll();
}
So I have 2 tables:
user_selection (id, user_id, fiche_id)
fiche (id, title, ...)
(fiche = sheet)
I have a research request which gives me some fiche ids
SELECT fiche.*, IF(fiche.fiche_type_id = 2,fiche.instance,fiche_type_texte.type_texte) AS type_texte, IF(fiche.fiche_type_id = 2,CONCAT(fiche.affaire," - ", fiche.titre),fiche.titre) AS titre, IF(fiche.fiche_type_id = 1,fiche.date_publication,fiche.date_texte) AS date_publication
FROM enviroveille_bascule.fiche
LEFT JOIN enviroveille_bascule.fiche_type_texte ON(fiche.fiche_type_texte_id = fiche_type_texte.id)
LEFT JOIN enviroveille_bascule.fiche_x_keyword ON(fiche_x_keyword.fiche_id = fiche.id)
LEFT JOIN enviroveille_bascule.fiche_x_theme ON(fiche_x_theme.fiche_id = fiche.id)
LEFT JOIN enviroveille_bascule.fiche_echeance ON(fiche_echeance.fiche_id = fiche.id)
LEFT JOIN enviroveille_bascule.fiche_x_activite ON(fiche_x_activite.fiche_id = fiche.id) LEFT JOIN enviroveille_bascule.fiche_x_nomenclature ON(fiche_x_nomenclature.fiche_id = fiche.id)
WHERE 1 = 1
AND fiche_echeance.fiche_echeance_type_id IN(2)
GROUP BY fiche.id
I'd like to know if all results from my research query is in the user selection.
So I tried :
// fiche_ids is an array of fiche.id resulting from research request
SELECT COUNT(*) AS nb
FROM " . $this->bdd . $this->table_user_selection . "
WHERE user_id = '" . $user->datas_user['id'] . "'
AND fiche_id IN ('" . implode("','", $fiche_ids) . "')
if($ds['nb'] == count($fiche_ids) && count($fiche_ids) > 0) {
return true;
} else {
return false;
}
That works well
Problem is that I have some research request giving 10K+ results.
And I have to do it several time on the same page, which makes server lags.
Is there a easy way to know if all my results are in my selection?
Note that selection may contain more fiche_id than the research result.
The best would be to be able do this in one SQL request.
I am doing a sql query but it is not returning any data from the tables. I am not sure why, I have changed from INNER to LEFT but no luck. I have revised the query several times but i cant find an issue. Any thoughts of why I am not getting anything displayed?
TABLE STRUCTURE
TABLE CREATE QUERY
PHP
$query = ("SELECT class.class_name, class.class_caption, class.class_credit_hours, class.class_description, faculty_fname, faculty_lname
FROM class
LEFT JOIN section
ON class.id = section.class_id
LEFT JOIN faculty
ON faculty.id = section.faculty_id OR faculty.id = office_hours.faculty_id
LEFT JOIN faculty_titles
ON faculty_titles.faculty_id = faculty.id
LEFT JOIN faculty_education
ON faculty_education.faculty_id = faculty.id
LEFT JOIN major_class_br
ON major_class_br.class_id = class.id
LEFT JOIN major_minor
ON major_class_br.major_minor_id = major_minor.id
LEFT JOIN sched_sect_br
ON sched_sect_br.section_id = section.id
LEFT JOIN schedule
ON schedule.id = sched_sect_br.schedule_id
LEFT JOIN semester
ON semester.id = schedule.semester_id
LEFT JOIN office_hours
ON schedule.id = office_hours.schedule_id AND faculty.id = office_hours.faculty_id
");
//execute query
$result = mysql_query($query);
if ($result){
$totalhours = 0;
while ($row = mysql_fetch_assoc( $result ))
{
print "<b>" . $row['class_name'] . "</b><br>";
print $row['class_caption'] . "<br>";
print $row ['class_credit_hours'] . "hrs. <br>";
print $row ['faculty_lname'] . "hrs. <br>";
print $row ['faculty_fname'] . "hrs. <br>";
print $row['class_description'] . "<br>";
print "------------------------------<br />";
$totalhours += $row['class_credit_hours'];
}
}
print "<p>Total hours for Major: " . $totalhours . ".</p>";
Desired display:
Computer Programming I
CP1000
4
James Doe
This course offers introduction to programming.
UPDATE: The issue was found here but I am not sure why
ON faculty.id = section.faculty_id OR faculty.id = office_hours.faculty_id
Copy the SQL, go to MySQL admin, execute the SQL there and see if it returns any results.
Remove all the JOIN and execute again, you should see at least something
Add the JOIN one by one
Try to identify which JOIN resulted in no rows being returned
Check the left and right hand side of that join and ensure there actually are valid data to join
Alternatively use OUTER JOIN
Well I have gotten this query:
$characterinfoquery = "
SELECT
c.Name,
c.Level,
c.Sex,
c.Playtime,
c.KillCount,
c.DeathCount,
cl.Name AS clanName
FROM
Character AS c,
Account AS a,
ClanMember AS cm,
Clan AS cl
WHERE
c.AccountID = a.AccountID AND
c.CharacterID = cm.CharacterID AND
cm.ClanID = cl.ClanID AND
a.UserID='".mssql_real_escape_string($_SESSION['username'])."'
";
But I want the members who do not have a clan to be showed too but instead of the clan name it would say "-" at where the clan name is supposed to be.
This is my while statement:
if(mssql_num_rows($characterinforesult) != 0){
$content = str_replace("%content%", file_get_contents("tpl/contents/characterinfo.html"), $content);
//Get character information
$search = array("%Name%", "%Level%", "%Sex%", "%Playtime%", "%KillDeath%", "%Clan%");
$rows = file_get_contents("tpl/contents/characterinfo_tr.html");
while($row = mssql_fetch_assoc($characterinforesult)){
if($row['KillCount'] != 0){
$KillDeath = round($row['KillCount']/$row['DeathCount'], 2);
}
else{
$KillDeath = "-";
}
$Playtime = $row['Playtime']/60;
$replace = array($row['Name'], $row['Level'], gender($row['Sex']), round($Playtime), $KillDeath, $row['clanName']);
$tr .= str_replace($search, $replace, $rows);
}
}
Could someone help me with this?
Output with innerjoins:
Name Level Sex Playtime K/D Ratio Clan
DragonDex 97 Male 375 min 0.22 Test
It shows 1 row while there are 2 characters in that account, 1 has a clan the other doesn't.
Do you need a left outer join:
SELECT
c.Name,
c.Level,
c.Sex,
c.Playtime,
c.KillCount,
c.DeathCount,
coalesce( cl.Name, ' - ' ) AS clanName
FROM
Character AS c
inner join
Account AS a
on c.AccountID = a.AccountID
left outer join
ClanMember AS cm
on c.CharacterID = cm.CharacterID
left outer join
Clan AS cl
on cm.ClanID = cl.ClanID
WHERE
a.UserID='".mssq ...