need help in password reset system in codeigniter4

i am new in MVC programming, especially CodeIgniter4, i made a login/register system, it works fine,
and i implemented a password reset system :
1- when a user click reset password link a form appears
2- u put your email address and u click submit (the data is sent for validation)
3- if the email is valid and exist in the database a reset link is sent to the user,
4- when the user click the link it redirect him to the reset form
5- u enter your new password and confirm

her is my code :

1- route.php

$routes->post('sendresetlink', 'ResetPwdController::sendresetlink');
$routes->get('redirect/(:num)/(:any)', 'ResetPwdController::loadResetPage/$1/$2'); // 1st param is id, 2nd is token
$routes->post('resetpassword', 'ResetPwdController::updatepassword');

2- ResetPwdController.php

//---------------------------->->- Reset password : step 1
    public function sendresetlink()
    {
        $userModel = new UserModel();
        $toEmail = $this->request->getVar('email2');
        $row = $userModel->where('email', $toEmail)->first();
        if ($row) {
            $mail = new PHPMailer(true);
            try {
                //Server settings
                // $mail->SMTPDebug = SMTP::DEBUG_SERVER;                   //Enable verbose debug output
                $mail->isSMTP();                                            //Send using SMTP
                $mail->Host       = 'smtp.gmail.com';                       //Set the SMTP server to send through
                $mail->SMTPAuth   = true;                                   //Enable SMTP authentication
                $mail->Username   = '[email protected]';               //SMTP username
                $mail->Password   = 'E123456';                           //SMTP password
                $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;            //Enable implicit TLS encryption
                $mail->Port       = 465;                                    //TCP port to connect to; use 587 if you have set `SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS`
                $mail->CharSet = 'UTF-8';

                //Recipients
                $date = getServerTimestamp();
                $mail->setFrom('[email protected]', "Reset password email {$date}");
                $mail->addAddress($toEmail);     //Add a recipient

                // $pwd = generate_password(8);
                $token = generate_token();
                $id = $row["id"];
                $url   = generate_url("redirect", $id, $token);
                $link  = "<a href='" .  $url . "'>reset password</a>";
                $mail->isHTML(true);
                //Set email format to HTML
                $mail->Subject = 'Password recovery';
                $mail->Body    = 'Your new password :  click the link below ' . $link;
                $mail->AltBody = 'Your new password :  click the link below ' . $link;
                $mail->send();
                $data = ['token' => $token, 'token_active' => 1, 'token_date' => getServerTimestamp()];
                $userModel->where('id', $id)
                    ->set($data)
                    ->update();
                echo json_encode(1);  //ok msg sent with token
            } catch (Exception $e) {
                $ajaxresponse = $mail->ErrorInfo;
                echo json_encode($ajaxresponse); //message not sent 
            }
        } else {
            echo json_encode(2); //wrong email adress
        }
    }
    //---------------------------->->- Reset password : step 2  
    public function loadResetPage($id, $token)
    {
        $userModel = new UserModel();
        $row = $userModel->find($id);

        if ($row) {
            if (isValidToken($row, $token)) {
                $data = array('id' => $id, 'token' => $token);
                echo view('resetpassword', $data); // return view => call a view , return redirect => route
                //    return redirect()->to('ResetPwdController');
            } else {
                echo "<h1>Error 1 : Bad link !</h1>";
                return 0;
            }
        } else {
            echo "<h1>Error 2 : Bad user !</h1>";
            return 0;
        }
    }

    //------------------------------ Reset password : step 3
    public function updatepassword()
    {
        $rules = [
            'password' => [
                'label' => 'Password',
                'rules' => 'required|min_length[4]|max_length[50]|alpha_numeric',
                'errors' => [
                    'required' => 'password is required',
                    'min_length' => 'min length is 5',
                    'max_length' => 'max length is 50',
                    'alpha_numeric' => 'add alpha and numeric',
                ]
            ],
            'confpwd' => [
                'label' => 'Confirm Password',
                'rules' => 'required|matches[password]',
                'errors' => [
                    'required' => 'retype password',
                    'matches' => 'password dont matches',
                ],
            ]
        ];

        if ($this->validate($rules)) {
            $userModel = new UserModel();
            $id = $this->request->getVar('id');
            $token = $this->request->getVar('token');
            $row = $userModel->find($id);
            if ($row) {
                if (isValidToken($row, $token)) {
                    $data = [
                        'password' => password_hash($this->request->getVar('password'), PASSWORD_DEFAULT),
                        'token_active' => 0,
                        'token' => '',
                        'token_date' => ''
                    ];
                    $userModel->update($id, $data);
                    echo json_encode(1);
                } else {
                    echo json_encode(2); //bad token or expired link
                }
            } else {
                echo json_encode(3); //bad id link
            }
        } else {
            echo json_encode($this->validator->listErrors());
            
        }
    }

3 – my custom helper

    function generate_token($len = 25)
{
  $dumpdata = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  $token = substr(str_shuffle($dumpdata), 0, $len);
  return $token;
}
//------------------------------------------------------
function generate_password($len = 8)
{
  $dumpdata = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#&$%_";
  $token = substr(str_shuffle($dumpdata), 0, $len);
  return $token;
}
//------------------------------------------------------
function generate_url(...$segments)
{
  $url = base_url();
  foreach ($segments as $seg) {
    $url .= '/' . $seg;
  }
  return $url;
}
//------------------------------------------------------
function getServerTimestamp()
{
  date_default_timezone_set('Africa/Algiers');
  $stamp =  date('Y-m-d H:i:s');
  return $stamp;
}
//------------------------------------------------------
function isValidToken($data, $token, $period=24)
{
  $date = date(getServerTimestamp());
  $diff = date_diff2($data['token_date'], $date);
  //print_r("Reset  : {$row['token_date']}<br>Server : {$date}<br>Period : {$diff['asString']}");
  $period_second = $period*60*60;
  return ($data['token'] == $token &&
    $data['token_active'] == 1 &&
    $diff['exceeded'] > 0 &&
    $diff['exceeded'] <= $period_second //validation limit $period_second = $period*60*60
  );
}
//------------------------------------------------------
function date_diff2($date1, $date2)
{
  $diff = strtotime($date2) - strtotime($date1);

  if ($diff <= 0) {
    $asString = "[<br> <b>valid :</b> 0 <br><b>Period : </b>00:00:00:00:00:00<br> Diff : </b>" . strval($diff) . "<br>]";
    $result = array(
      'valid' => 0,
      'year' => 0,
      'month' => 0,
      'days' => 0,
      'hours' => 0,
      'minutes' => 0,
      'seconds' => 0,
      'exceeded' => $diff,
      'asString' => $asString
    );
    return $result;
  }

  $years = floor($diff / (365 * 60 * 60 * 24));
  $months = floor(($diff - $years * 365 * 60 * 60 * 24) / (30 * 60 * 60 * 24));
  $days = floor(($diff - $years * 365 * 60 * 60 * 24 - $months * 30 * 60 * 60 * 24) / (60 * 60 * 24));
  $hours = floor(($diff - $years * 365 * 60 * 60 * 24 - $months * 30 * 60 * 60 * 24 - $days * 60 * 60 * 24) / (60 * 60));
  $minutes = floor(($diff - $years * 365 * 60 * 60 * 24 - $months * 30 * 60 * 60 * 24 - $days * 60 * 60 * 24 - $hours * 60 * 60) / 60);
  $seconds = floor(($diff - $years * 365 * 60 * 60 * 24 - $months * 30 * 60 * 60 * 24 - $days * 60 * 60 * 24 - $hours * 60 * 60 - $minutes * 60));
  $asString = '[ <br> <b>valid :</b> 1 <br><b>Period : </b>' .
    strval($years) . ":" .
    strval($months) . ":" .
    strval($days) . ":" .
    strval($hours) . ":" .
    strval($minutes) . ":" .
    strval($seconds) . "<br> <b>Diff : </b>" .
    strval($diff) . "<br>]";
  $result = array(
    'valid' => 1,
    'year' => $years,
    'month' => $months,
    'days' => $days,
    'hours' => $hours,
    'minutes' => $minutes,
    'seconds' => $seconds,
    'exceeded' => $diff,
    'asString' => $asString
  );
  return $result;
}

my issue is as so i am getting an error :
when submitting data in reset form the it don’t send data to the right route and the URL in the browser is the same as the email link
help please
i do not know if it is because of echo view or my logic is wrong?