Porque tu Model
la clase instancia una nueva Database
objeto en su constructor, cada vez que instancias un Model
(o cualquier clase que la amplíe), en realidad está abriendo un nuevo conexión de base de datos. Si crea varios Model
objetos, cada uno tiene su propia conexión de base de datos independiente, lo cual es poco común, generalmente innecesario, no es un buen uso de los recursos, pero también es activamente dañino ya que ha usado todas las conexiones disponibles del servidor.
Por ejemplo, hacer un bucle para crear una matriz de Model
objetos:
// 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)
Usar inyección de dependencia:
La solución es usar inyección de dependencia y pasar la Database
objeto en el Model::__construct()
en lugar de permitirle que cree una instancia propia.
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;
}
}
Entonces, para usarlo, el código de control (el código que creará una instancia de sus modelos) debería llamar a new Database()
sólo una vez. Ese objeto creado por el código de control debe luego pasarse a los constructores de todos los modelos.
// Instantiate one Database
$db = new Database();
// Pass it to models
$model = new Model($db);
Para el caso de uso en el que realmente necesita una conexión de base de datos independiente diferente para un modelo, puede entregarle una diferente. En particular, esto es útil para probar . Puede sustituir un objeto de base de datos de prueba o un objeto simulado.
// Instantiate one Database
$db = new Database();
$another_db = new Database();
// Pass it to models
$model = new Model($db);
$another_model = new Model($another_db);
Conexiones persistentes:
Como se menciona en los comentarios, usar una conexión persistente es posiblemente una solución, pero no es la solución que recomendaría . PDO intentará reutilizar una conexión existente con las mismas credenciales (como todas las suyas), pero no necesariamente desea que la conexión se almacene en caché durante la ejecución del script. Si decidió hacerlo de esta manera, debe pasar el atributo a la Database
constructor.
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'");
}
La documentación relevante está aquí:http://php.net/manual /es/pdo.conexiones.php#ejemplo-950
Solución única:
Usando un patrón singleton (tampoco recomendado), al menos podría reducir esto a una búsqueda/reemplazo en el código del modelo. La Database
la clase necesita una propiedad estática para mantener una conexión por sí misma. Luego, los modelos llaman a Database::getInstance()
en lugar de new Database()
para recuperar la conexión. Debería hacer una búsqueda y reemplazar en el código del modelo para sustituir Database::getInstance()
.
Aunque funciona bien y no es difícil de implementar, en su caso dificultaría un poco más las pruebas, ya que tendría que reemplazar toda la Database
clase con una clase de prueba del mismo nombre. No puede sustituir fácilmente una clase de prueba caso por caso.
Aplicar patrón singleton a 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;
}
}
Ahora necesitaría cambiar solo el Model
código para usar Database::getInstance()
:
class Model {
protected $_db;
public function __construct(){
// Retrieve the database singleton
$this->_db = Database::getInstance();
}
}