Jump to content
Larry Ullman's Book Forums

Recommended Posts

Hi Larry,

 

With the help of PHP and MySQL for Dynamic Web Pages 4th edition, I managed to put my first database driven web site live last October. Your book was/is truly inspirational for me as it enabled the inclusion of functionality that I would never have thought of if I hadn't read it.

 

Now, I have moved on to your PHP Advanced and OOP 3rd edition so that I can get some idea on the way each syntax works and enable me to make a judgement on which to use and when. However, I have to say that I am struggling with the same chapter 9 exercise that Michiel was having a problem with. I have got to the same point where the title, content etc. have been fetched into the form where I can edit the content, but when I click submit, the page confirms it has been updated, even though it hasn't. I can see that the page id is being pulled into the page by including it as a quickform element, but unfortunately the where id=':id'); section of the update query is failing to recognise it. If I hard code the page id, the database updates as it should.

 

I don't normally ask for help, preferring to try and figure it out myself, but after a week of trying it has become a bit frustrating. I am sure the answer is probably very simple, but would appreciate the answer so that I can analyze where I was making my mistake and move on to the next challenge.

 

Many thanks

 

Paul.

Link to comment
Share on other sites

I'm guessing the script is using prepared statements. :id is therefor a binding parameter, and not a variable itself. . What you need to do is bind the parameter to :id.

 

This is done via Mysqli::bind_param(), a function with two paramters. The first is type, i.e the type of the variable. This is most like an integer here. The second parameter is the the actuall variable (or a simple value).

 

I've included an example with a table having three columns. ID, col1 and col2. These uses three variables bound using Stmt::bind_param().

 

The data types:

s = String (Most data, including dates, etc)

i = Integer (Integers)

d = Doubles (Numbers with decimals)

b = blob (packaged data, not often used)

/* create a prepared statement */
if ($stmt = $mysqli->prepare("INSERT into table (id, col1, col2) VALUES id = ':id', col1 = ':val1', col2 = ':val2'")) {

    /* bind parameters for markers */
    $stmt->bind_param("i", $id);
    $stmt->bind_param("s", $val1);
    $stmt->bind_param("s", $val2);
    
    /* execute query */
    $stmt->execute();

    /* close statement */
    $stmt->close();
}
 

 

Hope that solves it for you.

Link to comment
Share on other sites

Hi Antonio,

 

Thanks for trying, but alas, that is not what I am after, although I did give your suggestion a go in an update query. I guess it would have probably helped you if I had listed the code being used in the page to start with, so here it is (Bit I am having the problem with is in red):

 

// Need the utilities file:
require('include/utilities.inc.php');

// Redirect if the user doesn't have permission:
if (!$user->canCreatePage()) {
    header("Location:index.php");
    exit;
}
    
// Create a new form:
set_include_path(get_include_path() . PATH_SEPARATOR . '/usr/local/pear/share/pear/');
require('HTML/QuickForm2.php');
$form = new HTML_QuickForm2('editPageForm');

// add form element value for page id
$title = $form->addElement('text', 'id');
$title->setLabel('Page ID');
$title->addFilter('strip_tags');

// Add the title field:
$title = $form->addElement('text', 'title');
$title->setLabel('Page Title');
$title->addFilter('strip_tags');
$title->addRule('required', 'Please enter a page title.');

// Add the content field:
$content = $form->addElement('textarea', 'content');
$content->setLabel('Page Content');
$content->addFilter('trim');
$content->addRule('required', 'Please enter the page content.');

// Add the submit button:
$submit = $form->addElement('submit', 'submit', array('value'=>'Publish'));

// Select the database entries

//prepare statement:
    $qry = 'SELECT id, title, content FROM pages WHERE id=:id';
    $stmt = $pdo->prepare($q);
    $run = $stmt->execute(array( ':id' => $_GET['id']));
    
    if ($run) {
    // fetch the page content from database as object:
    $stmt->setFetchMode(PDO::FETCH_CLASS, 'Page');
    $id=$_SESSION['id'];
   
        while ($page = $stmt->fetch()) {
    
    // populate the form with the data:
    $form->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
    'id' => $page->getId(),
    'title' => $page->getTitle(),
    'content'=> $page->getContent())));
    }
    }

// End of select

// Check for a form submission:
if ($_SERVER['REQUEST_METHOD'] == 'POST') { // Handle the form submission
    
    // Validate the form data:
    if ($form->validate()) {
    
            // Store in a session:
           $_SESSION['id'] = $id;
            
            // Update Database with new values
            $qry = 'UPDATE pages SET title=:title , content=:content WHERE id="$id"';
            $stmt = $pdo->prepare($q);
            $run=$stmt->execute(array(':title'=> $title->getValue(), ':content' => $content->getValue()));    
         
            // Check if transaction was successful
            if ($run) {
        
            $form->toggleFrozen(true);
            $form->removeChild($submit);
        }

                
    } // End of form validation IF.
    
} // End of form submission IF.

// }

// Show the page:
$pageTitle = 'Edit Page';
include('include/header.inc.php');
include('modules/edit_page.html');
include('include/footer.inc.php');
?>

 

Many thanks for your help

 

Paul.

Link to comment
Share on other sites

Ok. The problem with that part is the quotations marks. Your $id variable is passed as text, not as a variable. Several ways to solve this, but I would do as below.

 

// Store in a session:
$_SESSION['id'] = $id;

// Update Database with new values
$qry = 'UPDATE pages SET title=:title, content=:content WHERE id=:id';
$stmt = $pdo->prepare($q);
$run = $stmt->execute(
   array(
       ':id' => $id,
       ':title'=> $title->getValue(), 
       ':content' => $content->getValue()
   )
); 
Link to comment
Share on other sites

Hi Antonio, thanks for your help again.

 

I tried your most recent suggestion, but unfortunately it did fail. However, I did then notice there were some mistakes in my code where it was trying to prepare $q instead of $qry, Doh!

 

So, I made the changes but it still didn't work. I am now wondering whether the problem is that I am trying to retrieve the page id from the session into the $id variable incorrectly. My code now reads as:

 

// Check for a form submission:
if ($_SERVER['REQUEST_METHOD'] == 'POST') { // Handle the form submission
    
    // Validate the form data:
    if ($form->validate()) {
    
            // Store in a session:
           $id = $_SESSION['id'];
            
            // Update Database with new values
            $qry = 'UPDATE pages SET title=:title , content=:content WHERE id=:id';
            $stmt = $pdo->prepare($qry);
            $run=$stmt->execute(array(':id' => $id, ':title'=> $title->getValue(), ':content' => $content->getValue()));    
         
            // Check if transaction was successful
            if ($run) {

 

When the above is run, I get no error and the page moves on to the 'page updated successfully' screen, but the database is not updated. If I hard code the page id into the update qry, it works fine.

 

Michiel put in his thread that he managed to get it to work by retrieving the page id into a hidden form element. Is that a better way to perform this type of update and if so, can you educate me on how to do that please?

 

Many thanks

 

Paul.

Link to comment
Share on other sites

It might be, yes.

 

Doing that is as simple as setting a hidden input field with HTML and retriving it in the PHP $_POST array. That value should be generated dynamically, using for example the GET value of your request. (along the lines of http://domain.com/something.php?id=1.)
 

Generating ID, based on GET

<form action="" method="post">
<input type="hidden" name="id" value="<?= isset($_GET['id']) ? (int) $_GET['id'] : 0; ?>"> <!-- Generated dynamically. --->
</form>

This allows you to get the ID as POST

 

 

// Check for a form submission:
if ($_SERVER['REQUEST_METHOD'] == 'POST') { // Handle the form submission

    // Make sure an ID is found
    $id = isset($_POST['id']) ? (int) $_POST['id'] : 0;

    // Make sure ID is valid
    if ( $id === 0 ) {
        // Serve error message, redirect or do something along those lines
        exit("ID not found/not valid integer (0).");
    }

    // Validate the form data:
    if ($form->validate()) {

          // Safely use $id
    }
} 

 

Btw. I use short-hand IF-else here. It's great for such things, as you can check values AND type cast. The great thing about type casting is that a String "x" is cast to an Integer 0... Therefor, you always have a POST value to send, and you know it's an Integer.

 

Also, learn to debug your logic flow. Use IF-else to controll values you depends on, try debugging by echo out variables, print out your query to make sure it's correct. Etc. ;)

 

Hope that helps you out.

Edited by Antonio Conte
  • Upvote 1
Link to comment
Share on other sites

  • 2 weeks later...

It might be, yes.

 

Doing that is as simple as setting a hidden input field with HTML and retriving it in the PHP $_POST array. That value should be generated dynamically, using for example the GET value of your request. (along the lines of http://domain.com/something.php?id=1.)

 

Generating ID, based on GET

<form action="" method="post">
<input type="hidden" name="id" value="<?= isset($_GET['id']) ? (int) $_GET['id'] : 0; ?>"> <!-- Generated dynamically. --->
</form>

This allows you to get the ID as POST

 

 

// Check for a form submission:
if ($_SERVER['REQUEST_METHOD'] == 'POST') { // Handle the form submission

    // Make sure an ID is found
    $id = isset($_POST['id']) ? (int) $_POST['id'] : 0;

    // Make sure ID is valid
    if ( $id === 0 ) {
        // Serve error message, redirect or do something along those lines
        exit("ID not found/not valid integer (0).");
    }

    // Validate the form data:
    if ($form->validate()) {

          // Safely use $id
    }
} 

 

Btw. I use short-hand IF-else here. It's great for such things, as you can check values AND type cast. The great thing about type casting is that a String "x" is cast to an Integer 0... Therefor, you always have a POST value to send, and you know it's an Integer.

 

Also, learn to debug your logic flow. Use IF-else to controll values you depends on, try debugging by echo out variables, print out your query to make sure it's correct. Etc. ;)

 

Hope that helps you out.

Link to comment
Share on other sites

Hello Antonio,

 

Sorry it has taken a while to get back to you on this issue. The good news is that it seems to be resolved, although I don't really understand why?

 

As I am using HTML Quickform2, I was unable to embed your PHP snippet into the QF2 hidden field. Probably need to investigate a little more on how to do that. However, what I did was this:

 

//Add hidden field for id:
$id = $form->addElement('hidden', 'id', 1);

 

followed by your suggestion:

 

// Check for a form submission:
if ($_SERVER['REQUEST_METHOD'] == 'POST') { // Handle the form submission

 // Make sure an ID is found
    $id = isset($_POST['id']) ? (int) $_POST['id'] : 0;

    // Make sure ID is valid
    if ( $id === 0 ) {
        // Serve error message, redirect or do something along those lines
        exit("ID not found/not valid integer (0).");
    }
    
    // Validate the form data:
    if ($form->validate()) {

                
            // Update Database with new values
            $qry = 'UPDATE pages SET title=:title , content=:content WHERE id=:id';
            $stmt = $pdo->prepare($qry);
            $run=$stmt->execute(array(':id' => $id, ':title'=> $title->getValue(), ':content' => $content->getValue()));    
         
            // Check if transaction was successful
            if ($run) {

 

Now, the bit I don't understand is why in the QF2 hidden field I have hard coded a page id and yet the page still updates for any page id. I would have thought that page id 1 would be the only page that gets updated?

 

Anyway, I am happy that the basic functionality for editing page content is now working. I don't know whether it is the proper way of performing edits, but maybe in my journey of learning PHP, PDO, HTML and everything else that goes with creating dynamic web sites, it is a start and hopefully I will learn more robust methods to apply this functionality.

 

I thank you and Larry for taking the time to help people like myself. Keep up the good work.

 

Regards,

 

Paul.

Link to comment
Share on other sites

  • 5 months later...

Hello

 

I'm trying to finish my edit_page.php script and I can't get the page to recognise the page id tha tgets passed to it.

 

Here's the script:

<?php # add_page.php
// This page both displays and handles the "edit a page" form

// Need the utilities file:
require('includes/utilities.inc.php');

// Redirect if the user doesn't have permission to edit this page:
if(!$user->canEditPage(new Page($_GET['id']))){
	header("Location:index.php");
	exit();
}

// get the page id
if ( (isset($_GET['id'])) &&(is_numeric($_GET['id'])) ) {
	$id = $_GET['id'];
} elseif ( (isset($_POST['id'])) &&(is_numeric($_POST['id'])) ) {
	$id = $_POST['id'];
} else {
	header("Location: index.php");
}

// Create a new form:
//set_include_path(get_include_path().PATH_SEPARATOR.'/usr/share/php/');
require('HTML/QuickForm2.php');


$form = new HTML_QuickForm2('addPageForm');
// Add the title field:
$title = $form->addElement('text', 'title');
$title->setLabel('Page Title');
$title->addFilter('strip_tags');
$title->addRule('required', 'Please enter a page title');

// Add the content field:
$content = $form->addElement('textarea', 'content');
$content->setLabel('Page Content');
$content->addFilter('trim');
$content->addRule('required', 'Please enter the page content.');

// Add the submit Button:
$submit = $form->addElement('submit', 'submit', array('value'=>'Edit This Page'));

// Add a hidden form element
$pid = $form->addElement('hidden', 'id', $id);

// Run the select method on the page id
$select_query = 'SELECT id, creatorId, title, content, DATE_FORMAT(dateAdded, "%e %M %Y") AS dateAdded
FROM pages WHERE id=:id';
$stmt = $pdo->prepare($select_query);
$select_result = $stmt->execute(array(':id'=>$_GET['id']));

//if it ran ok, fetch the page as an object and set the retrieved values to the form
if($select_result){
	$stmt->setFetchMode(PDO::FETCH_CLASS, 'Page');
	while($page = $stmt->fetch()){

		$title->setValue($page->getTitle());
		$content->setValue($page->getContent());
	}
}	
// Check for a form submission:
if($_SERVER['REQUEST_METHOD'] == 'POST'){ //handle the form submission
	
	// Validate the form data:
	if($form->validate()){
		
		// Insert into the database:
		$insert_query = 'UPDATE pages SET title=:title, content=:content, dateUpdated=NOW()) WHERE id=:id';
		$stmt = $pdo->prepare($insert_query);
		$insert_result = $stmt->execute(array
		(':id'=>$pid->getValue(),':title'=>$title->getValue(),':content'=>$content->getValue()));
		
		// Redirect the user to the newly edited page
		if($insert_result){
			header("Location: /cms2/page.php?id=$p_id");
		}
	} // end of form validation IF
} // End of form submission IF

$pageTitle = 'Edit this Page';
include('includes/header.inc.php');
include('views/edit_page.html');
include('includes/footer.inc.php');

?>

If I try to edit the page, it redirects me to index.php, which means that it can't retrieve the id for some reason. Am I retrieving the page id incorrectly? I'm using the method from Larry's first 'PHP and MySQL for Dynamic Websites' book.

Link to comment
Share on other sites

Hi Antonio

 

When I do as you suggest, I get a bool(true) message at the top of the page. I've noticed that when I'm logged in as the authour of the page, it redirects me to index.php when I click on edit. When I log in as admin, it doesn't edit the page. 

Link to comment
Share on other sites

Ok, I decided to to switch the $id element from a hidden to a text form and used $pid->setValue($_GET['id']). I now have the correct id in the form but it still won't update the database when I submit the page. Not sure why it can't pick up the page value, unless my SQL statement is incorrect?

 

EDIT: The problem WAS with my SQL statement (a trailing ')' character I forgot to delete). It all works now.

  • Upvote 1
Link to comment
Share on other sites

Ok, another problem  :)

 

I'm using the following code at the top of the page to pass an instance of type page to verify that the authour or admin can edit the page:

 

$select_query = 'SELECT id, creatorId FROM pages WHERE id=:id';
$stmt = $pdo->prepare($select_query);
$select_result = $stmt->execute(array(':id'=>$_GET['id']));
	
// If the query ran ok, fetch the record into an object:
if($select_result){
	$stmt->setFetchMode(PDO::FETCH_CLASS, 'Page');
	$page = $stmt->fetch();
}
if (!$user->canEditPage($page)) {
	header("Location:index.php");
	exit();
}

This works, however, when I try to edit the page and submit the form, I get the following error:

 

 

PHP Catchable fatal error:  Argument 1 passed to User::canEditPage() must be an instance of Page, boolean given

 

I don't understand why it accepts the $page value as an object when validating canEditPage() when the page loads, but thinks it's a boolean value when it tries to submit the form?

Link to comment
Share on other sites

I expect the problem is because your SELECT query isn't working. You can confirm this by printing out the values of the variables. More specifically, I imagine the problem is here:

 

$select_result = $stmt->execute(array(':id'=>$_GET['id']));
Is the ID still being passed in the URL when the form is posted?
Link to comment
Share on other sites

Hi Larry

 

Thanks for the reply. I understand what you're saying, but the problem I'm having is where to pass the id? I'm running three select queries: one at the top to check the creator ID matches the user id, one after the form is created to retrieve the data from the DB and populate the form to enable editing, and one to update the form after it's posted. The update query has the page id, but do you also pass this into a variable? I've included the code below:

<?php # edit_page.php
// This page both displays and handles the "add a page" form

// Need the utilities file:
require('includes/utilities.inc.php');

// Create a new form:
//set_include_path(get_include_path().PATH_SEPARATOR.'/usr/share/php/');
require('HTML/QuickForm2.php');

$select_query = 'SELECT id, creatorId FROM pages WHERE id=:id';
$stmt = $pdo->prepare($select_query);
$select_result = $stmt->execute(array(':id'=>$_GET['id']));

// If the query ran ok, fetch the record into an object:
if($select_result){
	$stmt->setFetchMode(PDO::FETCH_CLASS, 'Page');
	$page = $stmt->fetch();
}
if (!$user->canEditPage($page)) {
	header("Location:index.php");
	exit();
}


$form = new HTML_QuickForm2('editPageForm');
// Add the title field:
$title = $form->addElement('text', 'title');
$title->setLabel('Page Title');
$title->addFilter('strip_tags');
$title->addRule('required', 'Please enter a page title');

// Add the content field:
$content = $form->addElement('textarea', 'content');
$content->setLabel('Page Content');
$content->addFilter('trim');
$content->addRule('required', 'Please enter the page content.');

// Add the submit Button:
$submit = $form->addElement('submit', 'submit', array('value'=>'Edit This Page'));

// Add a hidden form element
$pid = $form->addElement('hidden', 'id');

// Run the select method on the page id
$select_query = 'SELECT id, creatorId, title, content, DATE_FORMAT(dateAdded, "%e %M %Y") AS dateAdded
FROM pages WHERE id=:id';
$stmt = $pdo->prepare($select_query);
$select_result = $stmt->execute(array(':id'=>$_GET['id']));

//if it ran ok, fetch the page as an object and set the retrieved values to the form
if($select_result){
	$stmt->setFetchMode(PDO::FETCH_CLASS, 'Page');
	while($page = $stmt->fetch()){

		$title->setValue($page->getTitle());
		$content->setValue($page->getContent());
		$pid->setValue($page->getId());
	}
}
// Check for a form submission:
if($_SERVER['REQUEST_METHOD'] == 'POST'){ //handle the form submission
	
	// Validate the form data:
	if($form->validate()){
		
		// Insert into the database:
		$insert_query = 'UPDATE pages SET title=:title, content=:content, dateUpdated=NOW() WHERE id=:id';
		$stmt = $pdo->prepare($insert_query);
		$insert_result = $stmt->execute(array
		(':id'=>$pid->getValue(),':title'=>$title->getValue(),':content'=>$content->getValue()));
		
		// Redirect the user to the newly edited page
		if($insert_result){
			header("Location: /cms2/page.php?id=".$pid->getValue());
		}
	} // end of form validation IF
} // End of form submission IF

$pageTitle = 'Edit this Page';
include('includes/header.inc.php');
include('views/edit_page.html');
include('includes/footer.inc.php');

?>
Link to comment
Share on other sites

The page always expects the id to be passed in the URL. You can either change the form's action to do that, or you can change the code so that it can accept the ID in GET or POST. If you do the latter, you'd store the ID in a hidden form input.

Link to comment
Share on other sites

  • 9 months later...

The page always expects the id to be passed in the URL. You can either change the form's action to do that, or you can change the code so that it can accept the ID in GET or POST. If you do the latter, you'd store the ID in a hidden form input.

 

Hi Larry,

 

I hope it's OK with you if I resurrect this old thread.

 

Anyway, could edit_page.php first check (just after utilities.inc.php is required) whether the REQUEST_METHOD is set to GET or POST? If it is set to GET the value of $_GET['id'] is saved in $_SESSION['id'], (or pehaps something like  $_SESSION['pageID']) then one can use the $_SESSION variable when submitting changes to the $page object.  This logic seems to work OK (at least so far); however, I don't know if it's fundamentally sound in terms of security or OOP principles.

 

Thanks,

Hacker

Link to comment
Share on other sites

That sounds like a reasonable approach to me. Thanks for the question!

 

I found this to work much better...

 

By placing the following line of code in page.php:

 

$_SESSION['page'] = $page; after if ($page) {... (line 25 in your book)

 

AND...

 

$page = (isset($_SESSION['page'])) ? $_SESSION['page'] : null;

 

after session_start() in utilities.inc, all the gremlins were run out of town.

 

Hacker

Link to comment
Share on other sites

 Share

×
×
  • Create New...