dblayer : add more debug

dblayer : add more unit tests (foreign keys)
dblayer : add the same answer when updating a line with the same informations
dblayer : better support of foreign keys


git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@1842 bf3deb0d-5f1a-0410-827f-c0cc1f45334c
This commit is contained in:
2014-09-22 10:52:53 +00:00
parent 19966c094c
commit b034fa3625
2 changed files with 161 additions and 37 deletions

View File

@@ -39,20 +39,22 @@ class test_dblayer_{ENGINE} extends PHPUnit_Framework_TestCase
$dbconfig = $this->confs["{ENGINE}"]; $dbconfig = $this->confs["{ENGINE}"];
$db = new dblayer ($dbconfig["dsn"], $dbconfig["username"], $db = new dblayer ($dbconfig["dsn"], $dbconfig["username"],
$dbconfig["password"], $dbconfig["driver_options"]); $dbconfig["password"], $dbconfig["driver_options"]);
$db->table = "grouped"; foreach (array ("users", "grouped") as $table)
try
{ {
$res = $db->dropTable(); $db->table = $table;
} try
catch (Exception $e) {
{ $res = $db->dropTable();
}
catch (Exception $e)
{
}
} }
// Never generate an error, just drop the table if it exists, and do noting // Never generate an error, just drop the table if it exists, and do noting
// if it doesn't exists // if it doesn't exists
} }
public function test_createTable () public function test_createTable1 ()
{ {
// Create a table named group // Create a table named group
$dbconfig = $this->confs["{ENGINE}"]; $dbconfig = $this->confs["{ENGINE}"];
@@ -63,11 +65,13 @@ class test_dblayer_{ENGINE} extends PHPUnit_Framework_TestCase
"object"=>array ("varchar", "255", "not null"), "object"=>array ("varchar", "255", "not null"),
"where"=>array ("varchar", "255", "not null"), "where"=>array ("varchar", "255", "not null"),
"with space"=>array ("varchar", "255", "not null")); "with space"=>array ("varchar", "255", "not null"));
$db->unique = array ();
$db->primary = "group";
$res = $db->createTable (); $res = $db->createTable ();
$this->assertSame (0, $res); $this->assertSame (0, $res);
} }
public function test_insert () public function test_insert1 ()
{ {
$dbconfig = $this->confs["{ENGINE}"]; $dbconfig = $this->confs["{ENGINE}"];
$db = new dblayer ($dbconfig["dsn"], $dbconfig["username"], $db = new dblayer ($dbconfig["dsn"], $dbconfig["username"],
@@ -78,6 +82,7 @@ class test_dblayer_{ENGINE} extends PHPUnit_Framework_TestCase
"where"=>array ("varchar", "255", "not null"), "where"=>array ("varchar", "255", "not null"),
"with space"=>array ("varchar", "255", "not null")); "with space"=>array ("varchar", "255", "not null"));
$db->unique = array (); $db->unique = array ();
$db->primary = "group";
$res = $db->insert (array ("group"=>"gr ou\"p", $res = $db->insert (array ("group"=>"gr ou\"p",
"object"=>"/éobj%", "object"=>"/éobj%",
"where"=>"\$'\"", "where"=>"\$'\"",
@@ -198,10 +203,60 @@ class test_dblayer_{ENGINE} extends PHPUnit_Framework_TestCase
"with space"=>array ("varchar", "255", "not null")); "with space"=>array ("varchar", "255", "not null"));
$db->unique = array ("group"); $db->unique = array ("group");
$db->primary = "group"; $db->primary = "group";
// Update primary key with primary key in unique // Update primary key with primary key in unique with same values to test if
// the exception is NOT raised
$res = $db->update ("NEW GROUP", array ("group"=>"NEW GROUP", $res = $db->update ("NEW GROUP", array ("group"=>"NEW GROUP",
"object"=>"%éàoppp", "object"=>"%éàoppp",
"with space"=>"WITH SPACE")); "with space"=>"WITH SPACE"));
// SQLite and PostgreSQL return 1 line modified
// MySQL return 0 by default because there is no modification on the line
// http://fr2.php.net/manual/en/pdostatement.rowcount.php#104930
// Now, the MYSQL_ATTR_FOUND_ROWS is added to connection to be the same in
// all the engines
$this->assertSame (1, $res); $this->assertSame (1, $res);
} }
// Part to test the foreign keys
public function test_createTable2 ()
{
// Create a table named group
$dbconfig = $this->confs["{ENGINE}"];
$db = new dblayer ($dbconfig["dsn"], $dbconfig["username"],
$dbconfig["password"], $dbconfig["driver_options"]);
$db->table = "users";
$db->fields = array ("user"=>array ("varchar", "255", "not null"),
"groupmember"=>array ("varchar", "255", "not null"),
"where"=>array ("varchar", "255", "not null"),
"with space"=>array ("varchar", "255", "not null"));
$db->foreign = array (
"groupmember"=>array ("grouped", "group", "ON UPDATE CASCADE ON DELETE CASCADE"),
);
$res = $db->createTable ();
$this->assertSame (0, $res);
}
public function test_insert2 ()
{
$dbconfig = $this->confs["{ENGINE}"];
$db = new dblayer ($dbconfig["dsn"], $dbconfig["username"],
$dbconfig["password"], $dbconfig["driver_options"]);
$db->table = "users";
$db->fields = array ("user"=>array ("varchar", "255", "not null"),
"groupmember"=>array ("varchar", "255", "not null"),
"where"=>array ("varchar", "255", "not null"),
"with space"=>array ("varchar", "255", "not null"));
$db->unique = array ("user");
$db->primary = "user";
$db->foreign = array (
"groupmember"=>array ("grouped", "group", "ON UPDATE CASCADE ON DELETE CASCADE"),
);
$res = $db->insert (array ("user"=>"Us ou\"r",
"groupmember"=>"NEW GROUP",
"where"=>"\$'\"",
"with space"=>"with space"));
// SQLite start at 1, MySQL start at 0...
$this->assertThat($res, $this->lessThanOrEqual(2));
}
} }

View File

@@ -9,9 +9,9 @@
The dbLayer provide an abstraction layer on PDO to be easier on all the CRUD The dbLayer provide an abstraction layer on PDO to be easier on all the CRUD
(Create, Read, Update, Delete) operations, accross all the databases engines. (Create, Read, Update, Delete) operations, accross all the databases engines.
To use it, extends in your code this class, and define the attributes : To use it, extends in your code this class, and define the attributes :
- protected $table : name of the table you want to use - public $table : name of the table you want to use
- protected $fields : description of all the fields in the database like : - public $fields : description of all the fields in the database like :
protected $fields = array ( public $fields = array (
"id"=>array ("integer", "not null", "autoincrement"), "id"=>array ("integer", "not null", "autoincrement"),
"zone"=>array ("varchar", "255", "not null"), "zone"=>array ("varchar", "255", "not null"),
"viewname"=>array ("varchar", "255"), "viewname"=>array ("varchar", "255"),
@@ -20,12 +20,17 @@ To use it, extends in your code this class, and define the attributes :
"opendate"=>array ("datetime", "not null"), "opendate"=>array ("datetime", "not null"),
"closedate"=>array ("datetime"), "closedate"=>array ("datetime"),
); );
- protected $primary = "id" ; the primary key of the table (in text). Actually - public $primary = "id" ; the primary key of the table (in text). Actually
the dbLayer abstraction don't supports primary key on multiples columns the dbLayer abstraction don't supports primary key on multiples columns
- protected $unique = array ("column", array ("column1", "column2");) - public $unique = array ("column", array ("column1", "column2");)
- public $foreign : Add the support of foreign keys. Must be defined like :
public $foreign = array (
"localfield"=>array ("foreign Table", "foreign key",
"ON UPDATE CASCADE ON DELETE CASCADE"),
);
Optionnaly, you can add the Optionnaly, you can add the
- protected $debug = TRUE : enable the debug on screen (NOT FOR PROD !!) - public $debug = TRUE : enable the debug on screen (NOT FOR PROD !!)
*/ */
require_once ("domframework/verify.php"); require_once ("domframework/verify.php");
@@ -89,20 +94,23 @@ class dblayer extends PDO
public function __construct ($dsn, $username=null, $password=null, public function __construct ($dsn, $username=null, $password=null,
$driver_options=null) $driver_options=null)
{ {
try $driver = @explode (":", $dsn);
{ if (! isset ($driver[0]))
$this->db = new PDO ($dsn, $username, $password, $driver_options); throw new Exception (_("No valid DSN provided"), 500);
$this->db->setAttribute (PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch (Exception $e)
{
throw new Exception ("PDO error : ".$e->getMessage(), 500);
}
// Force specifics initialisations // Force specifics initialisations
switch ($this->db->getAttribute(PDO::ATTR_DRIVER_NAME)) switch ($driver[0])
{ {
case "sqlite": case "sqlite":
try
{
$this->db = new PDO ($dsn, $username, $password, $driver_options);
$this->db->setAttribute (PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch (Exception $e)
{
throw new Exception ("PDO error : ".$e->getMessage(), 500);
}
// Look at the right to write in database and in the directory // Look at the right to write in database and in the directory
$file = substr ($dsn, 7); $file = substr ($dsn, 7);
if (! is_writeable (dirname ($file))) if (! is_writeable (dirname ($file)))
@@ -121,11 +129,32 @@ class dblayer extends PDO
$this->sep = "`"; $this->sep = "`";
break; break;
case "mysql": case "mysql":
try
{
$driver_options[PDO::MYSQL_ATTR_FOUND_ROWS] = 1;
$this->db = new PDO ($dsn, $username, $password, $driver_options);
$this->db->setAttribute (PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch (Exception $e)
{
throw new Exception ("PDO error : ".$e->getMessage(), 500);
}
// Set the coding to UTF8 // Set the coding to UTF8
$this->db->exec("SET CHARACTER SET utf8"); $this->db->exec("SET CHARACTER SET utf8");
$this->sep = "`"; $this->sep = "`";
break; break;
case "pgsql": case "pgsql":
try
{
$this->db = new PDO ($dsn, $username, $password, $driver_options);
$this->db->setAttribute (PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch (Exception $e)
{
throw new Exception ("PDO error : ".$e->getMessage(), 500);
}
// Set the coding to UTF8 // Set the coding to UTF8
$this->db->exec("SET NAMES 'utf8'"); $this->db->exec("SET NAMES 'utf8'");
$this->sep = "\""; $this->sep = "\"";
@@ -202,9 +231,11 @@ class dblayer extends PDO
*/ */
public function verify ($datas, $updatekey=false) public function verify ($datas, $updatekey=false)
{ {
if ($this->debug) echo "== Entering verify\n";
$errors = array (); $errors = array ();
foreach ($this->fields as $key=>$params) foreach ($this->fields as $key=>$params)
{ {
if ($this->debug) echo " verify ($key)\n";
if ($updatekey === false) if ($updatekey === false)
{ {
// Don't check if there is an update : the database is already filled // Don't check if there is an update : the database is already filled
@@ -311,6 +342,7 @@ class dblayer extends PDO
if (count ($errors) !== 0) if (count ($errors) !== 0)
return $errors; return $errors;
if ($this->debug) echo " verify inconsistency\n";
// Check for inconsistency // Check for inconsistency
$verify = $this->verifyAll ($datas); $verify = $this->verifyAll ($datas);
if (count ($verify)) if (count ($verify))
@@ -325,6 +357,7 @@ class dblayer extends PDO
if ($updatekey !== false) if ($updatekey !== false)
{ {
if ($this->debug) echo " verify in updatekey\n";
// Check if the unique constrain is valid before doing the insertion // Check if the unique constrain is valid before doing the insertion
// 1. Read the actual state // 1. Read the actual state
$before = $this->read (array (array ($this->primary, $updatekey))); $before = $this->read (array (array ($this->primary, $updatekey)));
@@ -339,14 +372,22 @@ class dblayer extends PDO
} }
else else
{ {
if ($this->debug) echo " verify NO updatekey\n";
$after = $datasOK; $after = $datasOK;
} }
// Check if the unique constrain is valid before doing the insertion // Check if the unique constrain is valid before doing the insertion
foreach ($this->unique as $columns) foreach ($this->unique as $k=>$columns)
{ {
if ($this->primary === null)
{
return array (dgettext("domframework",
"No field primary defined for tests in primary"));
}
if (is_array ($columns)) if (is_array ($columns))
{ {
// Multiple columns in unique // Multiple columns in unique
if ($this->debug) echo " verify unique multiple $k\n";
$select = array (); $select = array ();
if ($updatekey !== false) if ($updatekey !== false)
$select[] = array ($this->primary, $updatekey, "!="); $select[] = array ($this->primary, $updatekey, "!=");
@@ -372,6 +413,7 @@ class dblayer extends PDO
else else
{ {
// One column in unique // One column in unique
if ($this->debug) echo " verify unique one column $k\n";
if (!array_key_exists ($columns, $after)) continue; if (!array_key_exists ($columns, $after)) continue;
$select = array (); $select = array ();
if ($updatekey !== false) if ($updatekey !== false)
@@ -395,36 +437,49 @@ class dblayer extends PDO
// Check if the foreign keys constrains are valid before doing the insertion // Check if the foreign keys constrains are valid before doing the insertion
foreach ($this->foreign as $foreign=>$data) foreach ($this->foreign as $foreign=>$data)
{ {
if ($this->debug) echo " verify foreign $foreign\n";
if (! isset ($datas[$foreign])) if (! isset ($datas[$foreign]))
{ {
$errors[] = array ("error", sprintf (dgettext("domframework", $errors[] = array ("error", sprintf (dgettext("domframework",
"The foreign column '%s' is not provided"), "The foreign column '%s' is not provided"),
$foreign)); $foreign));
return $errors;
}
if (! isset ($datas[$foreign][0]))
{
$errors[] = array ("error", sprintf (dgettext("domframework",
"The field type for column '%s' is not provided"),
$foreign));
return $errors;
continue; continue;
} }
$table = $data[0]; $table = $data[0];
$column = $data[1]; $column = $data[1];
$req = "SELECT $column ". $req = "SELECT $this->sep$column$this->sep ".
"FROM $this->sep$this->tableprefix$table$this->sep ". "FROM $this->sep$this->tableprefix$table$this->sep ".
"WHERE $this->sep$column$this->sep=:".md5 ($column); "WHERE $this->sep$column$this->sep=:".md5 ($column);
if ($this->debug) echo "DEBUG : $req\n"; if ($this->debug) echo "DEBUG : $req\n";
$st = $this->db->prepare ($req); $st = $this->db->prepare ($req);
$val = $datas[$foreign]; $val = $datas[$foreign];
$key = $column; $key = $column;
if ($this->debug) echo "DEBUG BIND : ".$this->fields[$foreign][0]."\n";
if ($this->debug) echo "DEBUG BIND : $column(".md5 ($column)."->". if ($this->debug) echo "DEBUG BIND : $column(".md5 ($column)."->".
var_export ($val, TRUE)."\n"; var_export ($val, TRUE)."\n";
if ($val === null) if ($val === null)
$st->bindValue (":".md5 ($key), $val, PDO::PARAM_NULL); $st->bindValue (":".md5 ($key), $val, PDO::PARAM_NULL);
elseif ($this->fields[$key][0] === "integer") elseif ($this->fields[$foreign][0] === "integer")
$st->bindValue (":".md5 ($key), $val, PDO::PARAM_INT); $st->bindValue (":".md5 ($key), $val, PDO::PARAM_INT);
elseif ($this->fields[$key][0] === "varchar") elseif ($this->fields[$foreign][0] === "varchar")
$st->bindValue (":".md5 ($key), $val, PDO::PARAM_STR); $st->bindValue (":".md5 ($key), $val, PDO::PARAM_STR);
elseif ($this->fields[$key][0] === "datetime") elseif ($this->fields[$foreign][0] === "datetime")
$st->bindValue (":".md5 ($key), $val, PDO::PARAM_STR); $st->bindValue (":".md5 ($key), $val, PDO::PARAM_STR);
elseif ($this->fields[$key][0] === "date") elseif ($this->fields[$foreign][0] === "date")
$st->bindValue (":".md5 ($key), $val, PDO::PARAM_STR); $st->bindValue (":".md5 ($key), $val, PDO::PARAM_STR);
else else
throw new Exception ("TO BE DEVELOPPED : ".$this->fields[$key][0], 500); {
throw new Exception ("TO BE DEVELOPPED : ".$this->fields[$foreign][0],
500);
}
$st->execute (); $st->execute ();
$res = array (); $res = array ();
while ($d = $st->fetch (PDO::FETCH_ASSOC)) while ($d = $st->fetch (PDO::FETCH_ASSOC))
@@ -452,12 +507,16 @@ class dblayer extends PDO
@param array $datas Datas to be recorded (column=>value)*/ @param array $datas Datas to be recorded (column=>value)*/
public function insert ($datas) public function insert ($datas)
{ {
if ($this->debug) echo "== Entering insert\n";
if ($this->db === null) if ($this->db === null)
throw new Exception (dgettext("domframework", "Database not connected"), throw new Exception (dgettext("domframework", "Database not connected"),
500); 500);
if ($this->unique === null) if ($this->unique === null)
throw new Exception (dgettext("domframework", throw new Exception (dgettext("domframework",
"Unique fields of table are not defined"), 500); "Unique fields of table are not defined"), 500);
if (! is_array ($this->unique))
throw new Exception (dgettext("domframework",
"The unique configuration is not an array"), 500);
if (!is_array ($datas)) if (!is_array ($datas))
throw new Exception (dgettext("domframework", throw new Exception (dgettext("domframework",
"The datas provided to create are not array"), "The datas provided to create are not array"),
@@ -472,7 +531,10 @@ class dblayer extends PDO
$datasOK = array (); $datasOK = array ();
$errors = $this->verify ($datas); $errors = $this->verify ($datas);
if (count ($errors) !== 0) if (count ($errors) !== 0)
throw new Exception (reset ($errors), 405); {
$errors = reset ($errors);
throw new Exception ($errors[1], 405);
}
foreach ($this->fields as $field=>$desc) foreach ($this->fields as $field=>$desc)
{ {
if (isset ($datas[$field])) if (isset ($datas[$field]))
@@ -527,6 +589,7 @@ class dblayer extends PDO
public function read ($select=null, $display=null, $order=null, public function read ($select=null, $display=null, $order=null,
$whereOr=false) $whereOr=false)
{ {
if ($this->debug) echo "== Entering read\n";
if ($this->db === null) if ($this->db === null)
throw new Exception (dgettext("domframework", "Database not connected"), throw new Exception (dgettext("domframework", "Database not connected"),
500); 500);
@@ -638,9 +701,11 @@ class dblayer extends PDO
Return the number of rows modified Return the number of rows modified
@param string|integer $updatekey The key applied on primary key to be @param string|integer $updatekey The key applied on primary key to be
updated updated
@param array $datas The values to be updated */ @param array $datas The values to be updated
@return the number of lines modified */
public function update ($updatekey, $datas) public function update ($updatekey, $datas)
{ {
if ($this->debug) echo "== Entering update\n";
if ($this->db === null) if ($this->db === null)
throw new Exception (dgettext("domframework", "Database not connected"), throw new Exception (dgettext("domframework", "Database not connected"),
500); 500);
@@ -652,7 +717,6 @@ class dblayer extends PDO
$errors = $this->verify ($datas, $updatekey); $errors = $this->verify ($datas, $updatekey);
if (count ($errors) !== 0) if (count ($errors) !== 0)
{ {
$errors = reset ($errors);
throw new Exception ($errors[1], 405); throw new Exception ($errors[1], 405);
} }
foreach ($this->fields as $field=>$desc) foreach ($this->fields as $field=>$desc)
@@ -724,6 +788,7 @@ class dblayer extends PDO
@param strin|integer $deletekey The key of primary key to be deleted */ @param strin|integer $deletekey The key of primary key to be deleted */
public function delete ($deletekey) public function delete ($deletekey)
{ {
if ($this->debug) echo "== Entering delete\n";
if ($this->db === null) if ($this->db === null)
throw new Exception (dgettext("domframework", "Database not connected")); throw new Exception (dgettext("domframework", "Database not connected"));
$req = "DELETE FROM $this->sep$this->tableprefix$this->table$this->sep "; $req = "DELETE FROM $this->sep$this->tableprefix$this->table$this->sep ";
@@ -740,6 +805,7 @@ class dblayer extends PDO
/** Translation of fields */ /** Translation of fields */
public function titles () public function titles ()
{ {
if ($this->debug) echo "== Entering titles\n";
if (count ($this->fields) === 0) if (count ($this->fields) === 0)
throw new Exception (dgettext("domframework", "No Field defined"), 500); throw new Exception (dgettext("domframework", "No Field defined"), 500);
$arr = array (); $arr = array ();
@@ -759,6 +825,7 @@ class dblayer extends PDO
/** Drop the table */ /** Drop the table */
public function dropTable () public function dropTable ()
{ {
if ($this->debug) echo "== Entering dropTables\n";
if ($this->db === null) if ($this->db === null)
throw new Exception (dgettext("domframework", "Database not connected")); throw new Exception (dgettext("domframework", "Database not connected"));
$sql = "DROP TABLE $this->sep$this->tableprefix$this->table$this->sep"; $sql = "DROP TABLE $this->sep$this->tableprefix$this->table$this->sep";
@@ -785,6 +852,7 @@ class dblayer extends PDO
*/ */
public function createTable () public function createTable ()
{ {
if ($this->debug) echo "== Entering createTable\n";
if ($this->db === null) if ($this->db === null)
throw new Exception (dgettext("domframework", "Database not connected"), throw new Exception (dgettext("domframework", "Database not connected"),
500); 500);
@@ -1089,6 +1157,7 @@ class dblayer extends PDO
Return the an array with the datas */ Return the an array with the datas */
public function directRead ($sql) public function directRead ($sql)
{ {
if ($this->debug) echo "== Entering directRead\n";
$st = $this->db->prepare ($sql); $st = $this->db->prepare ($sql);
$st->execute (); $st->execute ();
$res = array (); $res = array ();