Query works on localhost, but not in production server - php

My English is not so good, but I'll try to explain the problem.
I have three tables: One called Accounts, one called Accounts_Profiles and another called Accounts_Messages. My goal is to show the last users that the logged in user ($login_user_id) sent a message to.
My tables basically consists of this:
Accounts
id, name, email, nivel, verification
Accounts_Profiles
id, user_id, status
Accounts_Messagens
id, sender (int), receiver (int)
My query is currently like this:
SELECT DISTINCT Accounts.id, Accounts.email, Accounts.name, Accounts.username, Accounts.avatar, Accounts.nivel, Accounts.verification FROM Accounts JOIN Accounts_Messages ON Accounts.id = Accounts_Messages.receiver ORDER BY Accounts_Messages.id DESC;
In CodeIgniter/PHP, it looks like this:
/*
With the $this->db->distinct() I make sure that the users shown will not be repeated - It works!
*/
$this->db->distinct();
$this->db->select(
array(
"Accounts.id id",
"Accounts.email email",
"Accounts.name name",
"Accounts.username username",
"Accounts.avatar avatar",
"Accounts.nivel nivel",
"Accounts.verification verification",
"Accounts_Profiles.status online",
"(SELECT IF(COUNT(ACM.id) > 0, COUNT(ACM.id), null) FROM Accounts_Messages ACM WHERE ACM.receiver = '$login_user_id' AND ACM.sender = 'Accounts.id' AND ACM.is_read = '0') unread"
)
);
/*
Here I exclude the $login_user_id from the list, the admin users and also those who have not verified the account - It works!
*/
$this->db->join(
"Accounts_Profiles",
"Accounts_Profiles.user_id = Accounts.id",
"left"
);
$this->db->where(
array(
"Accounts.id !=" => $login_user_id,
"Accounts.nivel !=" => 'administrator',
"Accounts.verification !=" => 'false'
)
);
/*
Here, I sort messages by last sent. I check who the logged in user sent the last messages to and list those users - This only works on localhost.
*/
$this->db->join(
"Accounts_Messages",
"Accounts.id = Accounts_Messages.receiver",
"left"
);
$this->db->order_by(
"Accounts_Messages.id", 'DESC'
);
/*
Returning the result
*/
return $this->db->limit($filters['limit'])->offset($filters['offset'])->get("Accounts")->result();
The problem:
This works on localhost, but it doesn't work in production. On localhost I use MySQL. On the production server I use MariaDB.
In theory it shouldn't make any difference, since the clauses I used are compatible in both cases.
Am I forgetting to do something or some configuration on the production server?
Thank you!
I tried to add this for conscience sake, but it didn't work:
$this->db->group_by( "Accounts.id" );
Edit
As you can see, in localhost the ordering of users is given by the last message sent to them. That's what I expect in production.
On the production server the ordering is happening by conversation creation date (I don't know how). It's wrong, as the first users I chat with always come last even if I send them a new message.

Solution
I managed to solve the problem by doing the following adjustment in the query:
SELECT Accounts.id, Accounts.email, Accounts.name, Accounts.username, Accounts.avatar, Accounts.nivel, Accounts.verification, Accounts_Messages.id
FROM Accounts
JOIN (
SELECT m_to, MAX(dt_updated) as max_updated
FROM Accounts_Messages
GROUP BY m_to
) last_message
ON Accounts.id = last_message.m_to
JOIN Accounts_Messages
ON Accounts.id = Accounts_Messages.m_to AND Accounts_Messages.dt_updated = last_message.max_updated
GROUP BY Accounts.id, Accounts_Messages.id
ORDER BY last_message.max_updated DESC;
It looks like this in CodeIgniter:
<?php
/*
With the $this->db->distinct() I make sure that the users shown will not be repeated
*/
$this->db->distinct();
$this->db->select(
array(
"Accounts.id id",
"Accounts.email email",
"Accounts.name name",
"Accounts.username username",
"Accounts.avatar avatar",
"Accounts.nivel nivel",
"Accounts.verification verification",
"Accounts_Messages.id acmid",
"Accounts_Profiles.status online",
"(SELECT IF(COUNT(ACM.id) > 0, COUNT(ACM.id), null) FROM Accounts_Messages ACM WHERE ACM.m_to = '$login_user_id' AND ACM.m_from = 'Accounts.id' AND ACM.is_read = '0') unread"
)
);
/*
Here I exclude the $login_user_id from the list, the admin users and also those who have not verified the account
*/
$this->db->join(
"Accounts_Profiles",
"Accounts_Profiles.user_id = Accounts.id",
"left"
);
$this->db->where(
array(
"Accounts.id !=" => $login_user_id,
"Accounts.nivel !=" => 'administrator',
"Accounts.verification !=" => 'false'
)
);
/*
Here, I sort messages by last sent. I check who the logged in user sent the last messages to and list those users
*/
$this->db->join(
"(SELECT m_to, MAX(dt_updated) as max_updated FROM Accounts_Messages GROUP BY m_to) last_message",
"Accounts.id = last_message.m_to",
'inner'
);
$this->db->join(
"Accounts_Messages",
"Accounts.id = Accounts_Messages.m_to AND Accounts_Messages.dt_updated = last_message.max_updated",
'inner'
);
$this->db->group_by(
"Accounts.id, Accounts_Messages.id"
);
$this->db->order_by(
"last_message.max_updated",
"DESC"
);
?>

Related

Magento/PHP - How to get number of all members in a customer group

I am working on a custom magento extension.
I've made a selection menu with all customer groups, here is the code of it:
$CustomerGroups = Mage::getResourceModel('customer/group_collection')->toOptionArray();
$CustomerGroups = array_merge(array('' => ''), $CustomerGroups);
$fieldset->addField('customergroups', 'select',
array(
'name' => 'customergroups',
'label' => Mage::helper('smsnotification')->__('User Group'),
'class' => 'required-entry',
'values' => $CustomerGroups
)
);
Here is the result:
As you can see the results are like this:
not logged in
General
Wholesale
Retailer
What i want is simply to add the number of members in every customer group, for example General - (125 members).
Let me give you an example of what i want to achieve but don't know how in image:
Can you please help me out achieve that ?
Thanks in advance!
If you donot want to use a direct MySQL Query on database then you can try this:
$customerCollectionSize = array();
$customerGroups = Mage::getModel('customer/group')->getCollection();
foreach($customerGroups as $customerGroup) {
$customerGroupId = $customerGroup->getId();
$customerCollection = Mage::getModel('customer/customer')
->getCollection()
->addAttributeToSelect('*')
->addFieldToFilter('group_id', $customerGroupId);
$customerCollectionSize[$customerGroupId] = $customerCollection->getSize();
}
Now in this array $customerCollectionSize you will have total no. of customers corresponding to the customer group id.
If you want to use direct MySQL Query then try as #tom-lankhorst suggested above:
SELECT `group_id`, count(*) AS `count` FROM `customer_entity` GROUP BY `group_id`
Hope this helps!!
This query should do the job:
SELECT `group_id`, count(*) AS `count` FROM `customer_entity` GROUP BY `group_id`
Group by Customer Group name:
SELECT g.customer_group_code, count(e.entity_id) AS `count` FROM `customer_entity` AS e
JOIN customer_group AS g on g.customer_group_id = e.group_id
GROUP BY 1

WooCommerce - Get the number of orders by a given customer

I need to find that a particular customer has done business with that store previously.
To achieve that I need to find the number of orders by a given customer.
How can I achieve that?
I tried Googling, but did not find any solution.
Just drop in the user id and you'll get your total number of orders:
$numorders = wc_get_customer_order_count( $userid );
To go a step further for my own purposes, I use this code to get the number of a customer's non-cancelled orders since I don't want to count failed order attempts:
// Get TOTAL number of orders for customer
$numorders = wc_get_customer_order_count( $userid );
// Get CANCELLED orders for customer
$args = array(
'customer_id' => $userid,
'post_status' => 'cancelled',
'post_type' => 'shop_order',
'return' => 'ids',
);
$numorders_cancelled = 0;
$numorders_cancelled = count( wc_get_orders( $args ) ); // count the array of orders
// NON-CANCELLED equals TOTAL minus CANCELLED
$num_not_cancelled = $numorders - $numorders_cancelled;
Modifying #MarkPraschan Code, which works well for me without throwing any notice, as i got 2 notice about undefined variable $user_id and i'm not passing the code through a function. Using below code worked for me (which gets the number of order transaction minus canceled orders);
$current_user = wp_get_current_user();
$numorders = wc_get_customer_order_count( $current_user->ID );
// Get CANCELLED orders for customer
$args = array(
'customer_id' => $current_user->ID,
'post_status' => 'cancelled',
'post_type' => 'shop_order',
'return' => 'ids',
);
$numorders_cancelled = 0;
$numorders_cancelled = count( wc_get_orders( $args ) ); // count the array of orders
// NON-CANCELLED equals TOTAL minus CANCELLED
$num_not_cancelled = $numorders - $numorders_cancelled;
if you intend to display both completed and non completed orders, you will use the first two line of the above code, which is;
$current_user = wp_get_current_user();
$numorders = wc_get_customer_order_count( $current_user->ID );
Tested and working on;
WP = v4.9.9
WC = v3.5.3
I know this is an old question, but thought I'd share my code/info anyways.
The customer will be connected to the order via the postmeta key _customer_user in the wp_postmeta table.
You can lookup all orders with the status completed and processing for a specific user ID with the following query, where 279 is the user ID:
SELECT COUNT(p.ID)
FROM wp_posts AS p
INNER JOIN wp_postmeta AS m ON m.post_id = p.ID AND m.meta_key = '_customer_user' AND m.meta_value = 279
WHERE p.post_status IN ('wc-completed', 'wc-processing') AND p.post_type = 'shop_order'
When translated into PHP code that can be used on any WP installation simply by placing the code at the bottom of your theme functions.php file.
This example displays the total orders for a customer on the order page in the back-end of WordPress, directly below the customer selection/dropdown. For instance if you need to know if this is the first order a customer has placed/done you can display a message. Obviously you will want to change the order status filter to something that suits your needs. Doing a direct query like below is more efficient I believe.
// Hook into the order back-end WooCommerce > Orders > [Edit]
// The output will be placed under the dropdown to choose/connect a customer to the order
add_action('woocommerce_admin_order_data_after_order_details', 'f4d_customer_order_count');
function f4d_customer_order_count($order){
global $wpdb;
// Retrieve customer ID based on current order
$customerId = $order->get_customer_id();
// When the order was placed by a guest, just return
if($customerId===0) return;
// When the order is connected to a user/customer, query the total orders
// Database tables we will use in our query (also grab the table prefix)
$postsTable = $wpdb->prefix.'posts';
$postsMetaTable = $wpdb->prefix.'postmeta';
// Filter orders by specific status
$orderStatusFilter = array('wc-completed', 'wc-processing');
// Connect the array into a string that is compatible with our query (IN() query statement)
$orderStatusFilter = "'".implode("','", $orderStatusFilter)."'";
// Only get the single variable from the database
$totalOrders = $wpdb->get_var("
SELECT COUNT(p.ID)
FROM $postsTable AS p
INNER JOIN $postsMetaTable AS m ON m.post_id = p.ID AND m.meta_key = '_customer_user' AND m.meta_value = $customerId
WHERE p.post_status IN ($orderStatusFilter) AND p.post_type = 'shop_order'");
echo '<p class="form-field form-field-wide wc-customer-order-count">';
if($totalOrders===1){
// When this is the first order, display a message to our admin to give a first time offer
echo '<span style="color:white;background-color:red;padding:20px;">FIRST TIME OFFER</span>';
}else{
// Otherwise just display the total orders the customer has placed in the past
echo '<span>'.esc_html__( 'Total Orders', 'super-forms' ) . ': '.$totalOrders.'</span>';
}
echo '</p>';
}
If you need a list format of multiple customers/users, then you can use the $wpdb->get_results() instead of $wpdb->get_var() and loop over the results (table rows).
Found a way.
$args = [
'author' => 'id',
'post_status' => 'any',
'post_type' => 'shop_order'
];
$query = new WP_Query($args);
$orderCountByCustomer = $query->found_posts;

Optimizing a while loop that contains PDO query's

Information
Currently building an notification page which lists all of the logged in users notifications which contain information about the notification on each one.
For example, without information
You have an unread message
With information
<Sarah> Sent you an message
Problem
Because the notifications require data such as Username (for message notifications) or a article title (say your following an author and they release a new blog post, one notification would need to pull username form users table and then also the title of the blog from the blog table) this causes my page to lag even on localhost which I'm guessing would get significantly worse once uploaded and tested in the wild.
Current Code
function showNotifications($userid){
$STH = $this->database->prepare('SELECT * FROM notifications WHERE user_id = :userid ORDER BY timestamp DESC');
$STH->execute(array(':userid' => $userid));
while($row = $STH->fetch(PDO::FETCH_ASSOC)){
$this->sortNotif($row);
}
Quick explanation about the function below, because I have different types of notifications I created a bunch of ID's for specific types, for example type 1 = new message, type 2 = new blog post
function sortNotif($notif){
switch ($notif['type']) {
case "1":
$msg = $this->getMessageData($notif['feature_id']);
$user = $this->userData($msg['sender']);
echo '<li><i>'.timeAgo($notif['timestamp']).'</i>'.$user['first_name'].' sent you a message</li>';
break;
}
}
As you can see for just showing that a user has a new message it creates 2 query's and once looped through 40 or so notifications, over 100 or so users becomes a strain on the server.
Final Words
If anyone needs more information please ask and I'll be sure to update this question asap, thanks!
Edit
Below are table structures as requested in the below comments.
notifications
id | user_id | feature_id | type | timestamp | read
users
id | username | password | first_name | last_name | email | verify_hash | avatar | type
messages
id | receiver | sender | replying_to | deleted | body | timestamp | read
Change of plan, since I misunderstood the set up.
You will want to pull all of the data from each of the 'type' tables in one go, rather than on a per notification basis. This means that you will need to loop through your notifications twice, once to grab all the ids and appropriate types and then a second time to output the results.
function showNotifications($userid){
$STH = $this->database->prepare('SELECT * FROM notifications WHERE user_id = :userid ORDER BY timestamp DESC');
$STH->execute(array(':userid' => $userid));
// Centralized Book keeping for the types.
// When you add a new type to the entire system, add it here as well.
$types = array();
// Add the first notification type
$types["1"] = array();
// "query" is pulling all the data you need concerning a notification
$types["1"]["query"] = "SELECT m.id, u.username, u.firstname FROM messages m, users u WHERE m.sender = u.id AND m.id IN ";
// "ids" will hold the relevant ids that you need to look up.
$types["1"]["ids"] = array();
// A second type, just for show.
// $types["2"] = array();
// $types["2"]["query"] = "SELECT a.id, u.username, u.firstname FROM articles a, users u WHERE a.sender = u.id AND a.id IN ";
// $types["2"]["ids"] = array();
// Use fetchAll to gather all of the notifications into an array
$notifications = $STH->fetchAll();
// Walk through the notifications array, placing the notification id into the corret
// "ids" array in the $types array.
for($i=0; $i< count($notifications); $i++){
$types[$notifications[$i]['type']]["ids"][] = $notifications[$i]['feature_id'];
}
// Walk through the types array, hit the database once for each type of notification that has ids.
foreach($types as $type_id => $type){
if(count($type["ids"]) > 0){
$STH = $this->database->prepare($type["query"] . "( " . implode(",", $type["ids"]) . " )");
$STH->execute();
// Creates a hash table with the primary key as the array key
$types[$type_id]['details'] = $STH->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC);
$types[$type_id]['details'] = array_map('reset', $types[$type_id]['details']);
// run array_map to make it easier to work with, otherwise it looks like this:
// $results = array(
// 1234 => array(0 => array('username' => 'abc', 'firstname' => '[...]')),
// 1235 => array(0 => array('username' => 'def', 'firstname' => '[...]')),
// );
}
}
// Now walk through notifications again and write out based on notification type,
// referencing $types[<notification type>]["details"][<message id>] for the details
for($i=0; $i< count($notifications); $i++){
// check to see if details for the specific notification exist.
if(isset($types[$notifications[$i]['type']]["details"][$notifications[$i]['feature_id']])){
$notification_details = $types[$notifications[$i]['type']]["details"][$notifications[$i]['feature_id']];
switch ($notifications[$i]['type']) {
case "1":
echo '<li><i>'.timeAgo($notifications[$i]['timestamp']).'</i>' . $notification_details['first_name'].' sent you a message</li>';
break;
}
}
}
}
Update : Added logic to skip a notification if no details are pulled (ex. either the message or the user were deleted)
I think you want to run a single query that gathers all the info either via joins or a more complicated where statement.
Option 1: this might need to be tweaked so as to not the cartesian product of the tables
SELECT n.id, n.type, m.id, m.body, u.username, u.first_name
FROM notifications n, messages m, users u
WHERE n.user_id = :userid AND m.id = n.feature_id AND u.id = m.sender
Option 2: if the table aliases don't work, then you'll need to replace them with the full table name
SELECT SELECT n.id, n.type, m.id, m.body, u.username, u.first_name
FROM notifications n
JOIN messages m
ON n.feature_id = m.id
JOIN users u
ON m.sender = u.id
WHERE n.user_id = :userid

PHP how to check 2 statements to only send one message no duplicates between

I have been trying to code a notification system for a profile wall script I made a long time ago for a forum.
The trouble I have is, that I am trying to only send one PM to the people who fit the criteria for the PM, but I also have 2 select statements in MYSQL to find the people who should be PM'd.
I have 2 tables to select from... log_statuses and log_status_replies
I am trying to achieve this.
I want it to notify everyone who has commented only ONCE! Also the persons wall it belongs to and the person who written the status on the persons wall, but not yourself if you written it.
Kinda complex that's why I am struggling to get this right...
Here is my code for it to search through who commented and send them a PM.
How would I also do the checks for who written the status and who's wall it is, without it sending a duplicate PM if they have commented aswell.
Otherwise they would receive a PM because they have commented and because its their status/wall.
I hope you understand that, kinda hard to explain.
Here is my source code.. I would love anyone who could help..
$query = $smcFunc['db_query']('', '
SELECT id_member, id_poster
FROM {db_prefix}log_statuses
WHERE id_status = {int:id_status}',
array(
'id_status' => $status,
)
);
list($id_member,$id_poster) = $smcFunc['db_fetch_row']($query);
if ($id_poster != $ucomment || $id_member != $ucomment)
{
$query2 = $smcFunc['db_query']('', '
SELECT DISTINCT id_member
FROM {db_prefix}log_status_replies
WHERE id_status = {int:id_status}',
array(
'id_status' => $status,
)
);
while ($row = $smcFunc['db_fetch_assoc']($query2)) {
if ($row['id_member'] != $ucomment)
{
$pm_recipients = array(
'to' => array($row['id_member']),
'bcc' => array(),
);
require_once($sourcedir . '/Subs-Post.php');
$notify_body = $txt['status_body'] . '[iurl]' .$scripturl . '?action=profile;area=showstatus;s=' . $status . '[/iurl]';
$notify_body = str_replace("%poster",$context['user']['name'],$notify_body);
$pm_from = array(
'id' => $ucomment,
'username' => '',
'name' => '',
);
sendpm($pm_recipients,$txt['status_subject'] , $notify_body,false,$pm_from);
}
}
}
$id_member is the persons wall, $id_poster is the person who posted on the wall.
$ucomment is the person who is posting..
Solved.. I built an array from the while, then checked if the value exists from the comments table when checking the other table to see who to PM also..
$id_comment = array();
while ($row = $smcFunc['db_fetch_assoc']($query2)) {
$id_comment[] = $row['id_member'];
}
if (!in_array($id_poster, $id_comment) && !in_array($id_member, $id_comment)) {
//Send PM
}
Use UNION:
For example::
Select column1 from table1 where condition1=true
UNION
Select column1 from table1 where condition2=true
The resultset will not have a duplicate records.
By Chance, if you need all the values including the duplicates use UNION ALL

MySQL - Skip Duplicate WordPress Entries

I'm writing a script to display the 10 most recently "active" WordPress blog posts (i.e. those with the most recent comments). Problem is, the list has a lot of duplicates. I'd like to weed out the duplicates. Is there an easy way to do this by changing the MySQL query (like IGNORE, WHERE) or some other means? Here's what I have so far:
<?php
function cd_recently_active() {
global $wpdb, $comments, $comment;
$number = 10; //how many recently active posts to display? enter here
if ( !$comments = wp_cache_get( 'recent_comments', 'widget' ) ) {
$comments = $wpdb->get_results("SELECT comment_date, comment_author, comment_author_url, comment_ID, comment_post_ID, comment_content FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT $number");
wp_cache_add( 'recent_comments', $comments, 'widget' );
}
?>
Look at the DISTINCT option for the SELECT statement. Or alternatively the GROUP BY syntax (look at the same link). Though they work in different ways, these would be the two methods most likely to help you get exactly what you want.
I thought I had figured it out using GROUP BY, but now I'm not so sure. Here is the code:
if ( !$comments = wp_cache_get( 'recent_comments', 'widget' ) ) {
$comments = $wpdb->get_results("SELECT comment_post_ID, comment_author, comment_date FROM $wpdb->comments WHERE comment_approved = '1' GROUP BY comment_post_ID ORDER BY comment_date_gmt DESC LIMIT $number");
wp_cache_add( 'recent_comments', $comments, 'widget' );
}
The single change is to add GROUP BY comment_post_ID (the field I wanted to be unique). Unfortunately, this "breaks" the function; it's frozen and does not update.
I also could not get DISTINCT to work. One comment I'm following up on to figure this out came from http://www.webmasterworld.com/forum88/9204.htm
In particular, comment #:1259236 by ergophobe says, "You left out the GROUP BY. Without that, you will get multiple results for a given topic_id b/c the row will not be distinct. In fact, the distinct is not necessary, just the GROUP BY."
Still looking....

Categories