PHP/MySQL - How to delete insert if query fails? - php

I have this code
<?php
class Objekt{
// database connection and table name
private $conn;
private $table_name = "objects";
// object properties
public $id;
public $id_group;
public $title;
public $description;
public $lat;
public $lng;
public $icon;
public $tagsraw;
// constructor with $db as database connection
public function __construct($db){
$this->conn = $db;
}
function create(){
// query to insert record
$query = "INSERT INTO
" . $this->table_name . "
SET
id_group=:id_group, title=:title, description=:description, lat=:lat, lng=:lng, icon=:icon;
SELECT max(id) AS id FROM objects;";
// prepare query
$stmt = $this->conn->prepare($query);
// sanitize
$this->id_group=htmlspecialchars(strip_tags($this->id_group));
$this->title=htmlspecialchars(strip_tags($this->title));
$this->description=htmlspecialchars(strip_tags($this->description));
$this->lat=htmlspecialchars(strip_tags($this->lat));
$this->lng=htmlspecialchars(strip_tags($this->lng));
$this->icon=htmlspecialchars(strip_tags($this->icon));
// bind values
$stmt->bindParam(":id_group", $this->id_group);
$stmt->bindParam(":title", $this->title);
$stmt->bindParam(":description", $this->description);
$stmt->bindParam(":lat", $this->lat);
$stmt->bindParam(":lng", $this->lng);
$stmt->bindParam(":icon", $this->icon);
// execute query
if($stmt->execute()){
//$stmt->execute();
/*
ERROR below here
Uncaught PDOException: SQLSTATE[HY000]: General error
stack trace:
#0 PDOStatement ->fetch(2)
#1 Objekt->create()
*/
$result = $stmt->fetch(PDO::FETCH_ASSOC);
$this->id = $result['id'];
echo "$this->id";
//get tags matches
$tags = [];
$this->tagsraw = preg_replace('/\s+/', '', $this->tagsraw);
$tags = explode(',', $this->tagsraw);
$id_tags = [];
for ($i = 0; $i < sizeof($tags); $i++) {
$query = "SELECT id FROM category WHERE title=" . tags[i];
$stmt = $this->conn->prepare($query);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_OBJ);
$id_tags[i] = $result->id;
}
for ($i = 0; $i < sizeof($id_tags); $i++) {
$query = "INSERT INTO category_object SET id_object=" . $this->id . ", id_category=" . $id_tags[i];
$stmt = $this->conn->prepare($query);
$stmt->execute();
}
return true;
}
return false;
}
What I want to do:
Insert Object and create a relation to a category_object table over foreign keys id_object / id_category
What I would like to improve:
What do I have to do to delete an insert if it fails (where should I put a try/catch or something else)
What is wrong with my fetch? I Select the value from max(id) to get the value of my inserted object, why is it throwing an Exception
I have another Warning why is it telling me that
for ($i = 0; $i < sizeof($id_tags); $i++) {
$query = "INSERT INTO category_object SET id_object=" . $this->id . ", id_category=" . $id_tags[i];
$stmt = $this->conn->prepare($query);
$stmt->execute();
}
will be deprecated in future php versions..

Use transactions.
Any insert/update/delete done within a transaction will not be actually executed against the database until you call commit(), and you can always roll back the changes, abandoning them in case of failure.
In your code, you should be OK with replacing if($stmt->execute()){ with
try {
$this->conn->beginTransaction();
$stmt->execute();
// ...everything else from inside the if statement here...
$this->conn->commit();
return true;
} catch (\Exception $e) {
$this->conn->rollback();
return false;
}

Related

Error in php Connection Error: could not find driver

I got an error when I want to consume a Rest API using postaman and docker.
The error is marked in this line of code: Error in php Connection Error: could not find driver
<?php
class Materias {
// DB stuff
private $conn;
private $table = 'materias';
// Materias Properties
public $clave_materia;
public $nombre_materia;
public $semestre;
public $creditos;
// Constructor with DB
public function __construct($db) {
$this->conn = $db;
}
// Get Materias
public function read() {
// Create query
$query = 'SELECT * FROM ' . $this->table;
// Prepare statement
$stmt = $this->conn->prepare($query);
// Execute query
$stmt->execute();
return $stmt;
}
// Get Single Materia
public function read_single() {
// Create query
$query = 'SELECT * FROM ' . $this->table . 'WHERE clave_materia = ?';
// Prepare statement
$stmt = $this->conn->prepare($query);
// Bind clave_materia
$stmt->bindParam(1, $this->clave_materia);
// Execute query
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
// Set properties
$this->nombre_materia = $row['nombre_materia'];
$this->semestre = $row['semestre'];
$this->creditos = $row['creditos'];
}
// Create Post
public function create() {
// Create query
$query = 'INSERT INTO ' . $this->table . ' SET clave_materia = :clave_materia, nombre_materia = :nombre_materia, semestre = :semestre, creditos = :creditos';
// Prepare statement
$stmt = $this->conn->prepare($query);
// Clean data
$this->clave_materia = htmlspecialchars(strip_tags($this->clave_materia));
$this->nombre_materia = htmlspecialchars(strip_tags($this->nombre_materia));
$this->semestre = htmlspecialchars(strip_tags($this->semestre));
$this->creditos = htmlspecialchars(strip_tags($this->creditos));
// Bind data
$stmt->bindParam(':clave_materia', $this->clave_materia);
$stmt->bindParam(':nombre_materia', $this->nombre_materia);
$stmt->bindParam(':semestre', $this->semestre);
$stmt->bindParam(':creditos', $this->creditos);
// Execute query
if($stmt->execute()) {
return true;
}
// Print error if something goes wrong
printf("Error: %s.\n", $stmt->error);
return false;
}
// Update Post
public function update() {
// Create query
$query = 'UPDATE ' . $this->table . '
SET nombre_materia = :nombre_materia, semestre = :semestre, creditos = :creditos
WHERE clave_materia = :clave_materia';
// Prepare statement
$stmt = $this->conn->prepare($query);
// Clean data
$this->nombre_materia = htmlspecialchars(strip_tags($this->nombre_materia));
$this->semestre = htmlspecialchars(strip_tags($this->semestre));
$this->creditos = htmlspecialchars(strip_tags($this->creditos));
$this->clave_materia = htmlspecialchars(strip_tags($this->clave_materia));
// Bind data
$stmt->bindValue(':nombre_materia', $this->nombre_materia);
$stmt->bindValue(':semestre', $this->semestre);
$stmt->bindValue(':creditos', $this->creditos);
$stmt->bindValue(':clave_materia', $this->clave_materia);
// Execute query
if($stmt->execute()) {
return true;
}
// Print error if something goes wrong
printf("Error: %s.\n", $stmt->error);
return false;
}
// Delete Post
public function delete() {
// Create query
$query = 'DELETE FROM ' . $this->table . ' WHERE clave_materia = :clave_materia';
// Prepare statement
$stmt = $this->conn->prepare($query);
// Clean data
$this->clave_materia = htmlspecialchars(strip_tags($this->clave_materia));
// Bind data
$stmt->bindParam(':clave_materia', $this->clave_materia);
// Execute query
if($stmt->execute()) {
return true;
}
// Print error if something goes wrong
printf("Error: %s.\n", $stmt->error);
return false;
}
}
Error:
Connection Error: could not find driver<br />
<b>Fatal error</b>: Uncaught Error: Call to a member function prepare() on null in /var/www/html/models/Materias.php: 24
Stack trace:
#0 /var/www/html/api/materias/read.php(17): Materias->read()
#1 {main
}
thrown in <b>/var/www/html/models/Materias.php</b> on line <b>24</b><br />
**bd connection**
<?php
class Database {
// DB Params
private $host = 'localhost';
private $db_name = 'reticula';
private $username = 'root';
private $password = 'test ';
private $conn;
// DB Connect
public function connect() {
$this->conn = null;
try {
$this->conn = new PDO('mysql:host=' . $this->host . ';dbname=' . $this->db_name, $this->username, $this->password);
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
echo 'Connection Error: ' . $e->getMessage();
}
return $this->conn;
}
}
This is where you call matters
<?php
// Headers
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json');
include_once '../../config/Database.php';
include_once '../../models/Materias.php';
// Instantiate DB & connect
$database = new Database();
$db = $database->connect();
// Instantiate blog post object
$materias = new Materias($db);
// Blog post query
$result = $materias->read();
// Get row count
$num = $result->rowCount();
// Check if any posts
if($num > 0) {
// Post array
$materias_arr = array();
// $posts_arr['data'] = array();
while($row = $result->fetch(PDO::FETCH_ASSOC)) {
extract($row);
$materias_item = array(
'clave_materia' => $clave_materia,
'nombre_materia' => $nombre_materia,
'semestre' => $semestre,
'creditos' => $creditos
);
// Push to "data"
array_push($materias_arr, $materias_item);
// array_push($posts_arr['data'], $post_item);
}
// Turn to JSON & output
echo json_encode($materias_arr);
} else {
// No Posts
echo json_encode(
array('message' => 'No Posts Found')
);
}

PHP database connection class bind_param

I would like to write a database connection class and I dont understand how I have to write the select method with bind_param-s. Here is the full code. And here the part of the code where I need the help:
public function select($sql){
$db = $this->connect(); //This methos connect to the DB
$stmt = $db->prepare($sql);
if($stmt === false){ //If the prepare faild
trigger_error("Wrong SQL", E_USER_ERROR);
}
$error = $stmt->bind_param("i", $id);
if($error){
return "Error: ".$stmt->error, $stmt->errno;
}
$err = $stmt->execute();
if($error){
return "Error: ".$stmt->error, $stmt->errno;
}
$result = $stmt->bind_result($id);
$stmt->close();
$dbConnection->closeConnection($db);
return $result;
}
I need to got it parameters or how can I slove it?
You need to pass your values into this function too. And eventually bind them into prepared statement.
Optionally you can pass string with types, but by default all "s" will do.
Also remember that you should connect only ONCE per script execution. and then use one single connection all the way throughout your code.
And get rid of all these error checks. Set mysqli in exception mode instead.
public function q($sql, $values = array(), $types = NULL)
{
$stm = $this->mysql->prepare($sql);
if (!$types)
{
$types = str_repeat("s", count($values));
}
if (strnatcmp(phpversion(),'5.3') >= 0)
{
$bind = array();
foreach($values as $key => $val)
{
$bind[$key] = &$values[$key];
}
} else {
$bind = $values;
}
array_unshift($bind, $types);
call_user_func_array(array($stm, 'bind_param'), $bind);
$stm->execute();
return $stm->get_result();
}
so it can be used like this
$res = $db->q("SELECT name FROM users WHERE id=?", [$id]);
or
$res = $db->q("SELECT name FROM users WHERE id=?", [$id], "i");
your other functions have to be changed as well.
class DB{
public $con;
function __construct()
{
$this->con = new mysqli("localhost", "root", "", "proba_fferenc");
}
public function select(...)
{
// as shown above
}
}

SELECT_IDENTITY() not working in php

Scenario:
I have a SQL Query INSERT INTO dbo.Grades (Name, Capacity, SpringPressure) VALUES ('{PHP}',{PHP}, {PHP})
The data types are correct.
I need to now get the latest IDENTIY which is GradeID.
I have tried the following after consulting MSDN and StackOverflow:
SELECT SCOPE_IDENTITY() which works in SQL Management Studio but does not in my php code. (Which is at the bottom), I have also tried to add GO in between the two 'parts' - if I can call them that - but still to no avail.
The next thing I tried, SELECT ##IDENTITY Still to no avail.
Lastly, I tried PDO::lastInsertId() which did not seem to work.
What I need it for is mapping a temporary ID I assign to the object to a new permanent ID I get back from the database to refer to when I insert an object that is depended on that newly inserted object.
Expected Results:
Just to return the newly inserted row's IDENTITY.
Current Results:
It returns it but is NULL.
[Object]
0: Object
ID: null
This piece pasted above is the result from print json_encode($newID); as shown below.
Notes,
This piece of code is running in a file called save_grades.php which is called from a ajax call. The call is working, it is just not working as expected.
As always, I am always willing to learn, please feel free to give advice and or criticize my thinking. Thanks
Code:
for ($i=0; $i < sizeof($grades); $i++) {
$grade = $grades[$i];
$oldID = $grade->GradeID;
$query = "INSERT INTO dbo.Grades (Name, Capacity, SpringPressure) VALUES ('" . $grade->Name . "',". $grade->Capacity .", ".$grade->SpringPressure .")";
try {
$sqlObject->executeNonQuery($query);
$query = "SELECT SCOPE_IDENTITY() AS ID";
$newID = $sqlObject->executeQuery($query);
print json_encode($newID);
} catch(Exception $e) {
print json_encode($e);
}
$gradesDictionary[] = $oldID => $newID;
}
EDIT #1
Here is the code for my custom wrapper. (Working with getting the lastInsertId())
class MSSQLConnection
{
private $connection;
private $statement;
public function __construct(){
$connection = null;
$statement =null;
}
public function createConnection() {
$serverName = "localhost\MSSQL2014";
$database = "{Fill In}";
$userName = "{Fill In}";
$passWord = "{Fill In}";
try {
$this->connection = new PDO( "sqlsrv:server=$serverName;Database=$database", $userName, $passWord);
$this->connection->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
}
catch( PDOException $e ) {
die("Connection Failed, please contact system administrator.");
}
if ($this->connection == null) {
die("Connection Failed, please contact system administrator.");
}
}
public function executeQuery($queryString) {
$results = array();
$this->statement = $this->connection->query( $queryString );
while ( $row = $this->statement->fetch( PDO::FETCH_ASSOC ) ){
array_push($results, $row);
}
return $results;
}
public function executeNonQuery($queryString) {
$numRows = $this->connection->exec($queryString);
}
public function getLastInsertedID() {
return $this->connection->lastInsertId();
}
public function closeConnection() {
$this->connection = null;
$this->statement = null;
}
}
This is PDO right ? better drop these custom function wrapper...
$json = array();
for ($i=0; $i < sizeof($grades); $i++) {
//Query DB
$grade = $grades[$i];
$query = "INSERT INTO dbo.Grades (Name, Capacity, SpringPressure)
VALUES (?, ?, ?)";
$stmt = $conn->prepare($query);
$success = $stmt->execute(array($grade->Name,
$grade->Capacity,
$grade->SpringPressure));
//Get Ids
$newId = $conn->lastInsertId();
$oldId = $grade->GradeID;
//build JSON
if($success){
$json[] = array('success'=> True,
'oldId'=>$oldId, 'newId'=>$newId);
}else{
$json[] = array('success'=> False,
'oldId'=>$oldId);
}
}
print json_encode($json);
Try the query in this form
"Select max(GradeID) from dbo.Grades"

PDO - executed query with binded parameters yields no results?

I am trying to create a PHP array of random "fruits" from a database.
The database class that I am using:
class Db
{
private static $_instance = null;
private $_pdo;
private function __construct()
{
try {
$this->_pdo = new PDO('mysql:host=' . DB_HOST . ';dbname=' . DB_NAME .'', DB_USER, DB_PASS);
} catch (PDOException $e) {
die($e->getMessage());
}
}
public static function getInstance()
{
if (!isset(self::$_instance)) {
self::$_instance = new Db();
}
return self::$_instance;
}
public function prepare($sql)
{
return $this->_pdo->prepare($sql);
}
}
The class that is using the database to fetch "fruits" to create an array of a given size of random entries by using 3 seperate queries to calculate and retrieve "x" number of random items form the database.
class FruitBasket
{
private $_fruitArray = array(),
$_inputCode,
$_db;
public function __construct($input = null)
{
$this->_inputCode = $input;
$this->_db = Db::getInstance();
var_dump($this->_db);
}
public function pickFruit($count)
{
$doubleCount = $count * 2;//double the count used in calculation with the random number
$fruitIDs = ''; //the choosen fruits (id's)
$i = 0;
//#1 get total count of fruits table
$sql = "SELECT COUNT(*) FROM `fruits`";
if ($query = $this->_db->prepare($sql)) {
if ($query->execute()) {
$allFruits = $query->fetch(PDO::FETCH_NUM);
} else {
print_r("ERROR QUERY DID NOT EXECUTE #1");
}
} else {
print_r("ERROR CHECK SQL SYNTAX #1");
}
//#2 calculate random number to pull from all of id's
$sql = "SELECT id FROM `fruits` WHERE RAND()* ? < ? ORDER BY RAND() LIMIT 0, ? ";
if ($query = $this->_db->prepare($sql)) {
$query->bindParam(1, $allFruits[0], PDO::PARAM_INT);
$query->bindParam(2, $doubleCount, PDO::PARAM_INT);
$query->bindParam(3, $count, PDO::PARAM_INT);
if ($query->execute()) {
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
if ($i == 0) {
$fruitIDs .= "'" . $row['id'] . "'";
} else {
$fruitIDs .= ", '" . $row['id'] . "'";
}
$i++;
}
} else {
print_r("ERROR QUERY DID NOT EXECUTE #2");
}
} else {
print_r("ERROR CHECK SQL SYNTAX #2");
}
//#3 get the fruits
$sql="SELECT NAME FROM `fruits` WHERE `id` IN( ? )";
if ($query = $this->_db->prepare($sql)) {
$query->bindParam(1, $fruitIDs, PDO::PARAM_STR);
if ($query->execute()) {
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
$this->_fruitArray[] = $row['name'];
}
} else {
print_r("ERROR QUERY DID NOT EXECUTE #3");
}
} else {
print_r("ERROR CHECK SQL SYNTAX #3");
}
return $this->_fruitArray;
}
}
The table that I am attempting has a bunch of "fruits" in it, an example of how the table is structured:
==================================
| ID | NAME |
==================================
| 01 | Apple |
==================================
I am attempting to test this all out by using the following:
echo "<pre>";
echo "TESTING FRUIT ARRAY:</br></br>";
$basket = new FruitBasket();
echo"</br></br> PRINT_R: </br></br>";
print_r($basket->pickFruit(10));
echo "</br></br> VARDUMP: </br></br>";
var_dump($basket->pickFruit(10));
The sql query prepares and executes properly, I can do a vardump of the prepares and the binds and they return TRUE. Nothing is returned on the last query however.
In the first query that executes Doing a print statement of $allFruits shows the correct total count from the table.
The second query seems to be working properly,the string $fruitIDs, gets random id's from the table, I can echo this out and confirm that indeed the correct number of ID's are returned.
The problem occurs (I think) with the third query:
Nothing is returned form this query. The prepare statement returns true on a var dump as does the execute, however there is no results!
If I manually take the ID's that are output from query#2 and run it myself in mysql, the correct "fruit" names are returned.
Am I binding the variables incorrectly? I read the pages from the PHP manual but clearly I am doing something wrong.
Please help! :)
Thanks to the links and input provided by Your common sense, using the following:
Reference - frequently asked questions about PDO
and
Can I bind an array to an IN() condition?
I was able to resolve this by changing my query as follows:
//#2 calculate random number to pull from all of id's
$sql = "SELECT id FROM `fruits` WHERE RAND()* ? < ? ORDER BY RAND() LIMIT 0, ? ";
if ($query = $this->_db->prepare($sql)) {
$query->bindParam(1, $allFruits[0], PDO::PARAM_INT);
$query->bindParam(2, $doubleCount, PDO::PARAM_INT);
$query->bindParam(3, $count, PDO::PARAM_INT);
if ($query->execute()) {
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
$fruitIDs[] = $row['id'];
}
} else {
print_r("ERROR QUERY DID NOT EXECUTE #2"); }
} else {
print_r("ERROR CHECK SQL SYNTAX #2");
}
//#3 get the fruits
$inQuery = implode(',', array_fill(0, count($fruitIDs), '?'));
$sql="SELECT NAME FROM `fruits` WHERE `id` IN($inQuery)";
if ($query = $this->_db->prepare($sql)) {
if ($query->execute($fruitIDs)) {
while ($row = $query->fetch(PDO::FETCH_NUM)) {
$this->_fruitArray[] = $row[0];
}
} else {
print_r("ERROR QUERY DID NOT EXECUTE #3");
}
} else {
print_r("ERROR CHECK SQL SYNTAX #3");
}
return $this->_fruitArray;
}
I do not fully understand the security benefits or ramifications of binding the parameters or simply including them in the actual execute() but for now the query is performing as intended, so thank you for the input!

How do you perform a dynamic php, PDO prepared statement Update?

I'm having trouble finding good documentation on pdo update prepared statements and even more trouble finding documentation on dynamically updating the database with pdo prepared statements. I've gotten my dynamic insert to work but am having trouble with the update. The error I'm getting is:
Warning: PDOStatement::execute() [pdostatement.execute]:
SQLSTATE[HY093]: Invalid parameter number: parameter was not defined
in
/Users/scottmcpherson/Sites/phpsites/projectx/application/models/db.php
on line 91 error
Here is the class I created minus a couple of methods that are irrelevant to this problem:
<?php
require_once("../config/main.php");
class Database{
protected static $dbFields = array('username', 'password');
public $db;
public $tableName = 'users';
public $id = 1;
public $username = "Jonny";
public $password = "Appleseed";
public function __construct() {
$this->connect();
}
public function connect(){
try {
$this->db = new PDO("mysql:host=".DB_SERVER."; dbname=".DB_NAME, DB_USER, DB_PASS);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
}
public function properties() {
$properties = array();
foreach (self::$dbFields as $field) {
if (isset($this->field) || property_exists($this, $field)) {
$properties[$field] = $this->$field;
}
}
return $properties;
}
public function propertyValues() {
$property = $this->properties();
$propertyValues = array();
foreach ($property as $key => $value) {
$propertyValues = ":" . implode(", :", array_keys($property));
}
return $propertyValues;
}
public function polishedVals(){
// The end result of this function is:
// username=:username, password=:password
$props = $this->properties();
$phaseOne = array();
foreach ($props as $key => $value) {
$phaseOne[$key] = ":".$key;
}
$phaseTwo = array();
foreach ($phaseOne as $key => $value) {
$phaseTwo[] = "{$key}={$value}";
}
$polishedVals = implode(", ", $phaseTwo);
return $polishedVals;
}
public function update(){
$stmt = "UPDATE ". $this->tableName." SET ";
$stmt .= $this->polishedVals();
$stmt .= "WHERE id=" . $this->id;
$stmt = $this->db->prepare($stmt);
if($stmt->execute($this->properties())) {
echo "yes";
} else {
echo "error ";
}
}
}
$database = new Database();
echo$database->update();
?>
With all the variables replaced with the actual values, the result I'm going for with the update() method would look like this:
public function update(){
$stmt = "UPDATE users SET ";
$stmt .= "username=:username, password=:password ";
$stmt .= "WHERE id=1";
$stmt = $this->db->prepare($stmt);
if($stmt->execute($this->properties())) {
echo "yes";
} else {
echo "error ";
}
}
In addition to spotting this problem, please let me know if you see any other issues with this code. I'm still kind of new to PHP.
Edit: I've now created a new method that adds a : to the beginning of each key in the properties array:
public function colProperties(){
$properties = $this->properties();
$withCols = array();
foreach($properties as $key => $value){
$withCols[":".$key] = $value;
}
return $withCols;
}
So my update() method now looks like:
public function update(){
$stmt = "UPDATE ". $this->tableName." SET ";
$stmt .= $this->polishedVals();
$stmt .= "WHERE id=" . $this->id;
$stmt = $this->db->prepare($stmt);
if($stmt->execute($this->colProperties())) {
echo "yes";
} else {
echo "error ";
}
}
and if I var_dump($this->colProperties) I get:
array(2) { [":username"]=> string(5) "Jonny" [":password"]=> string(9) "Appleseed" }
And still getting the same error.
I don't think that passing parameters to an UPDATE query requires a different method than a SELECT one. The information in the PDOStatement->execute() manual page should apply:
<?php
/* Execute a prepared statement by passing an array of insert values */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE calories < :calories AND colour = :colour');
$sth->execute(array(':calories' => $calories, ':colour' => $colour));
?>
You are using named parameters so execute() expects an associative array. Use var_dump() to display $this->properties() right before execute():
var_dump($this->properties())
Make sure you keys match exactly.
The error is that in between
$stmt .= $this->polishedVals();
$stmt .= "WHERE id=" . $this->id;
There needs to be a space in between the WHERE clause as the polishedVals() method does not add a space after the implode. So, you'll have something like
UPDATE User SET city=:city, location=:locationWHERE User.id=28
Which causes the error.
Simple bug.

Categories