in php Foreach loop moves to the wrong key - php

I have the following array structure
jobs = {70811 ={....invoices = { A="A",...},...},...}
Then I have two nested loops. The outer nested loop seeks each job # in this case 70811. The inner loop then seeks each invoice listed for the job.
in each loop I query one of the tables
foreach ($this->p['jobs'] as $job => $value) {
$sql = 'select concat(sum(i.`AMOUNT`))as `amount`, ...
from `invoice` i
where i.`JOBID` = '.$job.'
group by i.`tracknum` = "'.$this->cTrackKey.'"';
$cmeJobEdits = 0;
if ($result = $db->query($sql))
{
if ($result->num_rows > 0)
{
$this->budget = mysqli_fetch_all( $result, $resulttype = MYSQLI_ASSOC );
$subTot = 0;
for ($i=0; $i<count($this->budget);$i++)
{
$subTot += $this->budget[$i]['amount'];
}
...
unset($i);
$xsql = 'update `cmejob` set `balance` = '.$this->budget[0]['balance'].',... where `jobid` = '.$job;
if (!$result = $db->query($xsql))
{
throw new Exception("Query to update budget failed!");
}else{
$cmeJobEdits++;
}
unset($xsql);
}
}else{
throw new Exception("Query to get billed amounts for this job failed! "`enter code here`);
}
unset($sql);
unset($value);
}//end loop updating cme jobs
My problem is this the outer query where the $job variable is not the next job # but the nested array key "invoices" as shown below.
As shown in the code I tried unsetting the query and key variables in the loops with no success.
select concat(sum(i.`AMOUNT`))as `amount`, ...
from `invoice` i
where i.`JOBID` = invoices //this is the value of the variable $job
group by i.`tracknum` = "'.$this->cTrackKey.'"';

Here is the var_dump($this->p['jobs'] of the values as you requested. Is this how you prefer seeing it
array(1) { [84528]=> array(6) { ["thisBudget"]=> string(4) "0.00" ["thisBilled"]=> string(9) "44,633.80" ["thisSum"]=> string(9) "28,003.00" ["thisBalance"]=> string(10) "-72,636.80" ["invoices"]=> array(1) { ["A"]=> string(1) "A" } ["DORMANT"]=> string(0) "" } }

The problem has been resolved.
As usual the error occurred in code not shown. The code above added an 'invoices' element to the $this->p['jobs'] array when it should have been added somewhere else.
now the loop works as described.

Related

How to convert a database row into a JSON string of values only in PHP?

I am trying to provide a count of certain sizes of lockers. My SQL statement is:
$sql = "SELECT Concat(`Height`, 'x', `Width`, 'x', `Depth`) as Locker, count(*) from lockers GROUP BY `Height`, `Width`, `Depth` ";
a var_dump in a loop of the rows gives me the following (many more rows are possible):
array(2) { ["Locker"]=> string(8) "15x30x45" ["count(*)"]=> int(6) }
array(2) { ["Locker"]=> string(8) "45x30x45" ["count(*)"]=> int(4) }
I want to obtain a JSON string that looks like this:
{
"SizeList": {
"15x30x45": 6
"45x30x45": 4
}
}
I have tried a lot of different methods (including converting it into an object, but I can't get the value of the size as an index. For example, I get different variations of:
[0]=> string(31) "{"Locker":"15x30x45","count(*)":6}"
[1]=> string(31) "{"Locker":"45x30x45","count(*)":4}"
Any help appreciated...
You could just manually create the object like this:
$sizeList = new stdClass();
foreach ($results as $row) {
$sizeList->{$row['Locker']} = $row['count(*)'];
}
echo json_encode(array('SizeList' => $sizeList));

How can I extract data in an ordered way from a php array?

I'm not a php ninja, so please bear with me on my query. I have the following code (in simplified example format here as a lot of other irrelevant stuff is happening in the real code):
Database connection already successfully defined and handled by an include at the start of the php file.
// Define our $now value to compare times with
$now = time();
// Define the numerical timestamps required for calculating stuff
$min1st = 5140800; //59.5 days
$max1st = 5227200; //60.5 days
$min2nd = 7732800; //89.5 days
$max2nd = 7819200; //90.5 days
$delmar = 10368000; //120 days
// Get each relevant entry from the key table
try {
$stmt = $conn->prepare("SELECT * FROM keyTable WHERE `status` = 'neutral' ORDER BY `lastModified` ASC");
$stmt->execute();
$result = $stmt->fetchAll();
} catch(PDOException $e) { catchMySQLerror($e->getMessage()); }
// Set up some instance counters
$a = 0;
$b = 0;
$c = 0;
// Create some arrays to pop the data into from the next stage so we can use it again later
$arrA = array();
$arrB = array();
$arrC = array();
So far so good. The next part is designed to discover which of the database results have a "lastModified" value set within a specified timeframe, using some data we set up near the top. This works too, fairly well (I think).
foreach($result as $value) {
// Lets find the ones that qualify for first email notification
if((($value['lastModified'] + $min1st) < $now) && (($value['lastModified'] + $max1st) > $now)) {
// Now only the entries that have been untouched for 60 days from today are listed here. Yay! Send out an email to the people responsible and remind them! Get user data...
try {
$stmt = $conn->prepare("SELECT * FROM users WHERE `uID` = :uid");
$stmt->bindValue(':uid', $value['uID']);
$stmt->execute();
$uRes = $stmt->fetchAll();
} catch(PDOException $e) { catchMySQLerror($e->getMessage()); }
// Add +1 to the incremental count
$a++;
// Collect some data for the log email
$arrA[] = $value['entryKey']."-"$value['entryID'];
$arrA[] = $value['entryName'];
$arrA[] = $value['entryTitle'];
$arrA[] = $value['dateStarted'];
$arrA[] = $value['lastModified'];
$arrA[] = $uRes[0]['title']." ".$uRes[0]['firstName']." ".$uRes[0]['lastName'];
$arrA[] = $uRes[0]['email'];
Then it will on to send each person that gets turned up in this if. The code for sending the emails out etc works perfectly, so no need to bore you all with that. The same process is repeated each time with other parameters - you can likely guess how that works by observing the other numerical timestamp values near the top. So lets go to the end of this (which is where the question lies, after the:
}
}
And now here I want to pull all the data out of $arrA (and also the respective other arrays $arrB and $arrC) so I can pop that all into a tidy table then mail it off to admin so they know what exactly happened when this was all run.
My issue is, I do not know how to extract in a usable way, $arrA. If I var_dump($arrA); I successfully get all the stuff that should be in there but I don't see a way to order it in a nice row by row method per loop performed. I tend to end up with this when performing a var_dump($arrA);:
array(14) { [0]=> string(15) "ABC-63" [1]=> string(36) "Fish and Chips" [2]=> string(33) "Cod Disposal" [3]=> string(22) "1447283057" [4]=> string(22) "1447286317" [5]=> string(21) "Mr Bob Smith" [6]=> string(22) "bob.smith#mail.com" [7]=> string(15) "XYZ-104" [8]=> string(23) "Blue Socks" [9]=> NULL [10]=> string(22) "1447286691" [11]=> string(22) "1447326523" [12]=> string(20) "Mrs Rosie Jones" [13]=> string(34) "r.jones#mail.com" }
Clearly there is output here for two data entries. I'd to know how to shape this into an output that would basically look like this:
<tr>
<td>ABC-63</td>
<td>Fish and Chips</td>
<td>Cod Disposal</td>
<td>1447283057</td>
<td>1447286317</td>
<td>Mr Bob Smith</td>
<td>bob.smith#mail.com</td>
</tr>
<tr>
<td>XYZ-104</td>
<td>Blue Socks</td>
<td>NULL</td>
<td>1447286691</td>
<td>1447326523</td>
<td>Mrs Rosie Jones</td>
<td>r.jones#mail.com</td>
</tr>
Table start and end would obviously be in place before and after the loop respectively.
How can I achieve this result from $arrA? Do I need to modify the way I am storing this data in array's earlier on, in some manner, in order to have some sort of easy way to manage this output near the end?
Thanks in advance.
If I understand correctly, you want to output elements of $arrA, 7 elements per line.
What you can do is iterate over $arrA, filling up a temporary array $line with the elements of $arrA. Every 7 elements, the line will be filled up. You can then do anything with it like echoing it to standard output our adding it to an email variable, and then reset the $line.
$line = [];
for ($i = 0; $i < count($arrA); $i++) {
$line[] = $arrA[$i];
if ($i % 7 == 6) { //line is filled up with 7 elements
echo implode($line, " | "); //output elements of line separated with pipe char
$email .= implode($line, " | ") . "\n"; //add the line to email body
$line = []; //reset line
}
}
This one's gonna help:
foreach($arrA as $index=>$value){
echo($value.' |');
}
Styling is up to you. But if you need further help, keep me updated.
Thanks to #quentinadam I was inspired to try this. It's not the angle I was thinking of using but it does work.
echo "<table border=\"1\">\n<tbody><tr>\n";
$i = 0;
foreach($arrA as $thing) {
if($i == 7) {
echo "</tr>\n<tr>\n";
}
echo "<td>".$thing."</td>\n";
$i++;
if($i == 8) {
$i = 1;
} else {
$i = $i;
}
}
echo "</tr></tbody>\n</table>\n\n";
I'd up-vote quentinadam for being on the right track but cannot yet up-vote.

Recursive PHP function gets only first values

I'm having an issue with below code. I'm trying for some time now, and I believe I must be blind and I do not see something obvious. Can you point at this obvious thing?
Function ought to get all categories and it's names. Takes IDs correctly, but names only for first category and first subcategory.
function collect($id, $current = null) {
global $db_1;
$s = "SELECT category.id as id, category_i18n.name as name from category,category_i18n WHERE category.parent_id= '$id' AND category.visible = '1' AND category_i18n.culture='en' AND category.id = category_i18n.id ORDER BY category.order_asc ASC";
$r = mysql_query($s, $db_1);
$row = mysql_fetch_array($r);
$children = array();
if (mysql_num_rows($r) > 0) {
while ($row) {
$children['name'] = $row['name'];
$children[$row['id']] = collect($row['id']);
}
}
else
{
$children['name'] = $row['name'];
}
return $children;
}
EDIT:
EDIT2:
After changes I'm getting array like this:
array(3) {
["name"]=>
string(9) "Cat"
["id"]=>
string(5) "10404"
["children"]=>
array(3) {
["name"]=>
string(10) "Subcat"
["id"]=>
string(5) "10410"
["children"]=>
bool(false)
}
}
I think it is not problem with mysql query, as in phpmyadmin it works and shows all data properly...
UPDATED :
It seems that you have only 1 level of subdirectory. Your function does this :
1) First it fetches the name and id of the given category.
2) It uses the obtained id to fetch the name of the subcategory which has parent_id = the previously fetched id.
3) Here comes the problem. Due to recursion it again calls collect() with the id of this subcategory. But there is no subcategory for this, so it returns null.
That is,
1st Parent (id1) -> Subcategory(id2) -> Subcategory(id2) and so on.
You can use this to get as many levels of categories as you want :
function collect($id, $current = null) {
global $db_1;
$s = "SELECT category.id as id, category_i18n.name as name from category,category_i18n WHERE category.parent_id= '$id' AND category.visible = '1' AND category_i18n.culture='en' AND category.id = category_i18n.id ORDER BY category.order_asc ASC";
$r = mysql_query($s, $db_1);
$temp = array();
if(mysql_num_rows($r) > 0)
{
while($row = mysql_fetch_array($r))
{
$temp[$row['id']]['name'] = $row['name'];
$temp[$row['id']]['children'] = collect($row['id']); //Get further children (Recursive)
if(!$temp[$row['id']]['children']) {continue;} // If FALSE is returned, means that this category has no sub categories. Move onto next one.
}
}
else {return FALSE;} //No rows present, return FALSE
return $temp;
}
This code is not tested. You will get an array in the following format
$parent[parent_id] -> [name]
-> [children] -> FALSE (if subdirectories are not present)
-> [children_id] #1 -> [name]
-> [children]
-> [children_id] #2 and so on.
UPDATE : you were getting only the last result, because I forgot one thing. The $temp[] array was getting overwritten every time in the loop.

PDO not inserting correct data

I have this function, which I pass a table name, and data into.
public function quickInsert($table, $data) {
$keys = array_keys($data);
$keysStr = '`'.implode('`,`', $keys).'`';
$valStr = ':'.implode(',:', $keys);
$Sql = "INSERT INTO `$table` ($keysStr) VALUES ($valStr)";
dump($Sql);
$st = $this->connection->prepare($Sql);
foreach ($data as $k => $v) {
dump(':'.$k.' - '.$v);
$st->bindParam(':'.$k, $v);
}
//dump($st->execute());
}
The output from the dumps is
string(114) "INSERT INTO `users` (`email`,`password`,`first_name`,`last_name`) VALUES (:email,:password,:first_name,:last_name)"
string(21) ":email - my#email.com"
string(140) ":password - myHashedPasswordString"
string(17) ":first_name - Tom"
string(17) ":last_name - Hart"
However, when I execute the query, the data in all the fields is the last name (In this case "Hart").
What have I gotten wrong?
$v is overwritten on each iteration of the loop, when it is finished and $st->execute() runs it only contains the last value. Try $st->bindValue() instead of $st->bindParam().

Retrieve database information and check results for certain conditions

I am looking for the most efficient method to do the following...
I have a table with three fields: id, eventid and movetype - and I have the following code to query the database for all entries with a matching eventid :
$get_results = mysql_query("SELECT * FROM table WHERE eventid='$eventid'");
while($row = mysql_fetch_array($get_results)){
// store results in array
$move_type_array = explode(" ", $row['movetype']);
}
Please note: movetype can only be three values: Internal, Outgoing, Incoming
Using a set of dummy data, var_dump($move_type_array) could output:
array(1) { [0]=> string(8) "Internal" } array(1) { [0]=> string(8) "Internal" }
Another example of an output would be:
array(1) { [0]=> string(8) "Internal" } array(1) { [0]=> string(8) "Incoming" } array(1) { [0]=> string(8) "Outgoing" }
I then need to check the output to see if the following conditions are met:
Does the array contain Internal twice?
If the array only contains Internal once, then does the array also contain Incoming and Outgoing?
If either of the conditions are met then a message should be displayed telling them which condition has been met, otherwise a message should tell them that the conditions have not been met.
I have tried using a number of PHP functions such as in_array(), and also tried storing the data in a string and using preg_match() however I was unsuccessful in both methods.
Try this:
$result = mysql_query("
SELECT SUM(IF(movetype = 'Internal', 1, 0)) AS internal,
SUM(IF(movetype = 'Outgoing', 1, 0)) AS outgoing,
SUM(IF(movetype = 'Incoming', 1, 0)) AS incoming
FROM table
WHERE eventid = $eventid
GROUP BY eventid
");
$count = mysql_fetch_array($result);
if ($count['internal'] >= 2 || ($count['internal'] == 1
&& $count['outgoing'] >= 1
&& $count['incoming'] >= 1)) {
echo 'Yes';
} else {
echo 'No';
}
// 1. Don't use '*' - fetch only required fields!
// 2. Don't use mysql_* functions - they are obsolete! Use PDO or MySQLi instead.
$get_results = mysql_query("SELECT `movetype` FROM table WHERE eventid='$eventid'");
while($row = mysql_fetch_array($get_results)){
// Count the values
$arr = array_count_values(explode(" ", $row['movetype']));
// Check them here
if(isset($arr['Internal'])) {
if($arr['Internal'] === 2)
echo ' Internal: 2'.PHP_EOL;
else if($arr['Internal'] === 1
&& isset($arr['Incoming'])
&& isset($arr['Outgoing']))
echo ' Internal: 1, Incoming and Outgoing'.PHP_EOL;
}
}

Categories