• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 333
  • Last Modified:

Add value to end of associative PHP array

I have some questions about this code, please.  Will the value of $_POST["id"] be overwritten with each loop?  If so, that's not what I want.  I'd like to end up with an array that has as many elements as there are loop iterations which correspond to each respective value of $id.  My thinking was : $_POST["id"][]= $id; but this returned an error.  Thanks.

<?php 
while ($row = mysqli_fetch_assoc($result)) {
	$id = $row["transaction_id"];


$_POST["id"]= $id;
?>

Open in new window

0
LB1234
Asked:
LB1234
  • 9
  • 8
  • 8
  • +1
18 Solutions
 
Ray PaseurCommented:
$_POST["id"][]= $id;
Looks right to me.  What was the error (hint, hint)?
0
 
gr8gonzoConsultantCommented:
1. Yes, the value would be overwritten.

2. Your thinking actually CAN work, but it likely threw a WARNING, not an error, because the first time it tried to use $_POST["id"] like an array, PHP found that it was not an array, so it had to convert it. You can skip the warning by putting the data into an array. Stick with me for a second.

3. It's generally a bad idea to modify $_GET or $_POST or any of the "magic" arrays. When you do that, it becomes difficult to tell what actually came from the POST data - you essentially are losing information. It's better to simply treat these as read-only and use a separate array for holding data:

<?php 
$arrIDs = array();
while ($row = mysqli_fetch_assoc($result)) {
	$id = $row["transaction_id"];


$arrIDs[] = $id;
?>

Open in new window

0
 
Ray PaseurCommented:
What @gr8gonzo said!  Code annotated with comments.  I've always thought it was a great design flaw in PHP to allow the request arrays ($_GET, $_POST, $_COOKIE) to be mutable.

<?php

// ALWAYS USE THE HIGHEST POSSIBLE LEVEL OF ERROR REPORTING
error_reporting(E_ALL);

// THIS CODE ASSUMES THAT THERE WAS A SUCCESSFUL QUERY AND THE VALUE IN $result IS A RESOURCE
// IF THIS ASSUMPTION IS INVALID THE CODE WILL FAIL
// http://www.php.net/manual/en/mysqli-result.fetch-assoc.php
while ($row = mysqli_fetch_assoc($result)) {

    // THIS CODE ASSUMES THAT THERE IS A RESULTS SET ELEMENT NAMED "transaction_id"
    // IF THIS ASSUMPTION IS INVALID THE CODE WILL FAIL
    $id = $row["transaction_id"];

    // THIS WOULD PROBABLY MUNG THE REQUEST VARIABLE BUT MIGHT FAIL IN A STRING-TO-ARRAY CONVERSION
    $_POST["id"][] = $id;
}

Open in new window

0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LB1234Author Commented:
Sorry guys, whenever I see anything other than the code I expect, I call it an error, even though it may actually only be a notice or a warning.  To test this out, I actually used different code (See below).

The warning was this:  Warning: Cannot use a scalar value as an array in C:\wamp\www\expenses\test.php on line 18

When I did a print_r, the number three was not part of the array, which is odd behavior if there was only a warning!

<?php


$_POST["id"] = 2;

$_POST["id"][] = 3;

print_r($_POST);

?>

Open in new window

0
 
Ray PaseurCommented:
PHP has three "error" levels in common use.

Notice: Probably means that something was omitted, such as a script that relies on an undefined variable.  Catastrophe may not ensue.
Warning: Probably means that something very bad happened, such as a script that used an external resource incorrectly.  Almost certainly will produce incorrect output.
Error: PHP cannot continue at all.
0
 
gr8gonzoConsultantCommented:
If there was already a $_POST["id"] value that contained a non-array value, then this would not work. You'd have to change it to an array before looping like this:

$_POST["id"] = array($_POST["id"]);
...loop...
0
 
gr8gonzoConsultantCommented:
(But again, use a new array, not _$POST).
0
 
LB1234Author Commented:
To simplify, i'm forgetting about the loop right now and just trying to add to the end of an associative array.  Still getting the scalar warning, and the value, 3, isn't being added to the array as expected. Should i be using array_push() or something?

<?php

$values = array();

$values["test"] = 2;

$values["test"][] = 3;

print_r($values);


?>

Open in new window

0
 
Ray PaseurCommented:
Learn about var_dump() and use it to visualize the data.  It makes it easy to see what is going on.  Shows more than print_r() will tell you!
http://www.php.net/manual/en/function.var-dump.php

In the instant case you will see that $values is an array.  And $values["test"] is an integer 2.  

When you try to append an array to an integer, PHP barks.

You might try it this way.  Or you might tell us a little more about the application and we might be able to suggest a well-known design pattern.

<?php
error_reporting(E_ALL);

// CREATE AN EMPTY ARRAY
$values = array();
var_dump($values);

// CREATE A "SUB-ARRAY" IN THE "test" ELEMENT OF THE ARRAY
$values["test"][] = 2;
$values["test"][] = 3;
var_dump($values);

Open in new window

0
 
Ray PaseurCommented:
As I try to think through the question a little more, I'm finding that my computer science antennae twitch at the concept of adding to the "end" of an associative array.  Let's go back to the nature of associative arrays.  They have named keys, not numeric keys.  $arr['name'] will always find the 'name' element, no matter what the order of the elements of the array.  Now consider a numerically indexed array.  $name[1] will always find the element at numeric position 1 (assuming there is a key numbered 1).

Associative arrays are useful for things like rows from query results sets.  Numeric arrays are useful for things like data sets of unpredictable size, where the names of the data elements do not matter.

PHP has a collection of functions for dealing with arrays, including sorting and counting.  Here are the man page references you need to understand PHP arrays.
http://www.php.net/manual/en/language.types.array.php
http://www.php.net/manual/en/language.operators.array.php
http://www.php.net/manual/en/array.sorting.php
http://www.php.net/manual/en/ref.array.php

There is one other thing you need to know about arrays.  PHP arrays are big, much bigger than any intuitive size.  To illustrate this, create three scalar variables in a script and examine the memory usage.  Then create an array with the same three scalar variables in each of the three positions of the array.  You'll quickly see the difference.  For many things this may not matter, but it's worth knowing that PHP arrays get big, fast, and can cause memory issues.
0
 
LB1234Author Commented:
This is a related question, which is why i'm bothering with this at all.  When I hit the submit button on a form, what information exactly is the processing page getting?  It's of course getting everything specified by the name="" elements, but is it getting anything else?
0
 
gr8gonzoConsultantCommented:
It gets those name=... elements, plus any query string variables (which will show up in $_GET), and any environmental / header variables. You can always install Firebug or Fiddler and watch the data that the browser sends to the server. Typically, the header variables aren't often used by processing page, but they're there.
0
 
Ray PaseurCommented:
Everything in the HTML form with a name= attribute will be submitted to the action script.  This will be true for scalar elements and array elements.  All of these will become part of the http request and will show up in $_GET or $_POST.  Except...

Empty checkboxes and radio buttons will not be submitted at all.  They are not present and "empty" - they are undefined.  More on this, here.

Anything that JavaScript did to the request elements is not included in this answer.
0
 
LB1234Author Commented:
Gr8 said: (But again, use a new array, not _$POST).

I was trying to avoid having to create a session variable to get the value of $id to the processing page (by using the $_POST).  So just use a new array and a session superglobal and that should do the trick, yes?
0
 
gr8gonzoConsultantCommented:
I'm not sure I follow. Why would you use a session variable?

You're dealing with $_POST data, which means your code is already on the processing page. Then you're trying to add other IDs from the database into the array, but this is still on the processing page, so why would you need sessions?
0
 
Dave BaldwinFixer of ProblemsCommented:
If you have multiple items with the same name, they will all be submitted.  However, since PHP considers "name=value" to be an assignment, you will only see the last one because each one will replace the one before it.  You can overcome this by using name="name[]" which the array syntax.  PHP will then put each value into the array name[] in the order they are received.

Note that other languages like ASP deliver multiple values as a list which is different than what PHP does.
0
 
LB1234Author Commented:
Ok getting back to the original code, I need to do this because I need to get the value of $id back into an array so I can use the corresponding values to insert the data back into the database in the appropriate field.


while ($row = mysqli_fetch_assoc($result)) {
	$id = $row["transaction_id"];
?>


  <tr>
  <!-- ID -->				<td class="td-center"><?php echo $id; $_POST["id"][] = $id; ?></td>
  <!-- Date-->   			<td class="td-center"><?php echo date("n-d-y", strtotime($row["date"]));?></td>
<!-- Amount-->   			<td class="td-center"><?php echo "$" . number_format($row["amount"], 2)?></td>

Open in new window

0
 
Dave BaldwinFixer of ProblemsCommented:
To me that doesn't make any sense.  $_POST["id"][] = $id; will be new every time the page is loaded.
0
 
LB1234Author Commented:
I hate programming!
0
 
Ray PaseurCommented:
I think there must be a larger back-story for this question.  If you can tell us a little more about what the application is supposed to do we may be able to offer a little more useful help.  It feels like we are dealing with fragments of a larger question.
0
 
gr8gonzoConsultantCommented:
Can you show us the full script?
0
 
LB1234Author Commented:
Ok Guys, here's the full script.  It's a table that pulls info from a database, and will allow the user to make edits and then write it all back to the database.  I need to for each row of the table to have the appropriate ID number so it's loaded into the database correctly by ID.  Using session instead of POST did solve my problem, though  I know it's not ideal.

<?php session_start();  ?> 
<?php include ("db_connection.php");?>
<?php include ("includes/layout/header.php");?>

<div id="table">
<form action="process_table.php" method="post">

<table class="gridtable" width="95%" border="0" cellspacing="0" cellpadding="2">
 
<tr>
        <th width="3%" scope="col">id</th>
        <th width="3%" scope="col">Date</th>
        <th width="2%" scope="col">Amount</a></th>
        <th width="5%" scope="col">Vendor</th>
        <th width="8%" scope="col">Description</th>
        <th width="1%" scope="col">Bind Cat.</th>
        <th width="5%" scope="col">Category</th>
        <th width="7%" scope="col">Notes</th>
        <th width="1%" scope="col">Bind Notes</th>
        <th width="5%" scope="col">Last Updated</th>
        <th width="5%" scope="col">Updated By</th>
        <th width="1%" scope="col">Done</th>
</tr>
 
 
 <?php
 
 
 
$query = "SELECT * FROM transaction LIMIT 10";

$result = mysqli_query($connection, $query);

while ($row = mysqli_fetch_assoc($result)) {
	$id = $row["transaction_id"];
?>


  <tr>
  <!-- ID -->				<td class="td-center"><?php echo $id; $_SESSION["id"][] = $id; ?></td>
  <!-- Date-->   			<td class="td-center"><?php echo date("n-d-y", strtotime($row["date"]));?></td>
<!-- Amount-->   			<td class="td-center"><?php echo "$" . number_format($row["amount"], 2)?></td>
<!-- Vendor-->   			<td><?php echo $row["vendor"]?></td>
<!-- Description-->   		<td><?php echo $row["description"]?></td>
<!-- Bind Category-->    		<td class="td-center"><input name="bind_category[]" type="checkbox" checked="checked"></td>
<!-- Category Drop Down-->  

<td class="td-center">
<?php 

$category_options = "";

$category_query = "SELECT * FROM category";

$category_result_set = mysqli_query($connection, $category_query);
?>

<?php 
while ($category_row = mysqli_fetch_assoc($category_result_set)) {
		$selected = ($category_row["category"] == $row["category"] && $row["done"]==1) ? "selected":"";
		$category_options .=  sprintf("<option value = '%s' '%s' >%s</option>", $category_row['category'], $selected, $category_row['category']);
	}
	
echo "<select name=\"category[]\">" . $category_options . "</select>";

?>

</td>
<!-- Notes-->   			<td><textarea name="notes[]" id="notes">test</textarea></td>
<!-- Bind Notes-->			<td class="td-center"><input name="bind_notes[]" type="checkbox" checked="checked"></td>
<!-- Last Updated-->  		<td class="td-center"><?php echo date("n-d-y")?></td>
<!-- Updated By-->			<td class="td-center"><?php echo "temp"  ?></td>
<!-- Done-->      			<td class="td-center"><input name="done[]" type="checkbox" checked="checked"></td>
  </tr>
<?php
}
?>

</table><br>

<input name="submit" type="submit"  align="right">

</form>
</div>



<?php include ("includes/layout/footer.php");?>

Open in new window

0
 
Ray PaseurCommented:
Ahh, the veil is lifted!

You might want to have a look at this.  It teaches the basic design pattern for PHP / MySQL table maintenance scripts.
http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/A_12335-PHP-and-MySQLi-Table-Maintenance.html
0
 
gr8gonzoConsultantCommented:
1. I -think- you might be misunderstanding the flow of data here. $_POST contains the -result- of the data. You can change it, but your changes won't carry over into other scripts (unless you're including or requiring them as part of the same script).

Imagine someone sending you a package in the mail. At THEIR house, they gather all the contents (the data in the form inputs) and hand them to the guy at the post office (submit the form).  The guy at the post office takes everything and dumps it into a nice box called $_POST, takes it to you and hands it to you. Now, you're back at YOUR house and you have this $_POST box that contains all the original form data.

If you try to put something new into the $_POST box, you can, but it's not really going anywhere once it's in there. It just sits there, in your house. If you want to take it to someone else's house (a completely separate script), you can take all that stuff out and give it to the post office to send to them (re-post the data through another form POST), but people in other houses cannot reach the $_POST box that is sitting inside your house.

A session does allow sort of a "sharing" mechanism between pages (sort of like one big $_POST box that everyone in other houses has access to), but it doesn't seem right for this situation.

What's strange is that you seem to be using the expected notation for other elements:
<input name="bind_category[]" type="checkbox" checked="checked">
....etc....
<textarea name="notes[]" id="notes">test</textarea>

I'm curious why you don't just use the same notation with a hidden input:
  <!-- ID -->				<td class="td-center"><?php echo $id; ?><input type="hidden" name="id[]" value="<?php echo $id; ?>"></td>

Open in new window


That would give you an array of IDs in your resulting $_POST.

2. Checkbox form inputs don't get sent if they're not checked, so if you're expecting things to have the same index across the board, you might be in for a surprise. If your IDs are unique, you could use them as the index in your form input names:

<input type="hidden" name="id[<?php echo $id; ?>]" value="<?php echo $id; ?>">
<input name="bind_category[<?php echo $id; ?>]" type="checkbox" checked="checked">
<textarea name="notes[<?php echo $id; ?>]" id="notes">test</textarea>
...etc...

That way, you'll be able to figure out which array element belongs to which ID. (Just try it and do a print_r() on the resulting $_POST to see what I'm talking about).

3. You fetch your categories from the database on every loop. This is really bad for performance. Fetch all your categories once (before your first loop), and put them into an array like $categories or something. Then when you need to build your category HTML, just loop through $categories. It'll be OODLES more efficient - I promise you.
0
 
Ray PaseurCommented:
It may get easier if you start from the beginning.
http://www.php.net/tut.php
0
 
LB1234Author Commented:
Guys this has been sooooooo helpful!  I'm very appreciative for the detailed answers and information and review. Really I am.  Gr8, many of the things in the code are sort of weird, but are there for testing (for example, the value of the checkboxes all being checked), so that every time i see if the data is being written to the database properly, I don't have to manually fill in stuff on the form.

And the hidden input type was something I'd never even heard of before you mentioned it.  Wow.  Really helpful.  That has never ever been covered in any of the resources I've used for html or PHP.  It's amazing how many resources you need to read for the same topic because it's just wasn't on the author's radar (and I understand of course that no book or tutorial series can be comprehensive).

And Gr8, the reason why I have the category fetch in every loop is because I need the values in the $row array to process this correctly.  The category_now loop uses values from the $row result to check values.  So it seems logical to me that the category loop could only come after the $row resource has been created.

And Ray, I've checked out many of the resources you've sent in the past, as well as starting from scratch on the php.net site.  Granted, it's very easy to forget some of this stuff.  But I like to try to figure things before looking up the answer.  I'm sure that may not be the best method in all situations.
0
 
gr8gonzoConsultantCommented:
Regarding the category fetching - I understand the logic, and it's not bad logic, but it's geared to slow down your app as usage increases.

Let's say that MySQL can return your categories in 0.2 seconds. Now let's say you have 2 transactions on the page. Your script will spend 0.4 seconds to return the data from the database. Now let's say you have 10 transactions on the page. Now you're looking at 2 seconds of database time before the page can load. Now let's say 30 transactions - that's 6 full seconds of staring at a white screen until the data loads.

On top of that, with 30 transactions on the page, you're hitting the database 30 times in 6 seconds, for a single page hit. Add a few more users and you'll start noticing a slowdown in the overall database performance because it will start getting busy enough that other requests will need to wait in line, making other pages slower.

Some people come back and say, "Well, this page is just for me or just for a few people, so it's okay." The problem there is that you can't see the future. Nearly every experienced developer has written at least a few apps that were intended to be small-time apps that barely ever saw the light of day. Then something causes the app to be used more often than originally anticipated, and you eventually have to spend a lot of time trying to remember and figure out why the page is so slow. It may not be this app, but it might be the next, so it's always a good idea to program efficiently every time, so you get into good habits.

Now, back to the database querying. Your process is sort of like shopping at the supermarket and using a list to figure out what you need to buy. However, what you're doing now is you're leaving the list at home, and you're going home after every item, and you're reading the list again to figure out what you need to get next, and then you're driving back to the supermarket and picking up one more item, and repeating.

Database interaction is always a time-expensive operation, so if you can get the data once and hold onto it and reuse it or even copy it, you'll end up with a more efficient application. Generally speaking, it is -almost always- a bad idea to have a database query inside of another database query's while loop.

So what you can do here is create a variable that contains all of your options WITHOUT selections, and then simply copy that variable each time in your loop:

<?php session_start();  ?> 
<?php include ("db_connection.php");?>
<?php include ("includes/layout/header.php");?>

<div id="table">
<form action="process_table.php" method="post">

<table class="gridtable" width="95%" border="0" cellspacing="0" cellpadding="2">
 
<tr>
        <th width="3%" scope="col">id</th>
        <th width="3%" scope="col">Date</th>
        <th width="2%" scope="col">Amount</a></th>
        <th width="5%" scope="col">Vendor</th>
        <th width="8%" scope="col">Description</th>
        <th width="1%" scope="col">Bind Cat.</th>
        <th width="5%" scope="col">Category</th>
        <th width="7%" scope="col">Notes</th>
        <th width="1%" scope="col">Bind Notes</th>
        <th width="5%" scope="col">Last Updated</th>
        <th width="5%" scope="col">Updated By</th>
        <th width="1%" scope="col">Done</th>
</tr>
 
 
 <?php
 
// ************** START OF $category_options CREATION *******************
$category_options = "";

$category_query = "SELECT * FROM category";

$category_result_set = mysqli_query($connection, $category_query);

while ($category_row = mysqli_fetch_assoc($category_result_set)) {
		$category_options .=  sprintf("<option value = '%s'>%s</option>", $category_row['category'], $category_row['category']);
	}

// Now you have $category_options with a bunch of <options> but none are selected yet...

// ************** END OF $category_options CREATION *******************
 
 
$query = "SELECT * FROM transaction LIMIT 10";

$result = mysqli_query($connection, $query);

while ($row = mysqli_fetch_assoc($result)) {
	$id = $row["transaction_id"];
?>


  <tr>
  <!-- ID -->				<td class="td-center"><?php echo $id; $_SESSION["id"][] = $id; ?></td>
  <!-- Date-->   			<td class="td-center"><?php echo date("n-d-y", strtotime($row["date"]));?></td>
<!-- Amount-->   			<td class="td-center"><?php echo "$" . number_format($row["amount"], 2)?></td>
<!-- Vendor-->   			<td><?php echo $row["vendor"]?></td>
<!-- Description-->   		<td><?php echo $row["description"]?></td>
<!-- Bind Category-->    		<td class="td-center"><input name="bind_category[]" type="checkbox" checked="checked"></td>
<!-- Category Drop Down-->  

<td class="td-center">
<?php 

// ************** START OF $category_options_selected CREATION *******************
// Now make a copy of that original string without the "selected" and simply use a find-and-replace
// to inject "selected" into the <option> that should be selected.

// First the copy
$category_options_selected = $category_options;

// Now make the right option selected if $row["done"]  equals 1
if($row["done"] == 1)
{  
   // Find <option value='abc'> and replace with <option value='abc' selected>
   $category_options_selected = str_replace("value = '".$row["category"]."'", "value = '".$row["category"]."' selected",$category_options_selected);
}
// ************** END OF $category_options_selected CREATION *******************

// And finally display our new copy with the selected option	
echo "<select name=\"category[]\">" . $category_options_selected . "</select>";
?>

</td>
<!-- Notes-->   			<td><textarea name="notes[]" id="notes">test</textarea></td>
<!-- Bind Notes-->			<td class="td-center"><input name="bind_notes[]" type="checkbox" checked="checked"></td>
<!-- Last Updated-->  		<td class="td-center"><?php echo date("n-d-y")?></td>
<!-- Updated By-->			<td class="td-center"><?php echo "temp"  ?></td>
<!-- Done-->      			<td class="td-center"><input name="done[]" type="checkbox" checked="checked"></td>
  </tr>
<?php
}
?>

</table><br>

<input name="submit" type="submit"  align="right">

</form>
</div>



<?php include ("includes/layout/footer.php");?>

Open in new window


This approach should be MUCH more efficient. You could have 500 transactions on a page and barely see any change in performance.
0

Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 9
  • 8
  • 8
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now