0

I'm banging my head against a wall, trying to work out why this PDO prepared statement isn't working. The only thing I can conclude is that if a variable is empty, the INSERT fails, but this is confusing because I must've done this a thousand times before without issue.

Here's my code. All of the variables are being set from an POST variable, so some of them could be blank if the user didn't provide this data.

$stmt = $db->prepare("INSERT INTO students
    (id, classid, userid, firstname, surname, form, gender)
    VALUES ('', :classid, :userid, :firstname, :surname, :form, :gender)");

    $stmt->execute(array(':userid' => $userid, ':classid' => $classid, ':firstname' => $firstname, ':surname' => $surname, ':form' => $form, ':gender' => $gender));

Is there some way of telling it to insert an empty field if the variable is empty?

EDIT: To be clear, this is not a new statement. I had previously been INSERTing with unprepared MySQLi, and had only just got around to updating this bit of code to PDO.

EDIT 2: My schema:

id  int(11) NO  PRI     auto_increment  
encryption  int(1)  NO              
classid int(11) NO              
googleclassroom varchar(50) NO              
userid  varchar(50) NO              
firstname   varchar(100)    NO              
surname varchar(100)    NO              
form    varchar(10) NO              
gender  varchar(10) NO              
colour_code varchar(10) NO              
col1    varchar(50) NO              
col2    varchar(50) NO              
col3    varchar(50) NO              
col4    varchar(50) NO              
col5    varchar(50) NO              
col6    varchar(50) NO              
col7    varchar(50) NO              
timestamp   datetime    NO  
18
  • What do you man by not working? Are you getting any error? Can you log SQL exception? Commented Sep 1, 2017 at 3:14
  • Is it possible to insert values when user provides all values? Commented Sep 1, 2017 at 3:14
  • It's not inserting the row. There's no error, but I'm not sure I've got it set up to report errors. Commented Sep 1, 2017 at 3:15
  • @Billa If I enclose the $variables in quotation marks (in other words, turn them into values), it inserts fine Commented Sep 1, 2017 at 3:15
  • Allow your db to accept nulls? Commented Sep 1, 2017 at 3:15

4 Answers 4

2

You can check with following:

  1. Allow all columns to accept NULL
  2. If id is primary key, remove it from query. You can set it to AUTO_INCREMENT

EDIT:

As id is primary key in your schema, you can't set ''. Also it seems every column is not allowed to accept NULL and because user is not providing the value the variables like $form could be undefined or NULL so your INSERT statement won't execute.

EDIT 2:

Try to find out exact error. Whether it's for SQL or PHP.

You can set display error by using link

Sign up to request clarification or add additional context in comments.

3 Comments

The reason I don't think this is the solution is that an identical MySQL query in the old code worked fine. Adding or removing the id field has no impact on whether this works. The only thing that stops it running is if I bind an empty variable. The table certainly accepts NULL values, as thousands of rows with NULL values have been previously set.
can you set display error. here is the link how to set phpreferencebook.com/tips/display-all-php-errors-and-warnings
Undefined offset: 12 in ...PATH... on line 83
1

I think the problem you're having is due to your data getting bind as bindParam(). According to PHP doc's.

Binds a PHP variable to a corresponding named or question mark placeholder in the SQL statement that was used to prepare the statement.

So I like to suggest using bindValue() instead. To explain why I'm referring back to PHP manual.

Unlike PDOStatement::bindValue(), the variable is bound as a reference and will only be evaluated at the time that PDOStatement::execute() is called.

So my answer would be,

$stmt = $db->prepare("INSERT INTO students
    (classid, userid, firstname, surname, form, gender)
    VALUES (:classid, :userid, :firstname, :surname, :form, :gender)");

$stmt->bindValue(':classid',$classid);//If this is a must enter or not entered by user then you can use bindParam()
$stmt->bindValue(':userid',$userid);//This as well if this is a must enter then you can use bindParam()
if($firstname == "" || $firstname == NULL){
$stmt->bindValue(':firstname', NULL, PDO::PARAM_INT);//_NULL seems to give issues
}else{
$stmt->bindParam(':firstname', $firstname);
}
$stmt->bindValue(':surname',$surname);
$stmt->bindValue(':form',$form);
$stmt->bindValue(':gender',$gender);

$stmt->execute();

8 Comments

Thanks. I tried that, but it didn't seem to work. I added an if isset statement before each line to see if that helped, but that caused the PHP to stop. I need to investigate further to see why.
This might help I think you have go through an if ... else link
I updated the answer please check and let me know. You have to toggle the values.
It didn't work when I tried it, but I'm pretty tired so will call it quits for tonight and try tomorrow evening. Thank you all.
If you don't have null enabled it work you have enable null acceptance in you database columns.
|
1

Thank you for all the comments and answers. Several different users suggested allowing NULL values; @sand recommended I set the default value of these columns to NULL. It worked

I remain baffled as to how this has worked happily for months (using an unprepared MySQLi statement) but refused to accept these INSERTs from a PDO statement.

Anyway, the answer (all credit to @Sand, @Anshuman and @Lawrence) is to set the default column values to NULL, then it doesn't care if the variables are empty or not.

Comments

1

Rob, this is not an answer to your question! I read your comments and I just wanted to show you how to properly use error reporting, prepared statements and exception handling.

You can structure the code in OOP or procedural functions, if you wish. But the structure is of no relevance here.

My advice: always read the documentation of the functions yu are using. Especially the returned values. This way you'll know how to properly handle all possible error-prone situations. In regard of PDO, this is a mandatory step ;-)

Good luck!

<?php

/*
 * Set error reporting level and display the errors on screen.
 * --------------------------------------------------------------
 * DON'T DISPLAY ERRORS ON PRODUCTION, DO IT ONLY ON DEVELOPMENT!
 * --------------------------------------------------------------
 */
error_reporting(E_ALL);
ini_set('display_errors', 1);

try {
    /*
     * Create a PDO instance as db connection.
     */

    // Create PDO instance.
    $connection = new PDO(
            'mysql:host=localhost;port=3306;dbname=yourDb;charset=utf8'
            , 'yourDbUsername'
            , 'yourDbPassword'
    );

    // Assign driver options.
    $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
    $connection->setAttribute(PDO::ATTR_PERSISTENT, TRUE);

    /*
     * Prepare and validate the sql statement.
     * 
     * --------------------------------------------------------------------------------
     * If the database server cannot successfully prepare the statement, PDO::prepare() 
     * returns FALSE or emits PDOException (depending on error handling settings).
     * --------------------------------------------------------------------------------
     */
    $sql = 'INSERT INTO students (
                classid,
                userid,
                firstname,
                surname,
                form,
                gender
            ) VALUES (
                :classid,
                :userid,
                :firstname,
                :surname,
                :form,
                :gender
            )';

    $statement = $connection->prepare($sql);

    if (!$statement) {
        throw new UnexpectedValueException('The sql statement could not be prepared!');
    }

    /*
     * Bind the input parameters to the prepared statement.
     * 
     * -----------------------------------------------------------------------------------
     * Unlike PDOStatement::bindValue(), when using PDOStatement::bindParam() the variable 
     * is bound as a reference and will only be evaluated at the time that 
     * PDOStatement::execute() is called.
     * -----------------------------------------------------------------------------------
     */

    $bindings = array(
        ':classid' => $classid,
        ':userid' => $userid,
        ':firstname' => $firstname,
        ':surname' => $surname,
        ':form' => $form,
        ':gender' => $gender,
    );

    foreach ($bindings as $key => $value) {
        $bound = $statement->bindValue(
                getInputParameterName($key)
                , $value
                , getInputParameterDataType($value)
        );

        if (!$bound) {
            throw new UnexpectedValueException('An input parameter could not be bound!');
        }
    }

    /*
     * Execute the prepared statement.
     * 
     * ------------------------------------------------------------------
     * PDOStatement::execute returns TRUE on success or FALSE on failure.
     * ------------------------------------------------------------------
     */
    $executed = $statement->execute();

    if (!$executed) {
        throw new UnexpectedValueException('The prepared statement could not be executed!');
    }

    /*
     * Get last insert id.
     */
    $lastInsertId = $connection->lastInsertId();

    if (!isset($lastInsertId)) {
        throw new UnexpectedValueException('The last insert id could not be read!');
    }

    /*
     * Close connection.
     */
    $connection = NULL;

    /*
     * Print results.
     */
    echo 'Provided data successfully added with the id: ' . $lastInsertId;
} catch (PDOException $pdoException) {
    echo $pdoException->getMessage();
    exit();
} catch (Exception $exception) {
    echo $exception->getMessage();
    exit();
}

/**
 * Get the name of an input parameter by its key in the bindings array.
 *  
 * @param int|string $key The key of the input parameter in the bindings array.
 * @return int|string The name of the input parameter.
 */
function getInputParameterName($key) {
    return is_int($key) ? ($key + 1) : (':' . ltrim($key, ':'));
}

/**
 * Get the PDO::PARAM_* constant, e.g the data type of an input parameter, by its value.
 *  
 * @param mixed $value Value of the input parameter.
 * @return int The PDO::PARAM_* constant.
 */
function getInputParameterDataType($value) {
    $dataType = PDO::PARAM_STR;
    if (is_int($value)) {
        $dataType = PDO::PARAM_INT;
    } elseif (is_bool($value)) {
        $dataType = PDO::PARAM_BOOL;
    }
    return $dataType;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.