How to display order details from database

Hello.
I have a little problem.
I am stuck and don’t know why it is not possible to retrieve the order_details from the database and reflect it on the relevant page.

Order detail page shows error

Notice: Undefined variable: order_details in /opt/lampp/htdocs/e-shop/order_details.php on line 50

Fatal error: Uncaught Error: Call to a member function fetch_assoc() on null in /opt/lampp/htdocs/e-shop/order_details.php:50 Stack trace: #0 {main} thrown in /opt/lampp/htdocs/ e-shop/order_details.php on line 50

attached picture.

order_details.php

<?php
session_start();

/*
not paid
paid
shipped
delivered
 */

include 'server/connection.php';
include 'layouts/header.php';

if (isset($_POST['order_details_btn']) && isset($_POST['order_id'])) {

	$order_id = $_POST['order_id'];
	$order_status = $_POST['order_status'];

	$stmt = $conn->prepare("SELECT * FROM order_items WHERE order_id = ?");

	$stmt->bind_param('i', $order_id);

	$stmt->execute();

	$order_details = $stmt->get_result();

} else {

	//header('location: account.php');

}

?>


<!-- Orders -->
<section id="orders" class="orders container my-5 py-5">
  <div class="container mt-5">
    <h2 class="form-weight-bold text-center">Order details</h2>
     <hr class="mx-auto ">
  </div>

 <table class="mt-5 pt-5 mx-auto" >
      <tr>
          <th>Product</th>
          <th>Price</th>
          <th>Quantity</th>
      </tr>

      <?php while ($row = $order_details->fetch_assoc()) {?>

            <tr>
              <td>
                <div class="product-info">
                  <img src="assets/imgs/<?php echo $row['product_image']; ?>"/>
                  <div>
                    <p class="pt-3"><?php echo $row['product_name']; ?></p>
                  </div>
                </div>
              </td>


              <td>
                <span>€<?php echo $row['product_price']; ?></span>
              </td>

               <td>
                <span><?php echo $row['product_quantity']; ?></span>
              </td>

            </tr>

          <?php }?>


  </table>

        <?php if ($order_status == "not paid") {?>
            <form style="float: right;" method="POST" action="payment.php">
              <input type="hidden" name="order_total_price" value="<?php echo $order_total_price; ?>"/>
               <input type="hidden" name="order_status" value="<?php echo $order_status; ?>"/>
              <input type="submit" name="total_pay_btn" class="btn btn-primary" value="Pay Now"/>
            </form>

            <?php }?>

</section>


 <?php include 'layouts/footer.php';?>

It’s been a minute since I’ve done php, but on quick glance the code looks valid.

Based on the error message, it looks like the query didn’t return any results. Are you sure the order id is being returned in the $_POST object, and that the query will return values?

one of these is false and so you never initialize $order_details.
Your code is very dangerous as it is. You only do the database query when the statements above are both true, but you fetch the database in all cases. This could not work.

You need to check for $order_details before you use it in the loop

Yes, the id value is returned and is inserted into the database and matches both the order table and the order_details table

in order details table order id also is 6

Oooh. Good catch. Is a button ever “set” in the post object? (Again, it’s been a minute since I’ve done php and cannot recall)

How exactly are you reaching this page? It should be using a href link or a get method form, since it is displaying data. A post method form is used when performing an action on the server, such as inserting, updating, or deleting data.

If an input is required for a page to work, you must validate that input before using it. If it isn’t valid, that’s an error and you should setup and display an error message letting the user know what is wrong and how to correct it. If it is valid, you would use it to find whatever data the page is responsible to display. If for some reason (programming mistake, bot submitting their own data) there is no matching data, you would test for this condition and display an appropriate message, and not attempt to run the code trying to display the output.

I am not familiar with PHP debugging to know what is available but do you have a way to see what happens line-by-line during execution? One term commonly used for that is single-stepping.

The errors are self explanatory in that you are trying to use a variable that is not defined. You should check that this variable is not empty and wrap this section of code within this IF condition.

if(!empty($order_details)){
	while ($row = $order_details->fetch_assoc()) {
	//table rows
	}
}

This will leave you with another error.
Undefined variable: order_status… ,which you wrapped around your Pay Now form.
Let’s just comment that code out for the moment and deal with the order_details button and query section.

Any POST processing should be verified against the server request method before using $_POST. There is also a possibility that order_id might not have a value and you wouldn’t want to use it in a query if it’s empty, so instead of isset() you should use !empty(). Now your IF condition should look like

if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['order_details_btn']) && !empty($_POST['order_id'])) {

Now there is no need to just define $order_id from $_POST['order_id'] without doing anything with the value. If you are going to define $order_id, you should trim() the value.

$order_id = trim($_POST['order_id']);

So at this point you are staring at a blank page without records so let’s add a general query in the processing ELSE condition section so records are shown by default.

$stmt = $conn->prepare("SELECT * FROM order_items");
$stmt->execute();
$order_details = $stmt->get_result();

Now you need a form to send that order_details call to view the record. Ideally this form would be within your records display so the order_id can be added to the form. Using your form example a basic version of this form might look like this.

<form action="payment.php" method="post">
	<input type="hidden" name="order_id" value="<?php echo $row['order_id']; ?>"/>	 
	<input type="hidden" name="order_total_price" value="<?php echo $row['order_total_price']; ?>"/>
	<input type="hidden" name="order_status" value="<?php echo $row['order_status']; ?>"/>
	<input type="submit" name="order_details_btn" class="btn btn-primary" value="View Details"/>
</form>

Add a <th>Status</th> column to your table and <td> tags around your form and you COULD press the View Details button and call up the single record, which kind of works.
HOWEVER we are not going to build the form this way as we are going define the values to a variable instead of echoing them.

From what I gather from your code is ideally there would be 3 types of status displays.

  • View Details button
  • Pay Now button
  • Or the record status as in PAID if is a value other than, "not paid"

So looking at this more broadly we could say we want to show a form or the field order_status if the order_status is "not paid". This can be defined roughly like so.

$status = (!empty($row['order_status']) && $row['order_status'] == "not paid"	?	'FORM' : $row['order_status']);

Now there are 2 types of forms to show, i.e. the ‘Pay Now’ or ‘View Details’ but we really just need 1 form and change the SUBMIT button names based on the same condition used when calling a single record. Much like we defined $status based on a condition we can define our submit button in the same way.

$button = (isset($_POST['order_details_btn']) && !empty($_POST['order_id']) ? '<input type="submit" name="total_pay_btn" class="btn btn-primary" value="Pay Now" />' : '<input type="submit" name="order_details_btn" class="btn btn-primary" value="View Details" />');				

Looking back at how we defined $status with ‘FORM’ as our placeholder we can now replace this with our actual form and dynamic submit button.

$button = (isset($_POST['order_details_btn']) && !empty($_POST['order_id']) ? '<input type="submit" name="total_pay_btn" class="btn btn-primary" value="Pay Now" />' : '<input type="submit" name="order_details_btn" class="btn btn-primary" value="View Details" />');	
$status = (!empty($row['order_status']) && $row['order_status'] == "not paid"	?	'<form action="payment.php" method="post">
	<input type="hidden" name="order_id" value="'.$row['order_id'].'"/>	 
	<input type="hidden" name="order_total_price" value="'.$row['order_total_price'].'"/>
	<input type="hidden" name="order_status" value="'.$row['order_status'].'"/>
	'.$button.'
</form>' : $row['order_status']);

Now all 3 status display types are defined as $status and can be echoed in your display section.

<td>
	<?php echo $status; ?>
</td>

Be sure to again look for the server request method when creating your payment section.

if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['total_pay_btn']) && !empty($_POST['order_id'])) {	
	//Payment processing
	echo "<pre>";
	print_r($_POST);  
	echo "</pre>";	 

}

I noticed on your page a list of possible status values, which includes not paid, paid, shipped and delivered. With a few small tweaks to the code I posted you can handle all cases.

Notice in my example where I am defining $status based on the condition of $row['order_status'] == "not paid". Based on your list values for status we can say we would like a Button shown on all possibilities except for delivered, in which case we can show $row['order_status']. So we would use NOT EQUAL delivered as our $status condition.

$status = (!empty($row['order_status']) && $row['order_status'] !== "delivered"	?	'<form action="payment.php" method="post">
	<input type="hidden" name="order_id" value="'.$row['order_id'].'"/>	 
	<input type="hidden" name="order_total_price" value="'.$row['order_total_price'].'"/>
	<input type="hidden" name="order_status" value="'.$row['order_status'].'"/>
	'.$button.'
</form>' : $row['order_status']);

Now we’ve already defined $button based on the order_details_btn, which is good and working fine but we need different buttons when the status is paid or shipped. If the status is paid we want a button to mark the order status as shipped, otherwise show the previously defined $button value. This would look like this.

$button = (!empty($row['order_status']) && $row['order_status'] == "paid" ? '<input type="submit" name="mark_shipped_btn" class="btn btn-primary" value="Mark As Shipped" />' : $button);

Then in the same way you can check for a shipped status and define the form button to Mark As Delivered.

$button = (!empty($row['order_status']) && $row['order_status'] == "shipped" ? '<input type="submit" name="mark_delivered_btn" class="btn btn-primary" value="Mark As Delivered" />' : $button);

In case it is not clear we are stacking these $button lines so each will reference the previous version. All in all the change is like so and I will show where I placed in in the scope of the page.

<?php
if(!empty($order_details)){
	while ($row = $order_details->fetch_assoc()) {
		$button = (isset($_POST['order_details_btn']) && !empty($_POST['order_id']) ? '<input type="submit" name="total_pay_btn" class="btn btn-primary" value="Pay Now" />' : '<input type="submit" name="order_details_btn" class="btn btn-primary" value="View Details" />');
		$button = (!empty($row['order_status']) && $row['order_status'] == "paid" ? '<input type="submit" name="mark_shipped_btn" class="btn btn-primary" value="Mark As Shipped" />' : $button);					
		$button = (!empty($row['order_status']) && $row['order_status'] == "shipped" ? '<input type="submit" name="mark_delivered_btn" class="btn btn-primary" value="Mark As Delivered" />' : $button);	
		$status = (!empty($row['order_status']) && $row['order_status'] !== "delivered"	?	'<form action="payment.php" method="post">
			<input type="hidden" name="order_id" value="'.$row['order_id'].'"/>	 
			<input type="hidden" name="order_total_price" value="'.$row['order_total_price'].'"/>
			<input type="hidden" name="order_status" value="'.$row['order_status'].'"/>
			'.$button.'
		</form>' : $row['order_status']);
?>

Like we did for payment processing we can define processing sections for shipped and delivered.

if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['mark_shipped_btn']) && !empty($_POST['order_id'])) {				
	//Shipped processing
	echo "<pre>Shipped processing<br>";
	print_r($_POST);  
	echo "</pre>";	 

}	

if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['mark_delivered_btn']) && !empty($_POST['order_id'])) {				
	//Delivered processing
	echo "<pre>Delivered processing<br>";
	print_r($_POST);  
	echo "</pre>";	 

}

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.