È questo wrapper per la DOP "il buon codice"?Ci sono potenziali problemi?
-
03-07-2019 - |
Domanda
Ho costruito questa classe per lavorare con DOP, per rendere la query SQL "facile" e meno di cui preoccuparsi.
Qui sono i miei pensieri
- Dovrebbe essere più come classe DB si estende DOP?
- È il metodo query troppo grande?Dovrebbe essere suddivisi in metodi privati che sono chiamati..questo è ciò che è noto come accoppiamento?
- È il mio modo per la rilevazione di una query di selezione troppo brutta per il proprio bene?
- Quali altri problemi sono evidenti?Come io sono il tipo di apprendimento-come-mi-go, sono sicuro che avrei potuto avere di fronte un sacco di potenziali problemi.
Grazie
`
class Db
{
private static $_instance = NULL;
private function __construct() {
// can not call me
}
private function __clone() {
// no!
}
public static function getInstance() {
if (!self::$_instance)
{
try {
self::$_instance = new PDO('mysql:host=' . CONFIG_MYSQL_SERVER . ';dbname=' . CONFIG_MYSQL_DATABASE, CONFIG_MYSQL_USERNAME, CONFIG_MYSQL_PASSWORD);;
self::$_instance-> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
trigger_error($e->getMessage());
}
}
return self::$_instance;
}
public static function query($query /*string*/, $bindings = NULL)
{
$queryPortion = substr($query,0, 6);
try {
if ($bindings) {
$prepared = self::getInstance()->prepare($query);
foreach($bindings as $binding=>$data) { // defaults to string
if (!is_array($data)) {
$prepared->bindParam($binding, $data);
} else {
switch(count($data)) {
case 1:
$prepared->bindParam($binding, $data['value']);
break;
case 2:
$prepared->bindParam($binding, $data['value'], $data['dataType']);
break;
case 3:
$prepared->bindParam($binding, $data['value'], $data['dataType'], (int)$data['length']);
break;
default:
trigger_error('An error has occured with the prepared statement bindings.');
return false;
break;
}
}
}
$prepared->execute();
return $prepared->fetchAll(PDO::FETCH_ASSOC);
} else if (String::match($queryPortion, 'select')) { // if this is a select query
$rows = self::getInstance()->query($query);
return $rows->fetchAll(PDO::FETCH_ASSOC);
} else {
return self::getInstance()->exec($query);
}
}
catch(PDOException $e)
{
trigger_error($e->getMessage());
}
}
public static function getLastInsertId()
{
try {
self::getInstance()->lastInsertId();
}
catch(PDOException $e)
{
trigger_error($e->getMessage());
}
}
public static function disconnect()
{
// kill PDO object
self::$_instance = NULL;
}
}
Soluzione
Non è male e come è stato detto, potrebbe essere di aiuto per le piccole applicazioni anche se è per lo più molto sottile astrazione su un altro astrazione.Non portare un sacco di altre funzionalità.
Qualcosa che si potrebbe desiderare di prendere in considerazione, tra le altre cose:
- Questo è PHP5 codice, utilizzare eccezioni invece di
trigger_error
eset_exception_handler
se necessario, fino a che le eccezioni sono più diffuse, ma è sicuramente più pulito e più a prova di futuro. - Si utilizza un singleton, non è una brutta cosa, necessariamente, ma in questo caso, per esempio, un inconveniente sarà che sarete solo in grado di gestire una connessione a un database.
- Non so se si fanno uso di stored procedure, ma anche di una stored procedure potrebbe restituire un set di risultati attraverso il
query()
metodo di troppo. - Si dispone di due punti e virgola (
;;
) alla fine delnew PDO
linea.
Detto questo, non credo che il tuo metodo di query è troppo grande e non c'è molto che potrebbe essere richiamato da altre parti c'è al momento.Anche se appena vedi due o tre righe che potrebbe essere chiamato da un'altra funzione, dividere.Questo è un buon modo per LAVAGGIO.
Altri suggerimenti
Sì e No.
È un buon codice per un semplice sporca e veloce applicazione.
Il problema nasce quando si utilizza questo in un più complesso strutturato di applicazione.Dove l'errore di gestione variano a seconda che sql in esecuzione.
Anche eventuali gravi errori verranno visualizzati come "problema in linea di 999" tipo di errori dove 999 è nel super duper routine e si avrà difficoltà a ritornare alle origini per una particolare richiesta sql.
Dopo aver detto che faccio questo genere di cose a me stesso per tutto il tempo su progetti di piccole dimensioni.
Ecco quello che ho usato (basta sostituire i riferimenti al Zzz_Config con $GLOBALS['db_conf'] o qualcosa del genere):
/**
* Extended PDO with databse connection (instance) storage by name.
*/
class Zzz_Db extends PDO
{
/**
* Named connection instances.
*
* @var array
*/
static private $_instances;
/**
* Retrieves (or instantiates) a connection by name.
*
* @param string $name Connection name (config item key).
* @return Zzz_Db Named connection.
*/
static public function getInstance($name = null)
{
$name = $name === null ? 'db' : "db.$name";
if (!isset(self::$_instances[$name])) {
if (!$config = Zzz_Config::get($name)) {
throw new RuntimeException("No such database config item: $name");
}
if (!isset($config['dsn'])) {
if (!isset($config['database'])) {
throw new RuntimeException('Invalid db config');
}
$config['dsn'] = sprintf('%s:host=%s;dbname=%s',
isset($config['adapter']) ? $config['adapter'] : 'mysql',
isset($config['host']) ? $config['host'] : 'localhost',
$config['database']);
}
$db = self::$_instances[$name] = new self(
$config['dsn'],
isset($config['username']) ? $config['username'] : null,
isset($config['password']) ? $config['password'] : null);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//$db->setAttribute(PDO::ATTR_STATEMENT_CLASS, 'Zzz_Db_Statement');
if ($db->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql') {
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
$db->exec('SET CHARACTER SET utf8');
}
}
return self::$_instances[$name];
}
}
L'utilizzo sarebbe:
$db = Zzz_Db::getInstance(); // or Zzz_Db::getInstance('some_named_db')
$stmt = $db->prepare('SELECT ...
L'obiettivo è di mantenere il db di configurazione in un *.file ini (modificabile da un non-programmatore).
Sono andato l'altro modo e di fatto una classe che estende DOP con un sacco di funzioni wrapper attorno prepare()
/execute()
, ed è molto più bello di funzioni (anche se è un po ' soggettivo...).
Un'altra cosa:è necessario impostare PDO::ATTR_EMULATE_PREPARES
per false
a meno che non si sta utilizzando un davvero vecchia versione di mysql (<=4.0).L'impostazione di default è true
, che è un enorme mal di testa e fa le cose per rompere clandestinamente...sto indovinando che è il motivo che hai un grande involucro intorno bindParam()
in primo luogo.
Per rispondere alla tua domanda, se è un buon codice o non, chiedete a voi stessi:
Qual è il valore aggiunto del mio codice, rispetto all'utilizzo di prodotti DOP direttamente?
Se si trova una buona risposta, andare per l'utilizzo del codice.Se non, vorrei bastone con DOP.
Anche provare a prendere in considerazione l' Zend Framework's DB classe che lavora in proprio e supporta DOP.