Using isset with $_get

I have a web site (test) that has a menu where you click an item, then text is displayed - or it would if I could solve some issues.

I am using some code I found online that I have tried to adapt but I get error messages. These are:

Notice: Undefined variable: row in C:\xampp\htdocs\testsite\index.php on line 43

Warning: Invalid argument supplied for foreach() in C:\xampp\htdocs\testsite\index.php on line 43

Notice: Undefined variable: data in C:\xampp\htdocs\testsite\index.php on line 56

The code:

<?php
//  Connect to the database
$PDO = new PDO("mysql:host=localhost;dbname=scarab", "root", "");

// set the PDO error mode to exception
$PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

if(isset($_GET['id']) && is_numeric($_GET['id']))
   {
       // query
       $query = "SELECT * FROM topmenu WHERE ID =" . $_GET["id"] . "ORDER BY id ASC";
       $row = $PDO->query($query);
   }
   else
   {
     $id = 1;
   }

?>
<!DOCTYPE html>
<html>
    <head lang="en">
        <meta name="title" content="">
        <meta name="description" content="">
        <meta name="keywords" content="">
        <meta name="robots" content="index, follow">
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <meta name="language" content="English">
        <meta name="revisit-after" content="30 days">
        <meta name="author" content="Ben Peters">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Site Title here</title>
        <link rel="stylesheet" href="css/pagestyles.css" type="text/css">
    </head>
<body>
    <table class="topmenu">
        <tr>
            <td>
                <h1 class="siteName">Site Name here</h1>
            </td>
            <?php
              foreach($row as $data) {
            ?>
            <td><a href="index.php?id=".<?php echo $data['id']; ?>."</a">
              <?php echo $data['menuheader']; ?>
            </a></td>
            <?php
              }
            ?>
        </tr>
    </table>

    <hr>

    <?php echo $data['pagecontent']; ?>

I would very much appreciate any help, include code examples. As I am new to php/mysqli/pdo, I understand that some advice would be advanced, so I would like any code, basic.

[off-topic]
@benpeters648 when you post code in the forum, you need to format it. To do so you can either select all the code and click the </> button, or type 3 backticks ``` on a separate line both before and after the code block.

I have done it for you this time.
[/off-topic]

Okay. So. What’s happening, and why are you getting what you get. Let’s look at your code, explain a few lines, and maybe it’ll make more sense.

Let’s first look at the output sections of your code. There are two:

and

PHP, helpfully, tells you what line the problem happened on:
Notice: Undefined variable: row in C:\xampp\htdocs\testsite\index.php on line 43
Line 43, in particular, will be this one:

So, PHP is telling you that $row is undefined. Why is $row undefined? Well, lets go find where this is defined…

Now; if $_GET[‘id’] exists, and is a number, then we run the top block, and $row is… a PDOStatement. Which… is a bit of a misnomer, but whatever we’ll run with it.

If $_GET[‘id’] does NOT exist, or is NOT a number, then we evaluate the bottom block.
If we go to the bottom block, what’s the value of $row? Uhhh… we didnt define one. So this is when PHP gets to line 43, says “This doesnt exist”, and has a wobbly.

The second error, Warning: Invalid argument supplied for foreach() in C:\xampp\htdocs\testsite\index.php on line 43, is related to the previous one; because $row didnt exist, the thing that you’re trying to foreach on doesnt exist, so the call to foreach also throws an error.

On to the third error, and the second block;

Line 56 is your bottom line…

Well, if the foreach didn’t run, we never set a value to $data. So $data[‘pagecontent’] is going to fail because $data doesnt exist.

Solutions.

Solution #1: Define $row for the bottom block. Either by executing another PDO query to pull down the record for ID #1, or by manually defining a result set equivalent.
Solution #2: Define an alternate output; this can be accomplished with another isset() check down in the output section, or by condensing it using the null coalescing operator.
EX:
<?php echo $data['pagecontent'] ?? "No Content Here"; ?>
Solution #3: Define an initial, empty value for $row.

1 Like

Will that query even execute? It seems to me that you need to put a space after you’ve stuck the id into it, otherwise the id will run into the “ORDER” keyword. Better still, you should use a Prepared Statement instead of putting the id into the query string the way you have, but perhaps get it working first and then add security.

There’s a missing fetch() somewhere, too, isn’t there? You execute the query (which returns a results object) but don’t you need to also fetch the results from it, either individually or in one go with fetchAll()? Presumably there will only ever be one result so it doesn’t matter that much.

More important than your “problem”, your code is vulnerable to an SQL Injection Attack.

NEVER EVER put variables in your query. You need to use Prepared Statements. Never trust user input.

Do not use the query method when you have variables.

Also, is_numeric is not the best option to check id’s. It allows more than just numbers to be true. Use ctype_digit when you have strictly numerical id’s.

1 Like

Before sending data to an array, you need to initialise/set it up first so in the case of $row:

$row = array();

That sets it up as an empty array so that if nothing gets added to it, a foreach loop sees an array, albeit an empty array

The following is a reply I wrote for you on a different help forum. I know you saw it because you liked it. You need to do the things that have already been given in replies. If you don’t understand something, you need to ask specific questions.

The sql query you are showing and the output you are (trying) to produce from that query make no sense. If this was working, you are querying for the row of data WHERE the id column matches the $_GET[‘id’] value and looping to produce a (one) link with ?id=$data[‘id’] in it, i.e. a link containing the same id that was in $_GET[‘id’].

You need to step back and come up with a stateable definition of what your code needs to do. You are doing two things, 1) producing navigation links, and 2) when a link has been clicked, you are displaying the content that corresponds to the id in the clicked link.

To do item #1, you would query to get ALL the page ids, and page names/titles, which I assume are in the menuheader column (if you list out the columns you are selecting in a query, rather than using *, it helps make your code/query self-documenting.) You would then test and loop over the result from this query to produce navigation links. Note: almost every if() conditional test needs an else so that code does something when the main condition fails. For navigation link data, if there is no data, rather than outputting nothing, you should output a ‘sorry, nothing to display’ or similar message.

To do item #2, you would test for a $_GET[‘id’] value and use it to query to get the matching row of data, fetch that single row of data (no loop needed), and if there was a matching row of data, output the content for the page. If there was no matching row of data, you would instead output a ‘nothing to display’ or similar message.

Do you understand that to do this requires two (2) sql queries?

There is no reason to take any risks by using user input in a DB query and no reason to have more than one query for the task at hand. You are querying to get the record id and menuheader for your LIST and the only other field used is pagecontent so query for these fields order by id.

$sql = "SELECT `id`, `menuheader`, `pagecontent` FROM topmenu ORDER BY id ASC";

Define $data as an array

	$data = array();

…and build this data array with the query results using the id as the primary array KEY.

	$data = array();
	$sql = "SELECT `id`, `menuheader`, `pagecontent` FROM topmenu ORDER BY id ASC";
	$query = $PDO->query($sql);
	while($row = $query->fetch(PDO::FETCH_ASSOC)){	
		$data[$row['id']] = $row;
	}

As you want $id to represent a users record selection or a default record, define this variable accordingly. Because we built the data array with the id as the array KEY this gives you a way to check if user input is a valid id by using array_key_exists of the GET value against your data records. If all checks out then $id can be defined with the GET value otherwise use your default id of 1;

	$id = (isset($_GET['id']) && array_key_exists($_GET['id'],$data) ? $_GET['id'] : 1);

Then is simply a matter of looping through your list and displaying page content with this same data array.


<?php
	$data = array();
	$sql = "SELECT `id`, `menuheader`, `pagecontent` FROM topmenu ORDER BY id ASC";
	$query = $PDO->query($sql);
	while($row = $query->fetch(PDO::FETCH_ASSOC)){	
		$data[$row['id']] = $row;
	}
	
	$id = (isset($_GET['id']) && array_key_exists($_GET['id'],$data) ? $_GET['id'] : 1);
?>
<!DOCTYPE html>
<html>
    <head lang="en">
        <meta name="title" content="">
        <meta name="description" content="">
        <meta name="keywords" content="">
        <meta name="robots" content="index, follow">
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <meta name="language" content="English">
        <meta name="revisit-after" content="30 days">
        <meta name="author" content="Ben Peters">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Site Title here</title>
        <link rel="stylesheet" href="css/pagestyles.css" type="text/css">
    </head>
<body>
	<table class="topmenu">
		<tr>
			<td>
				<h1 class="siteName">Site Name here</h1>
			</td>	
		</tr>
		<?php
		foreach($data as $rid => $row):
			echo '<tr>
				<td><a href="index.php?id='.$rid.'">'.$row['menuheader'].'</a></td>
			</tr>'."\r";
		endforeach;
		?>
    </table>

    <hr>

    <?php echo $data[$id]['pagecontent']; ?>
</body>
</html>
1 Like

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