I have a series of Classes:
abstract class Database extends PDO {} abstract class OracleDatabase extends Database {} abstract class MySQLDatabase extends Database {} abstract class MSSQLDatabase extends Database {} Then when I want an instance of a database connection, I create a new class that extends either OracleDatabase, MySQLDatabase or MSSQLDatabase, depending on where the database lives... e.g.
class MyAppDatabase extends OracleDatabase {} First of all, is this a good design? I've read that it's better to use Interfaces rather than extending abstract classes, but I'm not sure how to do that without duplicating code.
I do realize the whole point of PDO is to get away from DB-specific code, but I do still need different functionality when for things like DB object bracketing (e.g. in Oracle, you use double quotes; in MySQL you use backticks), data type detection (user_tab_columns vs INFORMATION_SCHEMA), etc.
And - what if I want to create a class called Updater which lets the client create a set of multiple "updates" to a specific table - using the same fields for SET and WHERE clauses, but different values? I don't think I'd be able to inherit from the Database class, so would I just make it an independent class that "HAS" a Database object?
class Updater { private $db; private $table; private $setFields; private $whereFields; private $updates; function __construct($db, $table, $setFields, $whereFields) { if (!($db instanceof Database)) { if (is_scalar($db)) { $type = gettype($db); } else { $type = get_class($db); } throw new Exception("Expected Database object; " . $type . " found."); } $this->db = $db; // ensure $table is a table in the database // ensure $setFields and $whereFields are columns in the table $this->table = $table; $this->setFields = $setFields; $this->whereFields = $whereFields; $this->updates = array(); } function addUpdate($setValues, $whereValues) { // ensure $setValues is an array and has the same cardinality as // $this->setFields // ensure $whereValues is an array and has the same cardinality as // $this->whereFields array_push($this->updates, array( 'setValues'=>$setValues, 'whereValues' => $whereValues ) ); } function doUpdate() { // without error handling $escTable = $this->db->bracket($table); $setTemplate = array(); foreach ($this->setFields as $setField) { $escField = $this->db->bracket($setField); $colonField = makeColonField($setField); // :fieldName $setting = "$escField = $colonField"; array_push($setTemplate, $setting); } $csvSetTemplate = implode(", ", $setTemplate); $whereTemplate = array(); foreach ($this->whereFields as $whereField) { $escField = $this->db->bracket($whereField); $colonField = makeColonField($setField); // :fieldName $setting = "$escField = $colonField"; array_push($whereTemplate, $setting); } $andedWhereTemplates = implode(" AND ", $whereTemplate); $sql = "UPDATE $escTable SET $csvSetTemplate WHERE $andedWhereTemplates"; $sth = $this->db->prepare($sql); foreach ($this->updates as $update) { $setValues = $update['setValues']; $whereValues = $update['whereValues']; $params = array(); for ($i=0; $i<count($setValues); $i++) { $setField = $this->setFields[$i]; $setValue = $setValues[$i]; $colonField = makeColonField($setField); $params[$colonField] = $setValue; } for ($i=0; $i<count($whereValues); $i++) { $whereField = $this->whereFields[$i]; $whereValue = $whereValues[$i]; $colonField = makeColonField($whereField); $params[$colonField] = $whereValue; } $sth->execute($params); } } } Is this a good solution?
Oracle/MySQL/MSSQLDatabaseas abstract? Seems they could be concrete, rather than declaring a new class to declare an instance of each.