我对如何处理重用数据库对象和配置代表应用程序全局的变量或常量的逻辑感到困惑。
到目前为止,我一直在做的是,我在Config
目录下创建了一个config.php
文件,并声明了所有config
元素,例如,我典型的config.php文件看起来像这样。
#Start Session
session_start();
#Start Output Buffering
ob_start();
#Set Default TimeZone
date_default_timezone_set('Asia/Kolkata');
#Define Time Constant
define('DATE', date("d-F-Y/H:ia"));
#Define Paths
define('CLASSES',$_SERVER['DOCUMENT_ROOT'].'/resources/library/classes/');
define('MODELS',$_SERVER['DOCUMENT_ROOT'].'/resources/library/models/');
define('LOGS',$_SERVER['DOCUMENT_ROOT'].'/resources/logs/');
#Define Connection Constant
define('HOST','localhost');
define('USERNAME','username');
define('PASSWORD','password');
define('DATABASE','dbname');
try
{
#Connection String.
$dbh = new PDO('mysql:host='.HOST.';dbname='.DATABASE,USERNAME,PASSWORD);
#Set Error Mode to ERRMODE_EXCEPTION.
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e)
{
#Print Errors.
echo $e->getMessage();
#Log Errors into a file
file_put_contents(LOGS.'Connection-log.txt', DATE.PHP_EOL.$e->getMessage().PHP_EOL.PHP_EOL, FILE_APPEND);
}
#Autoload Classes.
function __autoload($class) {
require_once CLASSES.'class.'.strtolower($class).'.php';
}
和在index.php文件中,我将包含这个文件一次,并在每个对象中重用它。
my index.php通常由这样的控制器组成。
if(isset($_GET['users'])) {
require_once(MODELS.'users/users.php');
} else if(isset($_GET['categories'])) {
require_once(MODELS.'categories/categories.php');
} else if(isset($_GET['search'])) {
require_once(MODELS.'search/search.php');
}
在模型中,我将实例化我想要的对象并使用它,例如在users/users.php
中,我将像这样实例化它
$user = new User($dbh);
一切都很好,但问题是每个类我必须通过构造函数传递数据库句柄对象,并在类中重用它,这对我来说有点难看。这种方法为我创造了一个问题,如果我想使用包含静态方法和属性的类,其中包含要从数据库检索的应用程序设置。我的要求是……
我想使用单例模式创建一个静态方法,该方法将保存要在整个应用程序中使用的数据库对象,而不需要每次通过每个类的构造函数传递$dbh
对象。我很困惑该如何处理这个问题。
谢谢
我有一个类似的设计模式,我在全局中连接并存储连接。
您不需要将数据库变量传递给每个类,因为它是全局变量。
你可以像这样在任何地方使用它:
$GLOBALS['dbh'];
为了隐藏这个,我实际上创建了一个名为get_db_connection()
的函数,该函数首先检查$GLOBALS
数组中是否存在有效连接,然后返回它。如果没有有效的连接,它会创建一个新的连接,将其存储在$GLOBALS中并返回。
它还有一个额外的好处,我确实需要在任何地方手动实例化连接,它是在第一次调用get_db_connection
时自动创建的,随后在任何地方都可用。
get_db_connection
函数看起来像这样:
function get_db_connection(){
if(isset($GLOBALS['db_conn']) && get_class($GLOBALS['db_conn']) == 'PDO'){
return $GLOBALS['db_conn'];
}else{
$conn = new PDO(/* parameters */);
$GLOBALS['db_conn'] = &$conn;
return $conn;
}
}
defence in favour of global
我认为这是全局变量的一种可原谅的用法,原因如下:
- 变量$dbh已经是全局变量,因为它不在函数中或者一个班级。
- 您没有在任何地方直接访问全局您的程序,但只能通过一个函数
get_db_connection
所以任何人都可以改变全局值的问题不是在这里。 解决这个问题的唯一方法是使用Singleton,这可能是对于这么简单的问题来说是不必要的。
我认为第二个原因最为具体。
这是我最后想到的。
class DB {
protected static $_dbh;
const HOST = 'localhost';
const DATABASE = 'dbname';
const USERNAME = 'usname';
const PASSWORD = 'passwo';
private function __construct() { }
public static function get_db_connection() {
if(!isset(self::$_dbh)) {
self::$_dbh = new PDO('mysql:host='.self::HOST.';dbname='.self::DATABASE,self::USERNAME,self::PASSWORD);
self::$_dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return self::$_dbh;
}
}
应用单例模式和静态方法调用可以很容易地访问数据库对象,而无需通过类构造函数传递数据库对象。
要在类范围内或外调用数据库对象,我所要做的就是调用一行代码。
DB::get_db_connection();
你可以尝试这样做:
function cnn() {
static $pdo;
if(!isset($pdo)) {
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);
$pdo->setAttribute(PDO::ATTR_TIMEOUT, 30);
$pdo->setAttribute(PDO::ATTR_PERSISTENT, true);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
return $pdo;
} else {
return $pdo;
}
}
之后,你可以调用任何你想要的查询:
echo cnn()->query('SELECT firstname FROM user WHERE id=4;')->fetch(PDO::FETCH_COLUMN)
第二个查询(对象被重用)
echo cnn()->query('SELECT title FROM news WHERE id=516;')->fetch(PDO::FETCH_COLUMN)