0

I created a Web App for a Delivery service. It incorporates google maps as well as queries a database for open deliveries. The problem is that when I hooked up a button to shift the delivery array by array_shift() through an ajax call, is wont recognize the array.

<?php
session_start();
require 'ConnectTest.php';

// IF YOU'RE NOT LOGGED IN, KICK BACK TO LOGIN SCREEN
if(!isset($_SESSION['username']) || !isset($_SESSION['password'])){
  header("Location:signin.php");
}

$servername = "localhost:3306";
$user = "sonic_client";
$pass = "client";
$dbName = "sonicStrains";

$drop = "DROP TABLE ".$_SESSION['username']."_deliveries";
mysqli_query($server, $drop);

//CREATE INDIVIDUAL DRIVER TABLE TO HOLD DELIVERIES
$createQuery = "CREATE TABLE ".$_SESSION['username']."_deliveries (
                transaction_id VARCHAR(13),
                timePaid INT(11),
                username VARCHAR(30),
                user_location VARCHAR(255),
                user_lat float(10,6),
                user_long float(10,6),
                item_name VARCHAR(20),
                item_quantity float,
                driver_username VARCHAR(60),
                driver_lat float(10,6),
                driver_long float(10,6),
                on_delivery tinyint(1))"
;

$created = mysqli_query($server, $createQuery);

if ($created){

  //query the deliveries for open deliveries up untill 5 deliveries
  $queryString = "SELECT * FROM deliveries LIMIT 5";

  $query = mysqli_query($server, $queryString);

  if($query){

    // CREATE ARRAY TO HOLD QUERY ROWS
    $rows = array();

    while($queryresult = mysqli_fetch_assoc($query)){
      $rows = $queryresult;
    }


    //INSERT & UPDATE THE TABLES WITH DELIVERY QUERIES
    foreach($rows as $row){

      // INSERT INTO INDIVIDUAL DRIVER TABLE
      $insertQuery = "INSERT INTO ".$_SESSION['username']."_deliveries (transaction_id, user_location, user_lat, user_long) VALUES('$row[transaction_id]', '$row[user_location]', '$row[user_lat]', '$row[user_long]')";

      $insertExec = mysqli_query($server, $insertQuery);

      // UPDATE MASTER LIST OF DELIVERIES SO THAT OTHER DRIVERS DONT QUERY SAME ORDER
      $updateQuery = "UPDATE deliveries SET on_delivery=true, driver_username='$_SESSION[driver_id]' WHERE transaction_id='$row[transaction_id]'";
      $updateExec = mysqli_query($server, $updateQuery);
    }

  }else{echo mysqli_error($server);}


}else{echo mysqli_error($server);}


if(isset($_GET['pop'])){
  array_shift($rows);
}

echo<<<_

<!DOCTYPE html>
<html>

<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
  <style> /* the stylesheet below */ </style>
</head> 

<body>

  <div class="Banner">
      <div class="TitleText">Sonic Strains &copy;</div>
  </div>


  <div class="login">Logout</div>
  <div class="gallery" id="container">
      <div class="map" id="mapInsert"></div>
      <div class="navButton">Start Nav</div><div class="orderButton">Order Details</div>
      <div class="abortButton">Abort</div><div class="confirmButton" onclick="shiftOrder()">Confirm</div><div class="disclaimer"></div>
  </div>


  <script>
      function initMap() {

        navigator.geolocation.getCurrentPosition(function(position) {

            var initialLocation = {lat:$rows[user_lat], lng:$rows[user_long]};
            var Map = new google.maps.Map(document.getElementById('mapInsert'));
            Map.setCenter(initialLocation);
            Map.setZoom(13);
            // MAKE ANOTHER MARKER FOR THE CLIENT LOCATION
                    var userLocation = {lat:$rows[user_lat], lng:$rows[user_long]};
                    var marker = new google.maps.Marker({
                        position:userLocation, 
                        map:Map,
                        draggable:false, 
                        clickable:false
                        });
                    marker.setMap(Map);

        }, function(positionError) {

        //---------- User denied geolocation prompt - default to Chicago

        Map.setCenter(new google.maps.LatLng(39.8097343, -98.5556199));
        Map.setZoom(5);
      },{enableHighAccuracy:true, timeout: 3000, maximumAge:1000});

      } 

      function shiftOrder(){

          var http = new XMLHttpRequest();
          http.onreadystatechange = function(){
              console.log(this.responseText);

          }
          http.open("GET", "driverIndex.php?pop='pop'", true);
          http.send();

      }

  </script>
  <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=KEY&callback=initMap" 
      async defer></script>

</body>
</html>

_;

?>

I have called print_r($rows) and it returns values as it should. I even use it to reference lat & long in my Google Maps, so why can't array_shift($rows) work?

If needed to reproduce my example above, here is the used CSS stylesheet:

.TitleText{
  font-size:200%;
}

.Banner{                                      /*This is test code to hold the top ad*/
  width:100%;
  text-align:center;
  border-style:solid;
  border-color:grey;
  color:green;
  font-size:230%;
  font-weight:heavy;
  letter-spacing:1px;
}

.TopNav{

  margin-top:20px;
}

a:link{
  text-decoration:none;
}

.login{
  margin-top:15px;
  margin-right:15px;
  float:right;
  border-style:solid;
  border-color:grey;
  border-radius:10px;
  background-color:blue;
  color:white;
  font-size:30px;

}

.gallery{                                         /*This will hold the gallery Items*/
  text-align:center;
  display: inline-block;
  width: 100%;
  height: auto;
  margin-top: 16px;
  border-style: solid;
  border-color: green;
}

.map{
  width:100%;
  height:400px;
  border-style:solid;
}

.navButton{
  background-color:green;
  color:white;
  font-size:30px;
  margin-top: 16px;
  text-align:center;
  width:100%;
  display: inline-block;
  border-radius:10px;
  border-style:solid;
  border-color:red;
}

.orderButton{
  background-color:green;
  color:white;
  font-size:30px;
  margin-top: 16px;
  text-align:center;
  float:right;
  display: inline-block;
  border-style:solid;
  border-right:red;
  border-color:purple;
  border-radius:10px;
  width:100%;
  height:auto;

}

.abortButton{
  background-color:green;
  color:white;
  font-size:30px;
  margin-top: 16px;
  text-align:center;
  border-style:solid;
  border-color:yellow;
  border-radius:10px;
  width:100%;
  height:auto;
  display: inline-block;
}

.confirmButton{
  background-color:green;
  color:white;
  font-size:30px;
  margin-top: 16px;
  text-align:center;
  border-style:solid;
  border-color:pink;
  border-radius:10px;
  width:100%;
  height:auto;
  display: inline-block;
}

h1{
  font-weight:bold;
}


h3{
  font-weight:bold;
  color:green;
}

.disclaimer{
  width:100%;
  height:30px;

  border-style:solid;
  border-radius:10px;
  border-color:grey;
  text-align:center;
}
Pinke Helga
  • 6,378
  • 2
  • 22
  • 42
  • 1
    Please read about [**SQL injection**](https://en.wikipedia.org/wiki/SQL_injection). Instead of building queries with string concatenation, use [**prepared statements**](https://secure.php.net/manual/en/pdo.prepare.php) with [**bound parameters**](https://secure.php.net/manual/en/pdostatement.bindparam.php). See [**this page**](https://phptherightway.com/#databases) and [**this post**](https://stackoverflow.com/questions/60174/how-can-i-prevent-sql-injection-in-php) for some good examples. – Alex Howansky Jan 04 '19 at 21:23
  • Why wont the array_shift() work? – Sonic Strains Jan 04 '19 at 21:25
  • 1
    `array_shift()` pops the first item off the front of an array and returns it, leaving the array one item shorter. You're not using the return value of the function or the array after it's been modified. What are you expecting to happen? Usually you'd have something like `$firstItem = array_shift($list);` – Alex Howansky Jan 04 '19 at 21:27
  • You should update code as if(isset($_GET['pop'])){ $rows = array_shift($rows); } – Praveen Govind Jan 04 '19 at 21:29
  • 4
    Also note, the day-to-day operation of your app should never include schema changes. I.e., you shouldn't be adding/removing a delivery table per driver. Instead, have a delivery table for all drivers, keyed by driver_id. – Alex Howansky Jan 04 '19 at 21:29
  • You could also skip the `array_shift()` and just use `$rows[0][user_lat]` to explicitly reference the elements of the first row. – Alex Howansky Jan 04 '19 at 21:33
  • I called array_shift($rows) which should pop the 1st element thus loading the next set of lat and long coordinates. The map remains the same though. – Sonic Strains Jan 04 '19 at 21:34
  • I want to use array_shift() because that element will no longer be needed. Simply looping through array without "popping" wont do. – Sonic Strains Jan 04 '19 at 21:36
  • correct, I forgot to reference the return value as he new array. How would I replace that array with the old array that currently hold the lat & long? – Sonic Strains Jan 04 '19 at 21:38
  • No, the return value is *not* the new array, it's a single item. The existing array is changed in place. I.e., you're popping the first element and then throwing it away, leaving you with the next four in `$rows`, which you're then treating as if it were a single element. You're using `$rows['user_lat']` but that won't work because `$rows` is still an array, not a single item from the array. You need to either iterate over the rows like `foreach ($rows as $row) {` or explicitly reference one row like `$rows[0]['user_lat']` – Alex Howansky Jan 04 '19 at 21:40
  • If $rows['user_lat'] has changed why wont it reflect in the Google Map? Shouldn't it reflect the new element? Could u type out a visualization to help – Sonic Strains Jan 04 '19 at 21:53
  • In your code `$rows` is a numbered array of all fetched rows by the query `SELECT * FROM deliveries LIMIT 5`. Conditionally it is reduced to max. 4 rows, removing the first. So `$rows['user_lat']` should not be defined at all, but `$rows[0]['user_lat']` to `$rows[4]['user_lat']` – Pinke Helga Jan 04 '19 at 22:01
  • Btw.: Prepared statements can not parameterize `INSERT INTO ".$_SESSION['username']."_deliveries` and `mysqli_real_escape_string` will not help much to sanitize. You have to enclose the table name into backticks `\`tablename\`` and properly escape the name also taking potential encoding attacks into account. If somehow possible you should avoid dynamic table names at all. – Pinke Helga Jan 04 '19 at 22:13
  • QUASI, $rows should be a associative array, why are you calling it a numbered array? – Sonic Strains Jan 04 '19 at 22:15
  • Ok, I see the line `$rows = $queryresult;` actually overwrites `$rows` with the latest fetched columns. This way the entire code does not seem to make sense, refrained from the fact that semantically the variable names do not express their purpose. – Pinke Helga Jan 04 '19 at 22:22
  • To fetch the **fifths row only**, the query could be as well `SELECT * FROM deliveries LIMIT 4,1` (zero-based count => 4) – Pinke Helga Jan 04 '19 at 22:28
  • Could you tell me how I would go about referencing the new value of $rows after the array_shift($rows) – Sonic Strains Jan 04 '19 at 22:30
  • The question is what you **really** want to achieve. There does not seam to be any reason to remove a *column* by position from the associative array. – Pinke Helga Jan 04 '19 at 22:34
  • $rows is an array filled with associative arrays. array_shift() should remove an array, not the column. ie $rows[0]['user_lat'] not $rows['user_lat'] – Sonic Strains Jan 04 '19 at 22:39
  • Above you said 'QUASI, $rows should be a associative array', now you say '$rows is an array filled with associative arrays ... ie $rows[0]' meaning `$rows` would be a numbered array containing e.g. [0]. You are confused. In fact you overwrite `$rows` with the latest fetched row, thus containing only columns as associative array. see `$rows = $queryresult;` in your code. You propably intended `$rows[] = $queryresult;` to append to the array. – Pinke Helga Jan 04 '19 at 22:47
  • Referencing $rows[0][column_name] returns an error -- 'user_lat' undefined. -- But it works when I call $rows['user_lat']. Calling $rows[0]['user_lat'] doesn't work. – Sonic Strains Jan 04 '19 at 22:53

1 Answers1

0

Even if it could not be conclusively clarified in the comment discussion, you propably expected the code

while($queryresult = mysqli_fetch_assoc($query)){
  $rows = $queryresult;
}

to add new rows as entries to the array. In fact it overwrites the variable with an associative array containing the latest row, i.e. columns.

To get an array of rows, use

while($queryresult = mysqli_fetch_assoc($query)){
  $rows[] = $queryresult;
}

This will append array items to the existing array.

Pinke Helga
  • 6,378
  • 2
  • 22
  • 42
  • should I declare $rows = array(); outside of the loop, or just $rows[ ] = $old_array; inside of the loop? – Sonic Strains Jan 04 '19 at 22:59
  • Explicitly declaring and initializing variables is always a good code style. Yes, of course outside the loop. – Pinke Helga Jan 04 '19 at 23:00
  • @SonicStrains not `=$old_array` but `= $new_item`, i.e. the query result. `$existing_array[] = $new_item;` – Pinke Helga Jan 04 '19 at 23:05
  • Your solution worked to load up the queries, but now I'm having trouble referencing the lat & long. I've tried $rows[0][4], $rows[0][5] (LatLng) and I tried $rows[0]['user_lat'], $rows[0]['user_long'] and both didn't properly reference the coordinates. – Sonic Strains Jan 04 '19 at 23:16
  • What does a `var_dump($rows);` actually output? The output should already answer your question. – Pinke Helga Jan 04 '19 at 23:20
  • Array ( [0] => Array ( [transaction_id] => 5c2fb27078926 [timePaid] => 0 [username] => [user_location] => 1301 Bataan St NW, Washington, DC 20036, USA [user_lat] => 38.907246 [user_long] => -77.037292 [item_name] => girl_scout_cookies [item_quantity] => 1 [driver_username] => [driver_lat] => 0.000000 [driver_long] => 0.000000 [on_delivery] => 1 ) etc etc etc – Sonic Strains Jan 04 '19 at 23:24
  • At the point of print_r `$rows[0]['user_lat']` is `38.907246`. – Pinke Helga Jan 04 '19 at 23:28
  • i used that ($rows[0]['user_lat']) as the reference for LatLng but it said --"user_lat is not a number" -- but LatLng was perfectly fine when it was just $rows['user_lat']. Why the fuss all of a sudden now? – Sonic Strains Jan 04 '19 at 23:33
  • Try `var_dump`. – Pinke Helga Jan 04 '19 at 23:35
  • i tried var _dump, it said it's a "string", but that doesn't explain why that same* string was okay to use when the array was simply $rows['user_lat']. It's the same array is it not. Why not accept the value now when it changed to $rows[0]['user_lat'] ? – Sonic Strains Jan 04 '19 at 23:39
  • Not clear why it should have worked correctly before (showing the correct number) but `(double) $row['user_lat']` explicitly casts the type into a number. – Pinke Helga Jan 04 '19 at 23:42
  • @SonicStrains We should stop breaking the SO rules by discussing many code problems at once. SO is meant to answer one specific issue per question. You should open a new question touching a specific issue if you run into further problems after solving one. – Pinke Helga Jan 04 '19 at 23:48
  • 1
    will do, thanx. – Sonic Strains Jan 04 '19 at 23:49