Novice to Ninja 6th edition: Undefined index: id

Hello @TomB, am here again and I hope am not really a pain in your neck(with my frequent questions)?
Yesterday, while working on the registration page of app(am doing this just to do what you said in your book that we should at least tackle 2 to 3 projects just to assimilate the ideas in your book). I run into the above problem also define here

Notice: Undefined index:id

(I will attach some images of the above problem ).

The 1st image is what it displayed when I tried to set the id using isset $suer['id'] = null or $user['id'] = '', while the 2nd image is what it complained of when I comment out the if statement inside the try block of the

save() method

and the 3rd image is what it displayed when I just use $user['id'] = null without using isset

I can’t help thinking it would be useful to see your code…

I will still post the codes. Just want to let you(plural) know what have tried to solve it

This is

register.html.php

if(!empty($errors)):?>
    <div class="error">
        <p>Your account could not be created due to the following reasons:</p>
        <ul>
            <?php
            foreach ($errors as $error):?>
                <li><?=$error?></li>
            <?php
            endforeach;?>
        </ul>
    </div>
<?php endif;?>

<div id="reg">
    <form id="theForm" method="post" action="" accept-charset="utf8">
        <fieldset id="">
            <legend>Registration form</legend>
            <ul class="">
                <li>
                    <input type="hidden" id="id" name="user['id']" value="<?=$user['id'] ?? ''?>">
                    <label for="nickName">Nickname</label>
                    <input type="text" id="nickName" name="user['nickName']" placeholder="Nickname or username here" value="<?=$user['nickName'] ?? ''?>" required autofocus>
                </li>
                <li>
                    <label for="email">Email</label>
                    <input type="email" id="email" name="user['email']" placeholder="Email here" value="<?=$user['email'] ?? ''?>" required>
                </li>
                <li>
                    <label for="password">Password</label>
                    <input type="password" id="password" name="user[password]" placeholder="Password here" value="">
                </li>
                
                <li>
                    <input type="submit" name="submit" id="register" value="Register">
                </li>
            </ul>
        </fieldset>
    </form>
</div>

and this is the Register.php controller(registerUser method)

public function registerUser(){
         $user = $_POST['user'];

         # Assume the data is valid to begin with.
         $valid = true;
         $errors = [];

         # If any field has been left blank, set valid to false
         if(empty($user['firstName'])){
             $valid = false;
             $errors = 'FIrstname can\'t be empty';
         }elseif($user['FirstName'] < strlen(3)){
             $valid = false;
             $errors = 'Firstname can\'t be less than 3 characters';
         }

         if(empty($user['lastName'])){
            $valid = false;
            $errors = 'Lastname can\'t be empty';
        }elseif($user['lastName'] < strlen(3)){
            $valid = false;
            $errors = 'Lastname can\'t be less than 3 characters';
        }

        if(empty($user['nickName'])){
            $valid = false;
            $errors = 'Nickname can\'t be empty';
        }elseif($user['nickName'] < strlen(3)){
            $valid = false;
            $errors = 'Nickname can\'t be less than 3 characters';
        }else{
            $user['nickName'] = strtolower($user['nickName']);

            # Search if nickname is taken
            if(count($this->usersTable->find('nickName', $user['nickName'])) > 0){
                $valid = false;
                $errors = 'Nickname is already taken';
            }
        }

        if(empty($user['email'])){
            $valid = false;
            $errors = 'Email field can\'t be empty';
        }elseif(filter_var($user['email'], FILTER_VALIDATE_EMAIL) == false){
            $valid = false;
            $errors = 'Invalid email address';
        }else{
            $user['email'] = strtolower($user['email']);

            # Search if email is in use
            if(count($this->usersTable->find('email', $user['email'])) > 0){
                $valid = false;
                $errors = 'Email is in use';
            }
        }
        /*if(empty($user['id'])){
            $user['id'] = null;
        }*/
        //$user['password'] = password_hash($user['password'], PASSWORD_DEFAULT);
        //var_dump($user);
        # If no error is found
        if($valid = true){
            #var_dump($user);
            //$user['id'] = '';
            $user['password'] = password_hash($user['password'], PASSWORD_DEFAULT);
            $user['joinedDate'] = new \DateTime();
            $user['lastActivity'] = new \DateTime();
            $user['pic'] = 'abc';
            $user['status'] = 'EMAIL_NOT_VERIFIED';
            $user['isMod'] = 0;
            $user['userStatus'] = 'ACCEPTED';
            $user['location'] = 'ode';
            
            var_dump($this->usersTable->save($user));

            //$this->usersTable->save($user);
            //header('location: /user/success');
        }else{
            return [
                'template' => 'register.html.php',
                'title' => 'Register for an account',
                'variable' => [
                    'errors' => $errors,
                    'user' => $user
                ]
            ];
        }
     }

I hope the code is not getting long

This is DatabaseTable.php(insert method and save method)

private function insert($fields) {
        $query = 'INSERT INTO `' . $this->table . '` (';

        foreach ($fields as $key => $value) {
            $query .= '`' . $key . '`,';
        }

        $query = rtrim($query, ',');

        $query .= ') VALUES (';


        foreach ($fields as $key => $value) {
            $query .= ':' . $key . ',';
        }

        $query = rtrim($query, ',');

        $query .= ')';

        $fields = $this->processDates($fields);

        $this->query($query, $fields);

        return $this->pdo->lastInsertId();
    }

save method

public function save($record) {
        $entity = new $this->className(...$this->constructorArgs);

        try {
            if ($record[$this->primaryKey] == '') {//What PHP is complaining of
                $record[$this->primaryKey] = null;
            }
            $insertId = $this->insert($record);

            $entity->{$this->primaryKey} = $insertId;
        }
        catch (\PDOException $e) {
            $this->update($record);
        }

        foreach ($record as $key => $value) {
            if (!empty($value)) {
                $entity->$key = $value; 
            }           
        }

        return $entity; 
    }

can you print the output of

var_dump($_POST);

in the registerUser method?

@TomB Thanks sir for your swift response

I have on several occassion var_dump($user) and on each occassion, it displayed everything including the manually created $user[‘id’]

if you look at the images closely, at the bottom of each page, it has this sql error

database error sqlstate[hy093] Invalid parameter number: number of bound variable does not match the number of token

Is this line a typo in `registerUser()?

        # If no error is found
        if($valid = true){

And is it intentional that some of the form fields (firstname, lastname) are not in your form, but are in your validation in registerUser(), which surely means it will fail validation every time?

Each time you find an error, you overwrite any previous error message, so your user will only ever see the last error you find, which would be irritating as a user. I suspect that’s a typo, as the section of code that displays the errors loops through as if it is an array, but your validation is not adding to an array, it is just overwriting a string.

1 Like

This line was originally this if($valid == true){ but I have to change it to this if($valid = true){ since the form will not submit when it remains at $valid == true

Goodness no. You don’t want to replace the loose equality check with an assignment.

The difference is
“when this variable has the value true, do this”
vs.
“when this variable is given the value true, do this”

The “fix” may “work” up to a point, but what really should be done is finding out why that variable does not have the value true at that point in the code.

What then should I do to know why the variable does not have the value true?

You can see in the code initial value assignments for the valid and errors variables

$valid = true; 
$errors = []; 

then followed by various checks that if a problem is found assign false to the valid variable and incorrectly reassign a string to the errors variable instead of adding a string to the initial empty array.

It looks like the else should be showing errors whenever there are any

else{
            return [
                'template' => 'register.html.php',
                'title' => 'Register for an account',
                'variable' => [
                    'errors' => $errors,
                    'user' => $user
                ]
            ];
        }

If that is not happening my guess is that code is expecting an array and not a string and fails because of that.

Try fixing the $errors assignments. i.e.
NOT LIKE
$errors = 'some message';
but as so
$errors[] = 'some message';

have done that when you pointed out the mistake and have returned it to if($valid == true){. It still don’t submit

if($valid == true)

It’s not a big deal but it should be pointed out that this style if code is redundant. The if function by default already checks for truthiness. There is no need whatsoever to do an additional truthy comparison check. You can simply just do like so.

if($valid)

As far as the code you posted in #6, the whole $valid/true -valid/false stuff is not necessary at all. (Yeah, I know it’s in the book)You already and correctly start with an empty error array. All you need to do after your validations is check whether the errors array is still empty or not.

You would just do like so

if($errors){
//Redisplay form with errors
}
else{
// Handle DB action
}

If you are doing a ‘return’ as the book shows, the else is not even needed and would be more like:

if($errors){
//Return form with errors
}
// Handle DB action

@droopsnoot has already pointed out that your if checks are incorrect as they overwrite eachother. It is also not what the book shows you. The errors need to be arrays just like the book shows like so:

$errors[] = 'Some Error.'

Because you’re validating fields that don’t exist in your form. You need to remove the checks on those fields, then it might submit.

Also, this syntax seems strange to me:

elseif($user['nickName'] < strlen(3)){

It’s in a few places.

This brings up a good point. The OP is effectively banning anyone named Al, Bo, Ed, Mo, etc, etc…

I think the point is it should be like:-

elseif(strlen($user['nickName']) < 3){...} // for a string less than 3 long

What they have is effectively:-

elseif($user['nickName'] < 1){ // strlen(3) === 1

Which makes sense, checking for an empty field, but a very odd way of doing it.

2 Likes