PHP: iniezione della stessa connessione al database in più oggetti

6

Supponiamo che ci siano due classi che definiscono oggetti con funzioni molto diverse in modo tale che nel datastore le informazioni richieste siano divise in due database separati.

Ad esempio, le classi Employee e SteelExtruder possono essere inserite rispettivamente nei database 'human_resources' e 'capital' . Piuttosto che codificare in modo rigido le connessioni ai rispettivi database:

class Employee {
    protected $dbc;
    public function __construct() {
        $this->dbc = new mysqli('localhost', 'user', 'pass', 'human_resources');
    }
}

Riesco a vedere i vantaggi in termini di flessibilità derivanti dall'iniezione della connessione al database nel costruttore:

class Employee {
    protected $dbc;
    public function __construct(mysqli $dbc) {
        $this->dbc = $dbc;
    }
}

Tuttavia, non riesco a vedere come implementarlo correttamente. Ho letto che i singleton e le librerie di database sono pratiche sbagliate, poiché si limitano a mascherare il fatto che sono in realtà solo variabili globali. D'altra parte, le alternative non sembrano neanche fantastiche:

A) Dichiara le connessioni necessarie a livello globale in un file del caricatore. Funzionalmente identico a una classe di libreria di database, ma ha un cattivo odore:

loader.php:
$dbc_human_resources = new mysqli('localhost', 'user', 'pass', 'human_resources');
$dbc_capital = new mysqli('localhost', 'user', 'pass', 'capital');

index.php:
$employees = array(
    new Employee($dbc_human_resources),
    new Employee($dbc_human_resources),
    ...
    );

B) Crea una nuova connessione ogni volta che viene creato un nuovo oggetto. A meno che non ci sia un qualche tipo di ottimizzazione di cui non sono a conoscenza, questo sembra incurantemente inefficiente.

$employees = array(
    new Employee(new mysqli('localhost', 'user', 'pass', 'human_resources')),
    new Employee(new mysqli('localhost', 'user', 'pass', 'human_resources')),
    ...
);

C) Archivia tutti gli oggetti in un oggetto libreria che richiede una singola connessione, che non sembra così male ma che si sente sbagliato:

Employee.php:
class Employee {
    //non-database properties
    public function __construct($args = array()) {
        //non-database initialization
    }
}

EmployeeLibrary.php:
class EmployeeLibrary {
    protected $dbc, $employees;
    public function __construct(mysqli $dbc) {
        $this->dbc = $dbc;
    }
    public function add_new_employee($args) {
        array_push($this->employees, new Employee($args));
    }
    //do things with database connection
}

loader.php:
$employee_library = new EmployeeLibrary(new mysqli('localhost', 'user', 'pass', 'human_resources');

index.php:
$employee_library->add_new_employee(...);
$employee_library->add_new_employee(...);

Sono sicuro che questo non è un problema raro. C'è un modo accettato per farlo?

    
posta concat 21.06.2015 - 18:39
fonte

1 risposta

5

Tutti e tre sono sbagliati, perché stai memorizzando stringhe di connessione nel codice sorgente. Il codice sorgente non è il posto giusto per la configurazione , perché non ti è richiesto di dover modificare il codice (e così, fai tutti i test di regressione) ogni volta che il tuo database si muove o ogni volta che passare dal database di sviluppo alla gestione temporanea e al database di produzione .

Invece:

  • Archivia la stringa di connessione in un file di configurazione .

  • Crea una classe che accede alle opzioni del file di configurazione.

    La classe dovrebbe fornire ai chiamanti l'interfaccia semplice a tali opzioni; i chiamanti non dovrebbero preoccuparsi di dove sia il file di configurazione o di come le opzioni sono memorizzate all'interno.

    Questa astrazione consente in seguito di passare, ad esempio, da una configurazione memorizzata in un file JSON, a una configurazione memorizzata in un file XML, o anche a spostare la configurazione in un database come Redis.

  • Crea un'istanza di questa classe ogni volta che devi accedere alle opzioni di configurazione.

    Non hai bisogno di un singleton qui; se un giorno le prestazioni diventano un problema (ovvero, il tempo necessario per aprire e analizzare il file di configurazione diventa proibitivo), utilizzare la memorizzazione nella cache.

    Se sei assolutamente sicuro (attenzione a YAGNI) che dovrai passare presto a un diverso meccanismo di archiviazione (ad esempio dal file JSON a Redis), puoi utilizzare Dependency Injection creando l'oggetto di configurazione nella parte superiore di lo stack e quindi passarlo ai chiamanti. Ciò garantirà che quando si passa a un meccanismo diverso, non cambierà più di una riga nella base di codice, sostituendo:

    $configuration = new JsonFileAppConfiguration();
    

    da:

    $configuration = new RedisAppConfiguration();
    

    ma tutto il resto si baserà solo sull'interfaccia IAppConfiguration che entrambe le classi implementano.

risposta data 21.06.2015 - 19:07
fonte