Ryan Smith Ryan Smith - 4 months ago 13
MySQL Question

Updating settings page with only one "save changes" button

Was wondering how it would be possible to make allow a user to make changes to his/her personal settings through a settings.php page, making it possible to change your username, password and email all on the same page. I know how to perform all these tasks I'm just not sure how it would be possible to perform different functions all on the same page with the same "save changes" button.

<div class="form-editinfo">
<form class="editform" method="POST" action="settings" style="width: 600px;" >
Name: <input type="text" name="editname" value="<?php echo $userRow['user_name']; ?>"><br /><br />
<table border='0' width='55%' cellspacing='0px'>
<tr><td>Current Password: </td><td><input type="password" name="editcurrpass" placeholder="Enter Current Password" ></td></tr>
<br /><tr><td>New Password: </td><td><input type="password" name="editnewpass" placeholder="Enter New Password" ></td></tr>
<tr><td>Confirm New Password: </td><td><input type="password" name="editconfpass" placeholder="Enter New Password" ></td></tr>
</table>
<input type="submit" name="editsubmit" value="Update Settings">
<?php

if(isset($_POST['editsubmit'])){

$newname = trim($_POST['editname']);
if(!empty($newname)){
$id = $_SESSION['user_session'];
$sqlnewname = $auth_user->runQuery("UPDATE users SET user_name='$newname' WHERE user_id='$id'");
$sqlnewname->execute();
}else{
echo "<br/>You must enter a new username!";
}
$uname = $userRow['user_name'];

$currpass = trim($_POST['editcurrpass']);
$newpass = trim($_POST['editnewpass']);
$newconfpass = trim($_POST['editconfpass']);
if(!empty($currpass) || !empty($newpass) || !empty($newconfpass)){
if(password_verify($currpass, $userRow['user_pass'])){
if($newpass == $newconfpass){
$new_password = password_hash($newpass, PASSWORD_DEFAULT);
$id = $_SESSION['user_session'];
$sqlupdpass = $auth_user->runQuery("UPDATE users SET user_pass=:newpass WHERE user_id='$id'");
$sqlupdpass->execute(array(':newpass'=>$new_password));
echo "<br />Password updated.";
}else{
echo "<br />Passwords do not match.";
}
}else{
echo "<br/>Incorrect Password.";
}
}else{
echo "<br />You did not fill out one or more of the required fields.";
}
}
?>

Answer

If I were you, I would think about breaking down this piece of code into separate parts (functions or class/methods) which allows you to better-manage your workflow because you can assign human-readable names to functions so they make more sense. Even though there is more code overall, all the functions in this scenario would be hidden as an include. Another upside is that you can reuse these functions elsewhere if need be. Also, it is best to put the business logic before the view and finally, I would add a hidden field in the form used to identify actions and I would also make the password fields an array for easier readability. See if this is more of a desired outcome for error messages:

/functions/myfunctions.php

// I suggest you use a query engine to run queries, this will return an array
// if toggled to do so. This will save you time and keep queries consistent
function query($con,$sql,$bind = false,$return = false)
    {
        if(is_array($bind) && !empty($bind)) {
            foreach($bind as $key => $value) {
                $bArr[":{$key}"]    =   $value;
            }
        }
        $query  =   $con->runQuery($sql);
        if(!empty($bArr))
            $query->execute($bArr);
        else
            $query->execute();

        if($return) {
            while($result = $query->fetch(PDO::FETCH_ASSOC)) {
                $row[]  =   $result;
            }

            return (!empty($row))? $row : array();
        }
    }
// Change password
function changePassword($password,$id,$con)
    {
        $password   =   trim($password);
        if(empty($password))
            return false;
        $password   =   password_hash($password,PASSWORD_DEFAULT);
        $bind       =   array($password,$id);
        query($con,"UPDATE `users` SET `user_pass` = :0 WHERE `user_id` = :1",$bind);

        return true;
    }
// Update name based on user_id
function updateName($name,$id,$con)
    {
        $name   =   trim($name);
        if(empty($name))
            return false;
        $bind   =   array($name,$id);
        query($con,"UPDATE `users` SET `user_name` = :0 WHERE `user_id` = :1",$bind);

        return true;
    }
// Check an array for empty fields
function checkArray($array,&$failed)
    {
        foreach($array as $key => $value) {
            $new[$key]  =   (is_array($value))? checkArray($array) : trim($value);
            if(empty($value))
                $failed[]   =   $key;
        }

        return $new;
    }
// Match two string
function passwordsMatch($pass1,$pass2)
    {
        $pass1  =   trim($pass1);
        $pass2  =   trim($pass2);

        if(empty($pass1) || empty($pass2))
            return false;

        return ($pass1 == $pass2);
    }
// Just wraps the password function
function storedPasswordMatch($password,$hash)
    {
        return password_verify($password,$hash);
    }
// Returns the messaging
function compileMessage($array,$type = 'error')
    {
        return '<span class="'.$type.'">'.implode('</span><br />'.PHP_EOL.'<span class="'.$type.'">',$array).'</span>';
    }

/settings

// Put this logic at the top of the page
// Update action
if(isset($_POST['action']) && $_POST['action'] == 'update_account') {
    // Add functions
    require(__DIR__.'/functions/myfunctions.php');
    // Update the name or record error
    if(!updateName($_POST['editname'],$id,$auth_user))
        $error['name']  =   'Name is invalid.';
    // Save a storing variable
    $allowPass  =   false;
    // Check if the passwords array is all filled
    $passwords  =   checkArray($_POST['password'],$allowPass);
    // If any password is not filled out, create error(s)
    if(!empty($allowPass)) {
        // I don't know what your preferred logic is, but this is set up so if
        // the user doesn't fill out all three passwords, then it's assumed
        // no password is being changed, so no error is generated
        if(count($allowPass) < 3) {
            foreach($allowPass as $err)
                $error[$err]    =   ucfirst($err).' password can not be empty.';
        }
    }
    // If all password fields are filled out
    else {
        $pass   =   $passwords['new'];
        $curr   =   $passwords['current'];
        $conf   =   $passwords['confirm'];
        // Check that the new and confirm password match
        $pMatch =   passwordsMatch($pass,$conf);
        // Check that the database password matches current password
        $dMatch =   storedPasswordMatch($curr,$userRow['user_pass']);
        // If new and confirm match
        if($pMatch) {
            // If current and database match
            if($dMatch) {
                // Change the password
                changePassword($pass,$id,$auth_user);
                // Record success
                $message['password']    =   'Password updated.';
            }
            else
                // Record error if in-database password doesn't match
                $error['password_match']    =   'Password on file does not match.';
        }
        else
            // If the new and confirm don't match record error
            $error['password_match']    =   'Passwords must match.';
    }
}
?>
<div class="form-editinfo">
    <?php
    // Write success messages to page
    if(!empty($message))
        echo compileMessage($message,'message');
    // Write error messages to page
    if(!empty($error))
        echo compileMessage($error);
    ?>
    <form class="editform" method="POST" action="settings" style="width: 600px;" >
        <input type="hidden" name="action" value="update_account" />
    Name:  <input type="text" name="editname" value="<?php echo htmlspecialchars($userRow['user_name']); ?>">
        <table border='0' width='55%' cellspacing='0px'>
            <tr>
                <td>Current Password: </td>
                <td><input type="password" name="password[current]" placeholder="Enter Current Password" /></td>
            </tr>
            <tr>
                <td>New Password: </td>
                <td><input type="password" name="password[new]" placeholder="Enter New Password" /></td>
            </tr>
            <tr>
                <td>Confirm New Password: </td>
                <td><input type="password" name="password[confirm]" placeholder="Confirm New Password"  /></td>
            </tr>
        </table>
    <input type="submit" name="editsubmit" value="Update Settings" />