PHP processing data and create array - php

I have a mysql query with result like this:
ID | index | Mapping index | Date
1 | 27 | value27 | 2019-04
2 | 28 | value28 | 2019-05
3 | 28 | value28 | 2019-05
4 | 32 | value32 | 2019-07
5 | 32 | value32 | 2019-05
The results should be prepared to display stacked charts. As result i need in php:
// array to display google chart
['2019-04', 1, 0, 0,],
['2019-05', 0, 2, 1,],
['2019-07', 0, 0, 1,],
// explanation
ID | value27 | value28 | value 32 | Date
1 | 1 | 0 | 0 | 2019-04
2 | 0 | 2 | 1 | 2019-05
2 | 0 | 0 | 1 | 2019-07
This is my php script:
$preparevar = array();
foreach($data["timechart"] as $date){
array_push($preparevar,[$date->date, $date->count , '\''.$date->repcontent.'\'' ]);
}
$googleChartArray = array(); //Use this array to group the results using date.
foreach( $preparevar as $d ) {
$date = $d[0];
$value = $d[1];
if( !isset( $googleChartArray[$date] ) ) {
$googleChartArray[$date] = array( "'". $date. "'" ); //Date needs to be enclosed in quote.
}
$googleChartArray[$date][] = $value;
}
$f = array(); //Format the above array to split value in a comma separated format.
foreach( $googleChartArray as $g ) {
$f[] = implode( ',' , $g );
}
$json_out = json_encode(array_values($googleChartArray));
The problem with this format is, that the zero values will be ignored:
[
['2019-04',1],
['2019-05',2,1],
['2019-07',1]
]
should be:
[
['2019-04',1,0,0],
['2019-05',0,2,1],
['2019-07',0,0,1]
]
Here an example of $data["timechart"]:
array(11) {
[0]=>
object(stdClass)#43 (14) {
["id"]=>
string(2) "46"
["index"]=>
string(2) "31"
["index2"]=>
string(1) "0"
["keynr"]=>
string(2) "31"
["repcontent"]=>
string(41) "Value31"
["count"]=>
string(1) "1"
["date"]=>
string(7) "2007-06"
}
And here an example of my query. I canĀ“t use SUM(CASE) for example beacause index are variable.
SELECT
orders.id,
positions_list.index,
RepK.keynr,
RepK.content AS repcontent,
RepK.p_company,
COUNT(positions_list.index) AS count,
DATE_FORMAT(orders.date_placement, '%Y-%m') AS date
from orders
JOIN tools
ON tools.id=orders.tool_id
JOIN positions_list ON positions_list.order_id = orders.id
LEFT JOIN repkey as RepK
ON RepK.keynr=positions_list.index
AND RepK.p_company=orders.comp_id
WHERE
tools.id =:id
AND RepK.keynr IS NOT NULL
group by DATE_FORMAT(orders.date_placement, '%Y-%m'),positions_list.index

MySQL doesn't currently offer variable width pivots, so you can either:
make two queries, the first to collect the unique repcontent columns, then build a second query to implement a pivot technique by writing a SELECT clause with dynamic CASE WHEN statements for each column or
make one query, and let php prepare the results (this can be scripted up in a few different ways, but I'll recommend this one)
Code: (Demo)
$resultSet = [
['repcontent' => 'Value 27', 'date' => '2019-04'],
['repcontent' => 'Value 28', 'date' => '2019-05'],
['repcontent' => 'Value 28', 'date' => '2019-05'],
['repcontent' => 'Value 32', 'date' => '2019-07'],
['repcontent' => 'Value 32', 'date' => '2019-05'],
];
$columns = array_unique(array_column($resultSet, 'repcontent'));
$lookupKeys = range(1, count($columns));
$lookup = array_combine($columns, $lookupKeys);
$defaults = array_fill_keys($lookupKeys, 0);
foreach ($resultSet as $row) {
if (!isset($result[$row['date']])) {
$result[$row['date']] = array_merge([$row['date']], $defaults);
}
++$result[$row['date']][$lookup[$row['repcontent']]];
}
echo json_encode(array_values($result));
Output:
[["2019-04",1,0,0],["2019-05",0,2,1],["2019-07",0,0,1]]
For simplicity, generate a result set as an array of arrays.
Extract the unique repcontent values
Generate an array with values ranging from 1 to the unique repcontent count
Forge a lookup array consisting of #1 as keys and #2 as values -- this will determine where each "count" will stored when looping later
Create a default array consisting of #2 as keys and zeros as values
Now, loop through the result set and if a given row has a repcontent value which is encountered for the first time, create a new row in the output array using the date as the first element and the elements from #4 to follow.
Unconditionally, add 1 to the row's column that corresponds with with the repcontent value
If you don't quite understand why any of the variables ($columns, $lookupKeys, $lookup, $defaults) are generated or what they contain, call var_export() on my variables before entering the loop -- that should clear up any confusion.
I have a feeling that I could refine your query, but I won't venture a guess without having some realistic sample data to play with.
I don't see why you would need to add additional quotes to your json for the Google chart to work. If the chart doesn't render without the additional quotes, this is probably a symptom that you are passing the php variable to javascript in an improper fashion.
p.s. I see that you some development with Joomla, if this is a Joomla script and you are not able to craft your query with Joomla's query building methods, please post your best effort on Joomla Stack Exchange and I'll see if I can help.

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.

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.

Regex from rules stored in a database for ASTERISK using PHP

I'm trying to create a kind of REGEX with dynamic variables
with PHP using some pre-defined values stored in a Database table, in order to
validate a Dial String from Asterisk.
The database has
a table for outgoing rules, which each user may create to be applied to his/her extension.
Variables in PHP can be like this:
$N = '[23456789]';
$Z = '\d*'; //(any digit, any occurrence)
$X = '[0-9]';
$x = '[0-9]';
numbers in the rule will be considered as number itself
symbols in the rule will be considered as symbol itself (only * and # will be accepted in the database as symbols)
The table is something like this:
+----+-----------+-------+-------------+------------+----------+-----------+--------+--------+
| id | extension | order | description | ruledigits | ruletype | subtract | prefix | suffix |
+----+-----------+-------+-------------+------------+----------+-----------+--------+--------+
| 1 | 1005 | 1 | | 9XX | Block | null | null | null |
| 2 | 1005 | 2 | | 302NXXXXXX | Mod | null | 1 | null |
| 3 | 2005 | 1 | | 00Z | Mod | 2 | 011 | null |
+----+-----------+-------+-------------+------------+----------+-----------+--------+--------+
So, if 1005 Extension DIALS 908 (no more digits), that call should be blocked
according to the Action field. if 1005 Extension Dials 3025555555 (no more
digits), that Call will be prefixed with number 1.
Iif 1005 Extension Dials 00325698289115 (international), that Call will NOT
me modified according to Rule # 3, since that rule ONLY applies to EXT 2005, so
the CALL will be sent as it was Dialed to the Server.
If that same last Call was made by 2005, the number would MATCH the rule,
since it begins with 00 and Z is ANY DIGIT ANY OCURRENCE. So, the dialed number will be Subtracted "2" digits from the beginning, and then prefixed with "011" before being sent to the trunk.
The important field here I think is ruledigits (and of course extension),
which will be used by PHP for the ARRAY of rules per EXT. then the action will
be read only if the condition was meet.
And the condition something like this, although this one only represents just
1 rule and case:
if(preg_match("/^1$N$X$N(555)$Z/", $number))
{
// $number format matches
echo "yes"; // apply the action according the table
}
else
{
echo "no"; // no action applied, proceed with the call.
}
I would need condition(s) that must be created on the fly as soon as the actual
Call (requesting the access to the database through a php script) finds that
there are not just 1, but some rules created for THAT extension individually.
How can I make or design a General function that can Access the database "rules"
table, and ARRAY the group of all the conditions created under the caller
extension in order to apply them to the actual call?
Note: The field
"ruledigits" on the table "rules" will only accept the following characters:
N, Z, *, #, or Any number.
Here's how I would personally approach using MySQL data to build Regex patterns for checking data:
//Simplified array
$dbResults = array(
array(
'id' => 1,
'extension' => 1005,
'order' => 1,
'description' => 'rule 1',
'ruledigits' => '9XX',
'prefix' => null,
'ruletype' => 'Block'
),
array(
'id' => 2,
'extension' => 1005,
'order' => 2,
'description' => 'rule 2',
'ruledigits' => '302NXXXXXX',
'prefix' => 1,
'ruletype' => 'Mod'
),
array(
'id' => 3,
'extension' => 2005,
'order' => 3,
'description' => 'rule 3',
'ruledigits' => '00Z',
'prefix' => '001',
'ruletype' => 'Mod'
)
);
$regexParts = array(
'N' => '[2-9]'
,'Z' => '\d*'
,'X' => '\d'
,'x' => '\d'
);
//Static test vars
$userExt = 1005;
$dialTests = array('00325698289115','908','3025555555');
echo 'Testing user extension: '.$userExt;
echo '<br /><br />';
//This loop is for testing purposes only, the contents are all the live system would use
foreach($dialTests as $testNo)
{
$actionTaken = 'None';
//By default, if nothing happens, make sure the final number is the original one we saw
$finalNo = $testNo;
foreach($dbResults as $row)
{
if($userExt != $row['extension']) continue;//If it's not the right extension, skip the rest of this loop iteration's code and move on to the next row
$regex = '';
$tokens = str_split($row['ruledigits']);//Turn the string into an array, so we can parse each character individually
foreach($tokens as $token)
{
if(isset($regexParts[$token])) $regex .= $regexParts[$token];//If the letter has a special meaning, use that
else $regex .= $token;//else just throw that exact letter/number in
}
if(preg_match('#^'.$regex.'$#',$testNo)>0)
{
$actionTaken = $row['ruletype'];//Log what action has been taken
if($actionTaken=='Mod')
{
$finalNo = $row['prefix'].$testNo;//Do the mod action
}
else if($actionTaken=='Block')
{
$finalNo = false;//Nullify the final number, so we know to block the call later on
}
}
}
//Here we just dump some info for testing purposes
echo $testNo.': Action taken = '.$actionTaken;
if($actionTaken=='Block') echo ' - Call terminated.';
if($actionTaken=='Mod') echo ' - New number = '.$finalNo;
echo '<hr />';
}
View the output demo on phpfiddle (click Run/F9)
You can use asterisk realtime architecture and correct view to match extensions table.
Or you can use mysql query with REGEXP/RLIKE construction.
https://dev.mysql.com/doc/refman/5.1/en/regexp.html
You can easy change asterisk regexp to general regexp by using simple rewrites like
'.' -> '.*'
'X' -> '[0-9]'
etc

Inserting multiple arrays into different rows

I'm collecting input fields as an array to be inserted into the different rows in the database. However it only inserts the first row of the array.
Please kindly assist. I have some issues handling arrays. I don't know how to construct the query and I've tried to search online and I couldn't get enough help. I'll really appreciate help with the query.
Here is my HTML code:
<input type = "text" class = "form_element" name = "wat_office_type[]" />
<input type = "number" name = "wat_office_price[]" class = "form_element" />
while I use jQuery to add more input boxes.
Here is my php:
$wat_office_type_post = $_POST['wat_office_type'];
$wat_office_price_post = $_POST['wat_office_price'];
$wat_office_type = array();
$wat_office_price = array();
foreach ($wat_office_type_post as $type) {
if (!empty($type))
$wat_office_type[] = $afrisoft->antiHacking($type);
}
foreach ($wat_office_price_post as $post) {
if (!empty($post))
$wat_office_price[] = $afrisoft->antiHacking($post);
}
I want to insert into 2 separate rows and achieve something like this:
--------------------------------------------
| Pk | wat_office_type | wat_office_price |
--------------------------------------------
| 1 | executive office | 1000 |
--------------------------------------------
| 2 | Training room | 4000 |
--------------------------------------------
| 3 | Events room | 5000 |
--------------------------------------------
I'll kindly appreciate if I can get help with the insert query (mysql,php) on how I can insert all values of the first array in one column, and values of the second array into the second column, while each are matching d numbers of arrays supplied.
Thanks.
If I understand correctly, you need to combine the two arrays into something like
$list = array(
array('pk' => 1, 'type' => 'executive', 'price' => 1000),
array('pk' => 2, 'type' => 'training room', 'price' => 4000),
array('pk' => 3, 'type' => 'events room', 'price' => 5000)
);
then you can call a foreach loop to query the database with each element, like
foreach($list as $key => $value) {
$sql = "INSERT INTO table_name (pk, type, price) VALUES (?, ?, ?)";
$query = $this->db->prepare($sql);
$params = array($value['pk'], $value['type'], $value['price']);
if($query->execute($params)) {
return true;
} else {
return false;
}
}
so instead doing two foreach loops and putting results in different arrays, you could use one foreach loop and create an associative array for each set of results (I assume the count on both of your arrays is the same?) as I mentioned above.

Merge meta table with main table

I have two tables, a main one, and one that supports the main table, very very similar to what wordpress has, posts and posts_meta.
Main table:
id
title,
content
id | title | content
1 | one | content one
2 | two | content two
Meta table:
id
item_id
key
value
id | item_id | key | value
1 | 1 | template | single
2 | 1 | group | top
1 | 2 | template | page
2 | 2 | group | bottom
And my goal is, in the end, have an array with the data from the main table, merged with the meta table. example:
$data = array(
array(
'id' => 1,
'title' => 'one',
'content' => 'content one',
'template' => 'single',
'group' => 'top'
),
array(
'id' => 2,
'title' => 'two',
'content' => 'content two',
'template' => 'page',
'group' => 'bottom'
)
);
What is the best way to achieve this in a way that preforms good?
I am using PDO to connect to my database, and how Im doing right now is, I first query the data on the first table, and then for each result, i query the meta table, I use prepared statements for this, since it's suposed to be fast, but even so, it's harming the performance of my script.
Thank you
Instead of querying meta table for each result from first query
you should extract the ids from the first result:
$rows = q('SELECT * FROM posts');
$byIds = [];
foreach ($rows as &$row)
{
$byIds[$row['id']] =& $row;
}
and run second query:
$rows2 = q('SELECT * FROM posts_meta WHERE item_id IN (' . implode(',', array_keys($byIds)) . ')');
Then loop the results in PHP and merge with first query results.
foreach ($rows2 as $row2)
{
$byIds[$row2['item_id']][$row2['key']] = $row2['value'];
}
You have your merged results in $rows variable now:
var_dump($rows);
This way you will have only 2 db requests.
Please note that i have used $byIds as array of references so i dont have to search row with specific id in second loop. This way order of elements in $rows are preserved.

Categories