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
This commit is contained in:
2014-09-19 07:23:06 +00:00
parent a1c5a35899
commit 7df93f95db

View File

@@ -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 ";