Fetch all rows related to user - php

I am trying to create a json object with data from two tables.
Table 1 holds user information
id
name
username
email
Table 2 holds messages to the users
id
f_id
message
date
So far so good. I have managed to do this with LEFT JOIN
$sql = "SELECT * FROM user LEFT JOIN messages ON user.id = messages.f_id
$result = mysqli_query($conn, $sql) or die(mysql_error());
while ($data = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
$messages[] = array(
'users' => array(
'Id' => $data['id'],
'Name' => $data['name'],
'Username' => $data['username'],
'Email' => $data['email'],
),
'messages' => array(
'f_id' => $data['f_id'],
'message' => $data['message'],
'date' => $data['date'],
);
);
}
but the issue/challenge is that it only returns one row from messages table even if there multiple rows related to the user.
This is what I'm trying to achieve
'user' => array(
'id' => 1,
'name' => John Doe,
'username' => Johndoe,
'email' => johndoe#mail.com,
),
'messages' => array(
'id' => 1,
'f_id' => 1,
'message' => 'Just a test',
'date' => 14-08-2014,
'id' => 2,
'f_id' => 1,
'message' => 'Just a test 1',
'date' => 12-08-2014,
);
How can I achieve this?
Do I need to run a while in while?

You can do it in 2 ways:
1) Query only the users, and then iterate over them and query the messages for each user. That means that you will query the database N+1 times, where N is the number of users.
2) Query the messages joined with the users (and not the opposite), which will result with a lot of "unused" data, but a single query.

Try:
SELECT
user.id AS user_id, user.name, user.email, user.username,
messages.id AS message_id, messages.f_id, messages.message, messages.date
FROM user, messages
LEFT JOIN messages ON user.id = messages.f_id
Note that an alias was needed for user.id and messages.id as both column names are the same.

Please update your sql query
$sql = "SELECT * FROM user Right JOIN messages ON user.id = messages.f_id
or
$sql = "SELECT * FROM messages LEFT JOIN user ON user.id = messages.f_id

You can try that :
SELECT * FROM messages LEFT JOIN user ON user.id = messages.f_id
It will get all messages with users but if a user has no messages, you'll not be able to get it with this kind of query.

What is probably happening is that when you're running the query on the users table, it will use the primary key of that table to ensure that there's no "duplicate" data (as it should). You have two options here:
Query Users, Query Messages [Not Recommended]
This might be a little slow since you'll be querying the database many times. You could do something like
SELECT * FROM users;
then later iterate over the users result and run a query on messages
SELECT * FROM messages WHERE f_id = <user_id>;
What's the issue with this? The more users you have, the more wasteful this is, and also the slower your site will perform. Not recommended.
Query messages, join users [Recommended]
Run this query on messages, rather than users. It will use the primary key of the messages table (which will be id) to determine what is a duplicate or not.
SELECT * FROM messages LEFT JOIN users ON users.id = messages.f_id;
This is much more efficient because it's using one query to grab all the data you need and it should need a little tweaking maybe as your site grows.

Related

How to link and display MySQL data from two different tables?

I have two tables named "stats" and "users"
users table has all the typical user data like id,username,password,email(columns)
stats table has id,attack, defense,ostats,gold,food(columns)
I want to display data from these two tables side by side and have the data linked through their IDS
For example,
Rank user_uid ostats attack defense gold
1 Test 10 5 5 100
2 Test2 8 2 6 60
3 Test3 6 5 1 40
Username is from table "users" and the rest of them are from table "stats"
So first I want to know how to link and display the data from the same ID, like Username(user_id=1) and ostats,attack,defense,gold,food(id=1)
Then I want them in order by their "ostats" (I don't have a column named "rank" in any table yet, just don't know how to create the rank using overall stats)
You could do something like (untested)
SELECT u.username, s.overall, s.attack, s.defense, s.gold
FROM stats s JOIN users u on s.user_uid = u.id
ORDER BY s.overall;
Possible solution to ranking:
set #row_number=0;
SELECT (#row_number:=#row_number+1) as rank, u.username, s.overall, s.attack, s.defense, s.gold
FROM stats s JOIN users u on s.user_uid = u.id
ORDER BY s.overall;
Another, horrible looking attempt:
set #row_number = (select count(*) from users) + 1;
select (#row_number:=#row_number-1) as rank, u.username, s.overall from
stats s join users u on s.user_uid = u.id order by s.overall desc;
set #row_number = 0;
Here in PHP code, you have to run it as two queries to set the variable, then run the actual ranking query. This way, the rank variable is always set to 0 when running this. Note that I've used different table and column names, just to simplify things a little. Remember to adjust to your specific needs.
// connect to database
$conn = mysqli_connect("localhost", "user", "password", "database");
// this query will set a variable to 0.
$setSql = "SET #row_number = 0;";
// run the query. This will return a boolean - true or false, depending on whether or not the query ran successfully
$variableSet = mysqli_query($conn, $setSql);
// if the query ran successfully
if($variableSet){
// setup the actual ranking query
$statsSql = "select
(#row_number:=#row_number+1) as rank,
u.id,
u.username,
s.overall
from
mstats s
join
musers u
on
s.muser = u.id
order by
s.overall desc;";
$ranks = mysqli_query($conn, $statsSql);
if(!$ranks){
// dump error from rank query
var_dump($conn->error);
} else {
// dump results as associative array
var_dump($ranks->fetch_all(MYSQLI_ASSOC));
}
} else {
// dump errors from setting variable
var_dump($conn->error);
}
For me, the results dump looks like this:
array (size=3)
0 =>
array (size=4)
'rank' => string '1' (length=1)
'id' => string '2' (length=1)
'username' => string 'Bar' (length=3)
'overall' => string '1000' (length=4)
1 =>
array (size=4)
'rank' => string '2' (length=1)
'id' => string '6' (length=1)
'username' => string 'Tom' (length=3)
'overall' => string '7' (length=1)
2 =>
array (size=4)
'rank' => string '3' (length=1)
'id' => string '1' (length=1)
'username' => string 'Foo' (length=3)
'overall' => string '3' (length=1)

zend framework - can result array from sql query contain database names?

If we run a query such as the following:
SELECT `user`.`user_id`
, `user`.`username`
, `profile`.`user_id`
, `profile`.`name`
, `profile`.`location`
FROM `user`
JOIN `profile`
USING (`user_id`)
WHERE `user`.`user_id` = 1;
then we get the result set:
object(ArrayObject)
private 'storage' =>
array
'user_id' => string '1'
'username' => string 'ExampleUsername'
'name' => string 'Example name'
'location' => string 'Example location'
Notice that user_id field is only returned once, even though it exists twice in the SQL query.
Is there a way to return table names as part of the result set? For example, the following result set would be desired:
object(ArrayObject)
private 'storage' =>
array
'user' => array
'user_id' => string '1'
'username' => string 'ExampleUsername'
'profile' => array
'user_id' => string '1'
'name' => string 'Example name'
'location' => string 'Example location'
I have seen this done in other frameworks (Laravel, CodeIgniter) but am not seeing the option for Zend Framework version 2 or 3.
This is just an example SQL query. We are running much more complex queries in our project where a returned associative array with table names as keys would be ideal.
I think you mean you want the keys to include table names, not database names.
IIRC there's no built-in way to do this in Zend Framework.
You can make each key distinct, but it's up to you to do this by defining column aliases:
SELECT `user`.`user_id` AS user_user_id
, `user`.`username`
, `profile`.`user_id` AS profile_user_id
, `profile`.`name`
, `profile`.`location`
FROM `user`
JOIN `profile`
USING (`user_id`)
WHERE `user`.`user_id` = 1;
This is a common problem with any database library that returns results in an associative array, not just Zend Framework and not even just PHP.
The second example you show, of fetching columns into some kind of nested data structure broken down by tables, is not supported in any database library I've ever used. How would it return the results of the following query?
SELECT user.user_views + profile.profile_views AS total_views
FROM user JOIN profile USING (user_id)
Would total_views belong under the user key or the profile key?
There are many other similar examples of SQL queries that return results that don't strictly "belong" to either of the joined tables.

How to get data from different associated tables using containable in Cakephp?

I have 3 tables: orders, discounts and products in the way that product has many discounts (discount times). Discount has many orders. in other words, it looks like: Product > Discount > Order.
I want to get data from 3 tables as the raw mySQL query below:
SELECT discounts.product_id, products.product_name,
sum(products.product_price - discounts.product_discount) as total_Amount,
count(orders.order_id) as total_Number
FROM products
inner join discounts on products.product_id = discounts.product_id
inner join orders on discounts.discount_id = orders.discount_id
group by discounts.product_id,products.product_name
This is what I did:
$this->Order->virtualFields['benefit']='SUM(Product.product_price - Discount.product_discount)';
$this->Order->virtualFields['number']='COUNT(Order.order_id)';
$products = $this->Order->find('all',array('contain'=>array('Discount'=>array('Product'))),
array( 'limit'=>20,
'fields'=>array('benefit','number'),
'group'=>array('Discount.product_id','Product.product_name')));
Debugger::dump($products);
$this->set('products',$products);
But I got an error:
Database Error
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Product.product_price' in 'field list'
SQL Query: SELECT `Order`.`order_id`, `Order`.`user_id`
`Order`.`order_date`, `Order`.`payment`, `Order`.`discount_id`, `Order`.`total`,
(SUM(`Product`.`product_price` - `Discount`.`product_discount`)) AS `Order__benefit`,
(COUNT(`Order`.`order_id`)) AS `Order__number`, `Discount`.`discount_id`,
`Discount`.`product_id`, `Discount`.`product_discount`,
`Discount`.`start_time`, `Discount`.`end_time`
FROM `project`.`orders` AS `Order`
LEFT JOIN `project`.`discounts` AS `Discount`
ON (`Order`.`discount_id` = `Discount`.`discount_id`) WHERE 1 = 1
It seems that containable didnt work as it didnt not contain products table in the query.
EDIT: according to Dave's suggestion, I used JOIN:
$this->Order->recursive=-1;
$this->Order->virtualFields['benefit']='SUM(Product.product_price - Discount.product_discount)';
$this->Order->virtualFields['number']='COUNT(Order.order_id)';
$option['joins'] = array(
array('table'=>'discounts',
'alias'=>'Discount',
'type'=>'INNER',
'conditions'=>array(
'Order.discount_id = Discount.discount_id',
)
),
array('table'=>'products',
'alias'=>'Product',
'type'=>'INNER',
'conditions'=>array(
'Discount.product_id = Product.product_id'
)
)
);
$products = $this->Order->find('all',$option,
array( 'limit'=>20,
'fields'=>array('Discount.product_id','Product.product_name'),
'group'=>array('Discount.product_id','Product.product_name')));
Debugger::dump($products);
$this->set('products',$products);
However, what $products contains is only:
array(
(int) 0 => array(
'Order' => array(
'order_id' => '23567636',
'user_id' => '1',
'order_date' => '2013-11-16 16:03:00',
'payment' => 'mc',
'discount_id' => '2',
'total' => '599',
'benefit' => '7212',
'number' => '19'
)
)
)
but, what I want is:
How can I fix that? thanks in advance.
Containable is not the same as JOIN.
Containable does not join the queries into a single query, but for the most part creates completely separate queries, then combines the results for your viewing pleasure.
So - per your error, in the query that's being run on the orders table, there IS no Product.product_price field because those fields are available only in a completely separate query.
Try using JOINs instead.

Cakephp custom query with left join table rows as nested arrays

I'm trying to get nested arrays for my Cakephp custom query below:
$this->query("
SELECT *
FROM group_buys GroupBuy
LEFT JOIN products Product
ON Product.id = GroupBuy.product_id
LEFT JOIN group_buy_users GroupBuysUser
ON GroupBuysUser.group_buy_id = GroupBuy.id
LEFT JOIN group_buy_images GroupBuyImage
ON GroupBuyImage.group_buy_id = GroupBuy.id
LEFT JOIN product_details ProductDetail
ON ProductDetail.product_id = Product.id
LEFT JOIN specifications Specification
ON Specification.id = ProductDetail.specification_id
LEFT JOIN specification_categories SpecificationCategory
ON SpecificationCategory.id = Specification.specification_category_id
WHERE GroupBuy.id = {$id}
");
Problem with this is that it comes up with redundant data obviously with GroupBuy table row values repeating which I don't want.
Is there a way we can have nested arrays if LEFT JOINED table has more rows than the former table with Cake's custom query?
I know this can be done with find recursive = 2 but would like to achieve this with custom query.
Have you tried using containable?
$this->GroupBuy->Behaviors->attach('Containable');
$this->GroupBuy->find('all', array(
'conditions' => array('GroupBuy.id' => $id),
'contain' => array(
'Product' => array(
'ProductDetail' => array(
'Specification' => array(
'SpecificationCategory'
)
)
),
'GroupBuysUser',
'GroupBuyImage'
),
));

Cakephp using group by in find method

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

Categories