How to join two MySQL tables for wp_rest_api - php

In my WordPress database I have three tables wp_companies which stores some company info and wp_product_types which stores a bunch of product types that a company can assign them selves then I have wp_company_products which is used to assign product types to a company using ids.
Heres an example of how the database looks:
wp_companies
id | company_name | member_type | logo
-----------------------------------------------------------------
1 | Google | full | https://via.placeholder.com/150
-----------------------------------------------------------------
2 | Crunchyroll | full | https://via.placeholder.com/150
wp_products
id | product_name |
----------------------
1 | Car Insurance |
----------------------
2 | House Insurance |
----------------------
3 | Life Insurance |
wp_company_products
id | company_id | product_id
----------------------------
1 | 1 | 2
----------------------------
2 | 2 | 1
----------------------------
3 | 1 | 3
Here's my current MySQL query that simply shows the data in wp_companies
add_action('rest_api_init', function() {
register_rest_route('custom-routes/v1', 'members', array(
'methods' => 'GET',
'callback' => 'get_members'
) );
});
function get_members($data) {
global $wpdb;
$query = $wpdb->get_results( "SELECT company_name, member_type, logo, site_url FROM {$wpdb->prefix}companies ORDER BY RAND()" );
foreach ( $query as $member ) {
$member_data[] = array(
"company_name" => $member->company_name,
"member_type" => $member->member_type,
"logo" => $member->logo,
);
}
return $member_data;
}
This displays my data like so on my api end point:
[
{
"company_name":"Google",
"member_type":"full",
"logo":"https://via.placeholder.com/150",
},
{
"company_name":"Crunchyroll",
"member_type":"full",
"logo":"https://via.placeholder.com/150",
}
]
But what I want is to combine the wp_companies and wp_company_products tables so that my data is displayed something like this on the api end point:
[
{
"company_name":"Google",
"member_type":"full",
"logo":"https://via.placeholder.com/150",
"products": [
"House Insurance",
"Life Insurance"
]
},
{
"company_name":"Crunchyroll",
"member_type":"full",
"logo":"https://via.placeholder.com/150",
"products": [
"Car Insurance",
]
}
]
How can I structure my MySQL query to be able to achieve this?

Please try this
function get_members($data) {
global $wpdb;
$query = $wpdb->get_results( "SELECT company_name, member_type, logo, site_url FROM {$wpdb->prefix}companies ORDER BY RAND()" );
foreach ( $query as $member ) {
$productQuery = $wpdb->get_results( "SELECT product_name FROM wp_products WHERE id IN (SELECT product_id from wp_company_products WHERE compony_id = '{$member->id}') " );
$products = array();
foreach ( $productQuery as $prduct ) {
array_push($products ,$prduct->product_name);
}
$member_data[] = array(
"company_name" => $member->company_name,
"member_type" => $member->member_type,
"logo" => $member->logo,
"products" => $products
);
}
return $member_data;
}

Related

(Laravel/PHP) SQL Query that selects all orders from table that matches product_id and variation_id in array

Im trying to build a SQL Query that will select all orders from a table that matches options that i defined.
Databse i use: Mysql
Language: PHP
Basicly i have a array that looks like this.
[
[
"user_id" => 1,
"product_id" => 5548,
"variation_id" => 14
],
[
"user_id" => 1,
"product_id" => 5548,
"variation_id" => 15
],
[
"user_id" => 1,
"product_id" => 4422,
"variation_id" => 4
]
]
This means that the user(id: 1) has one product with the "id" of 5548, and then he also has 2 variations of that product that are "id" 14 and 15. You can also see that the same user owns the product(id:4422) that has variation(id:4).
I then have a "order_lines" table that looks like this
order_lines
+----+-----+---------+-----------------------------+
| id | uid | user_id | product_id | variation_id |
+----+-----+---------+-----------------------------+
| 1 | 1 | 1 | 5548 | 14 |
+----+-----+---------+-----------------------------+
| 2 | 2 | 1 | 5548 | 15 |
+----+-----+---------+-----------------------------+
| 3 | 3 | 1 | 4422 | 4 |
+----+-----+---------+-----------------------------+
| . | . | . | .... | .. |
+----+-----+---------+-----------------------------+
I now need a SQL Query that selects all the rows where there is a match between the user_id, product_id and variation_id that are defined in the array.
The output should contain all rows that meet these conditions.
I hope someone can pin me in the right direction.
I'm building in Laravel if you got the query builder just at your hand. Else i very much appreciate an SQL Query.
if I am getting you right, below code will help you, using just Core PHP
foreach($array as $arr){
$user_id = $arr['user_id'];
$prodct_id = $arr['prodct_id'];
$variation_id = $arr['variation_id'];
$query = "SELECT * FROM order_lines WHERE user_id = $userId AND product_id = $productId AND variation_id = $variationId";
$queryResult = mysql_fetch_assoc($query);
$yourCollection[] = $queryResult;
}
print_r($yourCollection);
Try below code to use Laravel Query Builder, below code will help you to get results for multiple users based on product and variation.
$qb_order_lines = DB::table('order_lines');
$where_condition = [
['user_id' => '', 'product_id' => '', 'variation_id' => ''],
];
foreach ($where_condition as $condition) {
$qb_order_lines->orWhere(function($query) use ($condition) {
$query->where('user_id', $condition['user_id'])
->where('product_id', $condition['product_id'])
->where('variation_id', $condition['variation_id']);
});
}
$obj_result = $qb_order_lines->get();
If you want to get it for only one user, use below code
$obj_result = DB::table('order_lines')
->where('user_id', $condition['user_id'])
->where('product_id', $condition['product_id'])
->where('variation_id', $condition['variation_id'])
->get();
You can modify the above query builders based on your requirements like select fields or group by.
Let me know if you need any help.
For anyone interesting.
My problem was that i needed to count of many matches that were between my array and my database.
Instead of selecting and outputting. I eneded up using sql count() function in a query, that did the job.

Codeigniter 4 query builder join display only 1 time from first table

I have done up a query builder using join. I would like to show table 2, 3, 4, 5, 6 and so on based on the user id on table 1. I tried to query the result, it is showing like this :
My Tables
Table users
user_id | username | email
1 | userA | userA#email.com
2 | userB | userB#gmail.com
Table add_game
game_id | user_id | ign | acc_id
1 | 1 | ignA | accA
2 | 1 | ignB | accB
1 | 2 | ignB | accB
3 | 2 | ignD | accD
I will be using foreach loop and I believe it will display out multiple times based on the records in the database. What should I do if I only want to display the information highlighted in the red box (which is from users table) just 1 time and all the records associated with user id in add_game table?
This is my current code :
Controller
public function login()
{
$data = [];
helper(['form']);
$validation = \Config\Services::validation();
$db = db_connect();
$model = new LoginModel($db);
$user = $model->login($this->request->getVar('userlogin'));
$this->setUserSession($user[0]);
echo view('templates/header', $data, $user);
echo view('account/login', $data, $user);
echo view('templates/footer', $data, $user);
}
private function setUserSession($user){
$data = [
'user_id' => $user['user_id'],
'username' => $user['username'],
'email' => $user['email'],
'firstname' => $user['firstname'],
'lastname' => $user['lastname'],
'dob' => $user['dob'],
'country' => $user['country'],
'country_code' => $user['c_code'],
'contact' => $user['contact'],
'game_id' => $user['game_id'],
'ign' => $user['ign'],
'acc_id' => $user['acc_id'],
'isLoggedIn' => true
];
session()->set($data);
return true;
}
Model:
return $this->db->table('users')
->groupStart()
->where('username', $str)
->orWhere('email', $str)
->groupEnd()
->join('add_game', 'add_game.user_id = users.user_id')
->get()
->getResultArray();
I have a few more tables but not yet created for now so I have only joined 1 table for the time being. What am I missing? Or do I have to loop twice? Is there a way that I just need to loop 1 time? Hope someone can help me out here. Thanks in advance guys!
the easiest way to achieve this (display 2 records from add_game table and 1 record from users table) you need to create a foreach loop in your view, and exclude duplicated data from users table to be shown.
controller:
$data['my_data']=$this->Your_model->your_method(); // your query example
$this->load->view('your_view',$data)
view:
<?php $my_id=0;foreach($my_data as $row):?>
<?php if($my_id!=$row->user_id):?>
<div><?=$row->username?></div> <!--data from table user-->
<div><?=$row->created_at?></div> <!--data from table add_game-->
<?php else:?>
<div><?=$row->created_at?></div> <!--only data from table add_game-->
<?php endif;?>
<?php $my_id=$row->user_id;endforeach;?>

create array using two foreach

I hope you guys can help me here, because I guess my code is not made correctly.
I have 2 mysql tables:
table: checks
+-----------+-------------+------------+
| id | name | host |
+-----------+-------------+------------+
| 1 | demo 1 | 1.1.1.1 |
+-----------+-------------+------------+
| 2 | demo 2 | 1.1.1.2 |
+-----------+-------------+------------+
| 3 | demo 3 | 1.1.1.3 |
+-----------+-------------+------------+
table: checks_history
+-----------+-------------+------------+------------+
| id | check_id | status | timestamp |
+-----------+-------------+------------+------------+
| 1 | 1 | 0 | 3451245 |
+-----------+-------------+------------+------------+
| 2 | 1 | 0 | 3451245 |
+-----------+-------------+------------+------------+
| 3 | 2 | 0 | 3451245 |
+-----------+-------------+------------+------------+
| 4 | 1 | 1 | 3451245 |
+-----------+-------------+------------+------------+
| 5 | 2 | 0 | 3451245 |
+-----------+-------------+------------+------------+
I want create a json file per id (table: checks) with this structure:
{
"info": { // Associated to table "checks"
"id": "1",
"name": "Demo 1",
"host": "1.1.1.1"
},
"data": { // associated to table check_history according with the id on table check
"1": { // associated to Column "id" on table checks_history
"status": "0",
"timestamp": "3451245"
},
"2": {
"status": "0",
"timestamp": "3451245"
},
"4": {
"status": "1",
"timestamp": "3451245"
}
}
}
There is my code PHP:
$info = array();
$history = array();
$incidents = $database->select("app_checks","*", false);
foreach ($incidents as $key => $value) {
$id = $value['id'];
$name = $value['name'];
$host = $value['host'];
$check_history = $database->select("app_checks_history", "*", [ "checkid" => $id, "ORDER" => ['id' => 'DESC'], "LIMIT" => 30 ]);
foreach ($check_history as $k => $v) {
$history = array(
$v['id'] => array(
'timestamp' => $v['timestamp'],
'status' => $v['status']
)
);
}
$info = array(
'info'=> array(
'id'=> $id,
'name'=> $name,
'host'=> $host
),
'data' => $history
);
$json_data = json_encode($info, JSON_PRETTY_PRINT);
$fileName = 'json/server_'.$id.'.json';
file_put_contents($fileName, $json_data);
}
When I try run the code, im getting the first value on "data" instead all loop:
{
"info": {
"id": "1",
"name": "Demo 1",
"host": "1.1.1.1"
},
"data": {
"1": {
"status": "0",
"timestamp": "3451245"
}
}
}
I searched in the forum and I did not found any similar issue related to my code.
I appreciate any help here.
Thanks in advance.
br
Well, I don't know PHP; however, I believe if you get your data model right, it should get you there. I would have my data model something like below in C#, an instance of Check class represent a check as per your table, serialize and save each instance in its own JSON file.
namespace Stackoverflow
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class Check
{
[JsonProperty("info")]
public Info Info { get; set; }
[JsonProperty("data")]
public Dictionary<string, History> Data { get; set; }
}
public partial class History
{
[JsonProperty("status")]
[JsonConverter(typeof(ParseStringConverter))]
public long Status { get; set; }
[JsonProperty("timestamp")]
[JsonConverter(typeof(ParseStringConverter))]
public long Timestamp { get; set; }
}
public partial class Info
{
[JsonProperty("id")]
[JsonConverter(typeof(ParseStringConverter))]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("host")]
public string Host { get; set; }
}
}

MySQL select multi dimensional?

I would like to select all the posts and their attachments from my database.
This is the structure with dummy data:
Posts table
id | post | userId |
---------|---------------|----------|
1 | "hello" | 1 |
2 | "world" | 1 |
3 | "ouch" | 2 |
4 | "test" | 1 |
Attachments table
id | postId | fileName | time |
---------|---------------|----------|----------|
1 | 1 |"hey.jpg" | 0 |
2 | 1 |"test.png"| 53252354 |
3 | 2 |"asd.png" | 0 |
4 | 4 |"asd2.png"| 0 |
My code looks like this so far, but I doesn't really get what I'd like to.
$qry = $db->prepare('
SELECT p.id
, p.post
, p.userId
, att.fileName
, att.time
FROM posts p
LEFT
JOIN attachments att
ON att.postId = p.id
');
$qry->execute();
$postsArray = $qry->fetchAll(PDO::FETCH_ASSOC);
I would like to have something like this:
[{'id': 1,
'post': 'hello',
'userId': 1,
'attachments': [{'fileName': 'hey.jpg', 'time:' 0}, ... ]
}, ... ]
How could I achieve this?
Your query will give you the right data for the results you want, you can post-process in PHP to get the format you want:
foreach ($postArray as $post) {
$fixed_part = array('id' => $post['id'], 'post' => $post['post'], 'userId' => $post['userId']);
$key = serialize($fixed_part);
if (!isset($out[$key])) $out[$key] = $fixed_part;
$out[$key]['attachments'][] = array('fileName' => $post['fileName'], 'time' => $post['time']);
}
$out = array_values($out);
echo json_encode($out, JSON_PRETTY_PRINT);
Output is too long to post but can be seen in this demo. Query results can be seen on dbfiddle.
Usually I would do something like this:
$result = $db->query("select id, post, userId from posts");
$posts = [];
while ($post = $result->fetch(PDO::FETCH_OBJECT)) {
$post->attachments = [];
$posts[$post->id] = $post;
}
$result = $db->query("select postId, fileName, time from attachments");
while ($att = $result->fetch(PDO::FETCH_OBJECT)) {
$posts[$att->postId]->attachments[] = $att;
unset($att->postId); // optional
}
$posts = array_values($posts); // optional
echo json_encode($posts);
Note that the $posts array is id-indexed. I would keep it that way. But if you need exactly the same result as in your question (zero-indexed), you can add this line:
$posts = array_values($posts);
In newer MySQL versions you can also get the JSON result with a single SQL query:
select json_arrayagg(post_json) as json
from (
select json_object(
'id', p.id,
'post', p.post,
'userId', p.userId,
'attachments', json_arrayagg(
json_object('fileName', a.fileName, 'time', time)
)
) as post_json
from posts p
left join attachments a on a.postId = p.id
group by p.id
) x
Result:
[{"id": 1, "post": "hello", "userId": 1, "attachments": [{"time": 0, "fileName": "hey.jpg"}, {"time": 53252354, "fileName": "test.png"}]}, {"id": 2, "post": "world", "userId": 1, "attachments": [{"time": 0, "fileName": "asd.png"}]}, {"id": 3, "post": "ouch", "userId": 2, "attachments": [{"time": null, "fileName": null}]}, {"id": 4, "post": "test", "userId": 1, "attachments": [{"time": 0, "fileName": "asd2.png"}]}]
db-fiddle demo

Pivoting mysql result with dates and sums

I have some data in mysql that I need to create some reports from.
My data are coming from the following query :
SELECT StoreNbr,StoreName,Date, SUM(`Sales`) FROM sales_tbl GROUP BY StoreNbr,StoreName,Date;
This results in the following data (just a small subset for my example):
+-----------+---------------------------+----------------------------+
| StoreNbr | StoreName | Date | SUM(Sales) |
+-----------+---------------------------+----------------------------+
| 1112 | Store1 | 2016-01-16 | 115.09 |
| 1112 | Store1 | 2016-01-17 | 81.00 |
| 1113 | Store2 | 2016-01-16 | 112.44 |
| 1113 | Store2 | 2016-01-17 | 56.61 |
I would like to transform my data to be this way :
+-----------+---------------------------+----------------------------+
| StoreNbr | StoreName | 2016-01-16 | 2016-01-17 |
+-----------+---------------------------+----------------------------+
| 1112 | Store1 | 115.09 | 81.00 |
| 1113 | Store2 | 112.44 | 56.61 |
Obviously there might be thousands of rows (stores) and unknown number of dates to be returned in the query as my query might be run like this (this will need to return 120+ number of columns for dates):
SELECT StoreNbr,StoreName,Date, SUM(`Sales`) FROM sales_tbl WHERE (Date BETWEEN '2016-01-10' AND '2016-05-10') GROUP BY StoreNbr,StoreName,Date;
There are a few ways to do this, none very simple. I did some research and there are some that mention that mysql does not support pivoting. I am running mariadb though, and saw that mariadb supports pivoting through the connect engine. I was unable to make it work though (adjust their official examples on my data).
Another way is lots of IFs and Cases, but most of the answers I am finding are very difficult to adapt or are tailored only to the data the guy that asks provides.
Another approach would be to process the data as they come out on my array as I have a json response in the end that feeds a datatable. - This is another think I have not managed to figure out yet.
I am looking for a way to get the desired output independent on the amount of dates (and I guess dates could be replaced by weeks or whatever else). Can anyone help?
Select all distinct dates
SELECT DISTINCT Date
FROM sales_tbl
WHERE (Date BETWEEN '2016-01-10' AND '2016-05-10')
ORDER BY Date;
and initialize an array which is indexed by that dates storing zeros:
$dateIndexedArray = array();
while($row = $stmt1->fetch(PDO::FETCH_ASSOC) {
$dateIndexedArray[$row['Date']] = 0;
}
The arry will look like
[
'2016-01-16' => 0,
'2016-01-17' => 0
]
Then execute your query
SELECT StoreNbr, StoreName,Date, SUM(`Sales`) AS Sales
FROM sales_tbl
WHERE (Date BETWEEN '2016-01-10' AND '2016-05-10')
GROUP BY StoreNbr,StoreName,Date;
And store the "Sales" in a date indexed array per store
$report = array();
while($row = $stmt2->fetch(PDO::FETCH_ASSOC)){
$storeIndex = $row['StoreNbr'] . ':' . $row['StoreName'];
if (!isset($report[$storeIndex])) {
$report[$storeIndex] = array(
'StoreNbr' => $row['StoreNbr'],
'StoreName' => $row['StoreName'],
'Sales' => $dateIndexedArray
);
}
$report[$storeIndex]['Sales'][$row['Date']] = $row['Sales'];
}
The $report array will look like:
[
'1112:Store1' => [
'StoreNbr' => 1112,
'StoreName' => 'Store1',
'Sales' => [
'2016-01-16' => 115.09,
'2016-01-17' => 81.00
]
],
'1113:Store2' => [
'StoreNbr' => 1113,
'StoreName' => 'Store2',
'Sales' => [
'2016-01-16' => 112.44,
'2016-01-17' => 56.61
]
]
]
Update:
If you need all data to be in one row for each store you can change the code to:
$report = array();
while($row = $stmt2->fetch(PDO::FETCH_ASSOC)){
$storeIndex = $row['StoreNbr'] . ':' . $row['StoreName'];
if (!isset($report[$storeIndex])) {
$report[$storeIndex] = $dateIndexedArray;
$report[$storeIndex]['StoreNbr'] = $row['StoreNbr'];
$report[$storeIndex]['StoreName'] = $row['StoreName'];
}
$report[$storeIndex][$row['Date']] = $row['Sales'];
}
The resulting array will look like:
[
'1112:Store1' => [
'StoreNbr' => 1112,
'StoreName' => 'Store1'
'2016-01-16' => 115.09,
'2016-01-17' => 81.
],
'1113:Store2' => [
'StoreNbr' => 1113,
'StoreName' => 'Store2',
'2016-01-16' => 112.44,
'2016-01-17' => 56.61
]
]
Update 2: To get the total sales per store you can use WITH ROLLUP
SELECT StoreNbr, StoreName,Date, SUM(`Sales`) AS Sales
FROM sales_tbl
WHERE (Date BETWEEN '2016-01-10' AND '2016-05-10')
GROUP BY StoreNbr,StoreName,Date WITH ROLLUP;
$report = array();
while($row = $stmt2->fetch(PDO::FETCH_ASSOC)){
if ($row['StoreName'] === null) {
// Skip this row.
// It contains total sales grouped by StoreNbr
// (or not grouped if StoreNbr === null).
continue;
}
$storeIndex = $row['StoreNbr'] . ':' . $row['StoreName'];
if (!isset($report[$storeIndex])) {
$report[$storeIndex] = $dateIndexedArray;
$report[$storeIndex]['StoreNbr'] = $row['StoreNbr'];
$report[$storeIndex]['StoreName'] = $row['StoreName'];
}
if ($row['Date'] === null) {
// This row contains total sales grouped by StoreNbr & StoreName
$report[$storeIndex]['TotalSales'] = $row['Sales']
} else {
$report[$storeIndex][$row['Date']] = $row['Sales'];
}
}
Please note that i've never used WITH ROLLUP. So you might need to adjust the code.

Categories