From 7df93f95db39d43d5d92f0e258884c839b4852ba Mon Sep 17 00:00:00 2001 From: Dominique Fournier Date: Fri, 19 Sep 2014 07:23:06 +0000 Subject: [PATCH] dblayer : push the verify before insert/update in a public method, callable from in HTML before doing the work. Keep the verifications before doing the real modification of the database git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@1829 bf3deb0d-5f1a-0410-827f-c0cc1f45334c --- dblayer.php | 558 ++++++++++++++++++++++++---------------------------- 1 file changed, 252 insertions(+), 306 deletions(-) diff --git a/dblayer.php b/dblayer.php index 4c61235..ace2070 100644 --- a/dblayer.php +++ b/dblayer.php @@ -195,6 +195,243 @@ class dblayer extends PDO return $res; } + /** Verify if the provided datas can be inserted/updated in the database. + @param $datas An array containing the datas to verify with keys + @param $updatekey the key to update + @return an array with the errors */ + public function verify ($datas, $updatekey=false) + { + // TODO : See if the edited key is needed in UPDATE mode + $errors = array (); + foreach ($this->fields as $key=>$params) + { + if ($updatekey !== false) + { + // Don't check if there is an update : the database is already filled + if (in_array ("not null", $params) && !array_key_exists ($key, $datas)) + { + $errors[$key] = sprintf (dgettext("domframework", + "Mandatory field '%s' not provided"), + $key); + continue; + } + if (in_array ("not null", $params) && $datas[$key] === "") + { + $errors[$key] = sprintf (dgettext("domframework", + "Mandatory field '%s' is empty"), + $key); + continue; + } + } + + // Do not verify the non provided datas (if they are not mandatory) + if (!array_key_exists ($key, $datas)) + continue; + + // Verify the fields, if $verify is defined, before doing insertion + $verify = $this->verifyOne ($key, $datas[$key]); + if (is_array ($verify) && count ($verify)) + { + $errors[$key] = $verify[0]." ".$verify[1]." in ".$key; + continue; + } + // Check for type inconsistencies if the value is provided + if ($datas[$key] !== "" && $params[0] === "integer") + { + if (strspn ($datas[$key], "0123456789") !== strlen ($datas[$key])) + { + $errors[$key] = sprintf ( + dgettext("domframework", + "Errors in consistency : '%s' is not an integer"), + $key); + continue; + } + } + elseif ($datas[$key] !== "" && $params[0] === "varchar") + { + if (! isset ($params[1])) + { + $errors[$key] = sprintf ( + dgettext("domframework", + "The length of varchar field '%s' is not provided"), + $key); + continue; + } + if (strlen ($datas[$key]) > $params[1]) + { + $errors[$key] = sprintf ( + dgettext("domframework", + "Errors in consistency : '%s' data is too long"), + $key); + continue; + } + } + elseif ($datas[$key] !== "" && $params[0] === "datetime") + { + // The date format must be in ANSI SQL : YYYY-MM-DD HH:MM:SS + $d = DateTime::createFromFormat("Y-m-d H:i:s", $datas[$key]); + if (!$d || $d->format("Y-m-d H:i:s") !== $datas[$key]) + { + $errors[$key] = sprintf ( + dgettext("domframework", + "Incorrect datetime provided for field '%s'"), + $key); + continue; + } + } + elseif ($datas[$key] !== "" && $params[0] === "date") + { + // The date format must be in ANSI SQL : YYYY-MM-DD + $d = DateTime::createFromFormat("Y-m-d", $datas[$key]); + if (!$d || $d->format("Y-m-d") !== $datas[$key]) + { + $errors[$key] = sprintf ( + dgettext("domframework", + "Incorrect date provided for field '%s'"), + $key); + continue; + } + } + elseif ($datas[$key] !== "") + { + $errors[$key] = sprintf (dgettext("domframework", + "Unknown field type for '%s'"), $key); + continue; + } + else + { + // Nothing to do if the value is empty : just save it + } + } + + // Check for inconsistency + $verify = $this->verifyAll ($datas); + if (count ($verify)) + return $verify; + + $datasOK = array (); + foreach ($this->fields as $field=>$desc) + { + if (isset ($datas[$field])) + $datasOK[$field] = $datas[$field]; + } + + if ($updatekey !== false) + { + // Check if the unique constrain is valid before doing the insertion + // 1. Read the actual state + $before = $this->read (array (array ($this->primary, $updatekey))); + if (count ($before) === 0) + return array (dgettext("domframework", "Entry to modify unavailable")); + $before = reset ($before); + // 2. Map the proposal entries into the before state + $after = $before; + foreach ($datasOK as $field=>$val) + $after[$field] = $val; + } + else + { + $after = $datasOK; + } + // Check if the unique constrain is valid before doing the insertion + foreach ($this->unique as $columns) + { + if (is_array ($columns)) + { + // Multiple columns in unique + $select = array (); + if ($updatekey !== false) + $select[] = array ($this->primary, $updatekey, "!="); + foreach ($columns as $col) + { + if (!array_key_exists ($col, $after)) continue; + $select[] = array ($col, $after[$col]); + } + // If there is only the primary key, there is no chance to have a + // conflict + if ($updatekey !== false && count ($select) >= 2) + { + $rc = $this->read ($select, array ($this->primary)); + if (count ($rc) > 0) + { + $errors[] = sprintf (dgettext("domframework", + "The provided values for columns '%s' already exists"), + implode (",", $columns)); + continue; + } + } + } + else + { + // One column in unique + if (!array_key_exists ($columns, $datasOK)) continue; + $select = array (); + if ($updatekey !== false) + { + if ($columns === $this->primary) + $select[] = array ($this->primary, $updatekey, "!="); + } + $select[] = array ($columns, $datasOK[$columns]); + $rc = $this->read ($select, + array ($this->primary)); + if (count ($rc) > 0) + { + $errors[] = sprintf (dgettext("domframework", + "The column '%s' with this value already exists"), + $columns); + continue; + } + } + } + + // Check if the foreign keys constrains are valid before doing the insertion + foreach ($this->foreign as $foreign=>$data) + { + if (! isset ($datas[$foreign])) + { + $errors[] = sprintf (dgettext("domframework", + "The forign column '%s' is not provided"), + $foreign); + continue; + } + $table = $data[0]; + $column = $data[1]; + $req = "SELECT $column ". + "FROM $this->sep$this->tableprefix$table$this->sep ". + "WHERE $this->sep$column$this->sep=:".md5 ($column); + if ($this->debug) echo "DEBUG : $req\n"; + $st = $this->db->prepare ($req); + $val = $datas[$foreign]; + $key = $column; + if ($this->debug) echo "DEBUG BIND : $column(".md5 ($column)."->". + var_export ($val, TRUE)."\n"; + if ($val === null) + $st->bindValue (":".md5 ($key), $val, PDO::PARAM_NULL); + elseif ($this->fields[$key][0] === "integer") + $st->bindValue (":".md5 ($key), $val, PDO::PARAM_INT); + elseif ($this->fields[$key][0] === "varchar") + $st->bindValue (":".md5 ($key), $val, PDO::PARAM_STR); + elseif ($this->fields[$key][0] === "datetime") + $st->bindValue (":".md5 ($key), $val, PDO::PARAM_STR); + elseif ($this->fields[$key][0] === "date") + $st->bindValue (":".md5 ($key), $val, PDO::PARAM_STR); + else + throw new Exception ("TO BE DEVELOPPED : ".$this->fields[$key][0], 500); + $st->execute (); + $res = array (); + while ($d = $st->fetch (PDO::FETCH_ASSOC)) + $res[] = $d; + if (count ($res) === 0) + { + $errors[] = sprintf (dgettext("domframework", + "The foreign key '%s' doesn't exists"), + $column); + continue; + } + } + return $errors; + } + /** Create a new entry in the table. Datas must be an indexed array @param array $datas Datas to be recorded (column=>value) @obsolete 0.5 */ @@ -217,153 +454,22 @@ class dblayer extends PDO throw new Exception (dgettext("domframework", "The datas provided to create are not array"), 405); - if (!in_array ($this->primary, $this->unique)) - $this->unique[] = $this->primary; - $datasOK = array (); - // Check for missing parameters foreach ($this->fields as $key=>$params) { if (in_array ("autoincrement", $params)) $datas[$key] = null; - if (in_array ("not null", $params) && !array_key_exists ($key, $datas)) - throw new Exception (sprintf (dgettext("domframework", - "Mandatory field '%s' not provided"), - $key), 405); - if (in_array ("not null", $params) && $datas[$key] === "") - throw new Exception (sprintf (dgettext("domframework", - "Mandatory field '%s' is empty"), - $key), 405); - if (!array_key_exists ($key, $datas)) - continue; - // Verify the fields, if $verify is defined, before doing insertion - $verify = $this->verifyOne ($key, $datas[$key]); - if (is_array ($verify) && count ($verify)) - throw new Exception ($verify[0]." ".$verify[1]." in ".$key); - // Check for type inconsistencies if the value is provided - if ($datas[$key] !== "" && $params[0] === "integer") - { - if (strspn ($datas[$key], "0123456789") !== strlen ($datas[$key])) - throw new Exception (sprintf ( - dgettext("domframework", - "Errors in consistency : '%s' is not an integer"), - $key), 405); - } - elseif ($datas[$key] !== "" && $params[0] === "varchar") - { - if (! isset ($params[1])) - throw new Exception (sprintf ( - dgettext("domframework", - "The length of varchar field '%s' is not provided"), - $key), 500); - if (strlen ($datas[$key]) > $params[1]) - throw new Exception (sprintf ( - dgettext("domframework", - "Errors in consistency : '%s' data is too long"), - $key), 405); - } - elseif ($datas[$key] !== "" && $params[0] === "datetime") - { - // The date format must be in ANSI SQL : YYYY-MM-DD HH:MM:SS - $d = DateTime::createFromFormat("Y-m-d H:i:s", $datas[$key]); - if (!$d || $d->format("Y-m-d H:i:s") !== $datas[$key]) - throw new Exception (sprintf ( - dgettext("domframework", - "Incorrect datetime provided for field '%s'"), - $key), 500); - } - elseif ($datas[$key] !== "" && $params[0] === "date") - { - // The date format must be in ANSI SQL : YYYY-MM-DD - $d = DateTime::createFromFormat("Y-m-d", $datas[$key]); - if (!$d || $d->format("Y-m-d") !== $datas[$key]) - throw new Exception (sprintf ( - dgettext("domframework", - "Incorrect date provided for field '%s'"), - $key), 500); - } - elseif ($datas[$key] !== "") - throw new Exception (sprintf (dgettext("domframework", - "Unknown field type for '%s'"), $key), - 500); - else - { - // Nothing to do if the value is empty : just save it - } - $datasOK[$key] = $datas[$key]; } - - // Check for inconsistency - $verify = $this->verifyAll ($datas); - if (count ($verify)) - throw new Exception (dgettext("domframework", "Errors in consistency : "). - print_r ($verify, TRUE), 405); - - // Check if the unique constrain is valid before doing the insertion - foreach ($this->unique as $columns) + if (!in_array ($this->primary, $this->unique)) + $this->unique[] = $this->primary; + $datasOK = array (); + $errors = $this->verify ($datas); + if (count ($errors) !== 0) + throw new Exception (reset ($errors), 405); + foreach ($this->fields as $field=>$desc) { - if (is_array ($columns)) - { - $select = array (); - foreach ($columns as $col) - { - if (!array_key_exists ($col, $datasOK)) continue; - $select[] = array ($col, $datasOK[$col]); - } - $rc = $this->read ($select, array ($this->primary)); - if (count ($rc) > 0) - throw new Exception (sprintf ( - dgettext("domframework", - "The provided values for columns '%s' already exists"), - implode (",", $columns)), 405); - } - else - { - if (!array_key_exists ($columns, $datasOK)) continue; - $rc = $this->read (array (array ($columns, $datasOK[$columns])), - array ($this->primary)); - if (count ($rc) > 0) - throw new Exception (sprintf ( - dgettext("domframework", - "The column '%s' with this value already exists"), - $columns), 405); - } + if (isset ($datas[$field])) + $datasOK[$field] = $datas[$field]; } - - // Check if the foreign keys constrains are valid before doing the insertion - foreach ($this->foreign as $foreign=>$data) - { - $table = $data[0]; - $column = $data[1]; - $req = "SELECT $column FROM $this->sep$this->tableprefix$table$this->sep ". - "WHERE \"$column\"=:".md5 ($column); - if ($this->debug) echo "DEBUG : $req\n"; - $st = $this->db->prepare ($req); - $val = $datasOK[$foreign]; - $key = $column; - if ($this->debug) echo "DEBUG BIND : $column(".md5 ($column)."->". - var_export ($val, TRUE)."\n"; - if ($val === null) - $st->bindValue (":".md5 ($key), $val, PDO::PARAM_NULL); - elseif ($this->fields[$key][0] === "integer") - $st->bindValue (":".md5 ($key), $val, PDO::PARAM_INT); - elseif ($this->fields[$key][0] === "varchar") - $st->bindValue (":".md5 ($key), $val, PDO::PARAM_STR); - elseif ($this->fields[$key][0] === "datetime") - $st->bindValue (":".md5 ($key), $val, PDO::PARAM_STR); - elseif ($this->fields[$key][0] === "date") - $st->bindValue (":".md5 ($key), $val, PDO::PARAM_STR); - else - throw new Exception ("TO BE DEVELOPPED : ".$this->fields[$key][0], 500); - $st->execute (); - $res = array (); - while ($d = $st->fetch (PDO::FETCH_ASSOC)) - $res[] = $d; - if (count ($res) === 0) - throw new Exception (sprintf (dgettext("domframework", - "The foreign key '%s' doesn't exists"), - $column), 405); - } - $binds = array_keys ($datasOK); array_walk ($binds, function(&$value, $key) { $value = md5 ($value); @@ -535,173 +641,13 @@ class dblayer extends PDO if ($this->primary === null) throw new Exception (dgettext("domframework", "No Primary defined"), 500); $datasOK = array (); - // Check for missing parameters - foreach ($this->fields as $key=>$params) + $errors = $this->verify ($datas); + if (count ($errors) !== 0) + throw new Exception (reset ($errors), 405); + foreach ($this->fields as $field=>$desc) { - // Don't save a none provided field. The old value will be use in database - if (!array_key_exists ($key, $datas)) - continue; - - // Check for type inconsistencies if the value is provided - if ($datas[$key] !== "" && $params[0] === "integer") - { - if (strspn ($datas[$key], "0123456789") !== strlen ($datas[$key])) - throw new Exception (sprintf ( - dgettext("domframework", - "Errors in consistency : '%s' is not an integer"), - $key), 405); - } - elseif ($datas[$key] !== "" && $params[0] === "varchar") - { - if (! isset ($params[1])) - throw new Exception (sprintf ( - dgettext("domframework", - "The length of varchar field '%s' is not provided"), - $key), 500); - if (strlen ($datas[$key]) > $params[1]) - throw new Exception (sprintf ( - dgettext("domframework", - "Errors in consistency : '%s' data is too long"), - $key), 405); - } - elseif ($datas[$key] !== "" && $params[0] === "datetime") - { - // The date format must be in ANSI SQL : YYYY-MM-DD HH:MM:SS - $d = DateTime::createFromFormat("Y-m-d H:i:s", $datas[$key]); - if (!$d || $d->format("Y-m-d H:i:s") !== $datas[$key]) - throw new Exception (sprintf ( - dgettext("domframework", - "Incorrect datetime provided for field '%s'"), - $key), 500); - } - elseif ($datas[$key] !== "" && $params[0] === "date") - { - // The date format must be in ANSI SQL : YYYY-MM-DD - $d = DateTime::createFromFormat("Y-m-d", $datas[$key]); - if (!$d || $d->format("Y-m-d") !== $datas[$key]) - throw new Exception (sprintf ( - dgettext("domframework", - "Incorrect date provided for field '%s'"), - $key), 500); - } - elseif ($datas[$key] !== "") - throw new Exception (sprintf (dgettext("domframework", - "Unknown field type for '%s'"), $key), - 500); - else - { - // Nothing to do if the value is empty : just save it - } - - $datasOK[$key] = $datas[$key]; - } - - if (count ($datasOK) === 0) - throw new Exception (dgettext("domframework", - "Don't receive any field to display"), 501); - - // Check for type inconsistencies before using $datasOK - foreach ($datasOK as $key=>$params) - { - $verify = $this->verifyOne ($key, $datas[$key]); - if (is_array ($verify) && count ($verify)) - throw new Exception ($verify[0]." ".$verify[1]." in ".$key); - } - - // Check if the unique constrain is valid before doing the insertion - // 1. Read the actual state - $before = $this->read (array (array ($this->primary, $updatekey))); - if (count ($before) === 0) - throw new Exception (dgettext("domframework", - "Entry to modify unavailable"), 404); - $before = reset ($before); - // 2. Map the proposal entries into the before state - $after = $before; - foreach ($datasOK as $field=>$val) - $after[$field] = $val; - // 3. Check for constrain violation on unique columns - foreach ($this->unique as $columns) - { - if (is_array ($columns)) - { - // Multiple columns in unique - $select = array (); - $select[] = array ($this->primary, $updatekey, "!="); - foreach ($columns as $col) - { - if (!array_key_exists ($col, $after)) continue; - $select[] = array ($col, $after[$col]); - } - - // If there is only the primary key, there is no chance to have a - // conflict - if (count ($select) >= 2) - { - $rc = $this->read ($select, array ($this->primary)); - if (count ($rc) > 0) - throw new Exception (sprintf ( - dgettext("domframework", - "The provided values for columns '%s' already exists"), - implode (",", $columns)), 405); - } - } - else - { - // One column in unique - if (!array_key_exists ($columns, $datasOK)) continue; - $select = array (); - if ($columns === $this->primary) - $select[] = array ($this->primary, $updatekey, "!="); - $select[] = array ($columns, $datasOK[$columns]); - $rc = $this->read ($select, - array ($this->primary)); - if (count ($rc) > 0) - throw new Exception (sprintf ( - dgettext("domframework", - "An entry already exists with this value in the column '%s'"), - $columns), 405); - } - } - - // Check if the foreign keys constrains are valid before doing the update - foreach ($this->foreign as $foreign=>$data) - { - // If the foreign keys are not modified, we don't check if they are - // correct - if (!isset ($datasOK[$foreign])) - continue; - $table = $data[0]; - $column = $data[1]; - $req = "SELECT $this->sep$column$this->sep ". - "FROM $this->sep$this->tableprefix$table$this->sep ". - "WHERE $this->sep$column$this->sep=:".md5 ($column); - if ($this->debug) echo "DEBUG : $req\n"; - $st = $this->db->prepare ($req); - $val = $datasOK[$foreign]; - $key = $column; - if ($this->debug) echo "DEBUG BIND : $column(".md5 ($column).")->". - var_export ($val, TRUE). - "\n"; - if ($val === null) - $st->bindValue (":".md5 ($key), $val, PDO::PARAM_NULL); - elseif ($this->fields[$key][0] === "integer") - $st->bindValue (":".md5 ($key), $val, PDO::PARAM_INT); - elseif ($this->fields[$key][0] === "varchar") - $st->bindValue (":".md5 ($key), $val, PDO::PARAM_STR); - elseif ($this->fields[$key][0] === "datetime") - $st->bindValue (":".md5 ($key), $val, PDO::PARAM_STR); - elseif ($this->fields[$key][0] === "date") - $st->bindValue (":".md5 ($key), $val, PDO::PARAM_STR); - else - throw new Exception ("TO BE DEVELOPPED : ".$this->fields[$key][0], 500); - $st->execute (); - $res = array (); - while ($d = $st->fetch (PDO::FETCH_ASSOC)) - $res[] = $d; - if (count ($res) === 0) - throw new Exception (sprintf (dgettext("domframework", - "The foreign key '%s' doesn't exists"), - $column), 405); + if (isset ($datas[$field])) + $datasOK[$field] = $datas[$field]; } $req = "UPDATE $this->sep".$this->tableprefix."$this->table$this->sep SET ";