Eftersom din Model
klass instansierar en ny Database
objekt i dess konstruktor, varje gång du instansierar en Model
(eller någon klass som utökar den), öppnar du i praktiken en ny databasanslutning. Om du skapar flera Model
objekt, var och en har sedan sin egen oberoende databasanslutning, vilket är ovanligt, vanligtvis onödigt, inte en bra användning av resurser, men också aktivt skadlig eftersom den har använt upp alla serverns tillgängliga anslutningar.
Till exempel, looping för att skapa en array av Model
objekt:
// If a loop creates an array of Model objects
while ($row = $something->fetch()) {
$models[] = new Model();
}
// each object in $models has an independent database connection
// the number of connections now in use by MySQL is now == count($models)
Använd beroendeinjektion:
Lösningen är att använda beroendeinjektion och pass Database
objekt till Model::__construct()
snarare än att låta den instansiera sin egen.
class Model {
protected $_db;
// Accept Database as a parameter
public function __construct(Database $db) {
// Assign the property, do not instantiate a new Database object
$this->_db = $db;
}
}
För att använda den då bör den kontrollerande koden (koden som kommer att instansiera dina modeller) själv anropa new Database()
bara en gång. Det objektet som skapas av den styrande koden måste sedan skickas till konstruktörerna av alla modeller.
// Instantiate one Database
$db = new Database();
// Pass it to models
$model = new Model($db);
För användningsfallet där du faktiskt behöver en annan oberoende databasanslutning för en modell, kan du ge den en annan. Detta är särskilt användbart för testning . Du kan ersätta ett testdatabasobjekt eller ett skenobjekt.
// Instantiate one Database
$db = new Database();
$another_db = new Database();
// Pass it to models
$model = new Model($db);
$another_model = new Model($another_db);
Ihållande anslutningar:
Som nämnts i kommentarerna är användning av en beständig anslutning möjligen en lösning, men inte den lösning jag skulle rekommendera . PDO kommer att försöka återanvända en befintlig anslutning med samma referenser (som alla dina kommer att ha), men du vill inte nödvändigtvis att anslutningen ska cachelagras under skriptkörning. Om du bestämde dig för att göra det på detta sätt måste du skicka attributet till Database
konstruktör.
try {
// Set ATTR_PERSISTENT in the constructor:
parent::__construct(DB_TYPE.':host='.DB_HOST.';dbname='.DB_NAME,DB_USER,DB_PASS, array(PDO::ATTR_PERSISTENT => true));
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
}
Den relevanta dokumentationen finns här:http://php.net/manual /en/pdo.connections.php#example-950
Singleton lösning:
Genom att använda ett singelmönster (rekommenderas inte heller) kan du åtminstone reducera detta till en sökning/ersätt i modellkoden. Database
klass behöver en statisk egenskap för att hålla en anslutning för sig själv. Modeller anropar sedan Database::getInstance()
istället för new Database()
för att hämta anslutningen. Du skulle behöva göra en sökning och ersätta i modellkoden för att ersätta Database::getInstance()
.
Även om det fungerar bra och inte är svårt att implementera, skulle det i ditt fall göra testningen lite svårare eftersom du skulle behöva ersätta hela Database
klass med en testklass med samma namn. Du kan inte enkelt ersätta en testklass på en instans för instans.
Tillämpa singelmönster på Database
:
class Database extends PDO{
// Private $connection property, static
private static $connection;
// Normally a singleton would necessitate a private constructor
// but you can't make this private while the PDO
// base class exposes it as public
public function __construct(){
try {
parent::__construct(DB_TYPE.':host='.DB_HOST.';dbname='.DB_NAME,DB_USER,DB_PASS);
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
} catch(PDOException $e){
Logger::newMessage($e);
logger::customErrorMsg();
}
}
// public getInstance() returns existing or creates new connection
public static function getInstance() {
// Create the connection if not already created
if (self::$connection == null) {
self::$connection = new self();
}
// And return a reference to that connection
return self::$connection;
}
}
Nu skulle du bara behöva ändra Model
kod för att använda Database::getInstance()
:
class Model {
protected $_db;
public function __construct(){
// Retrieve the database singleton
$this->_db = Database::getInstance();
}
}