在PHP中使用OOP方法时,在相关实例中,数据库连接的更好方法是什么?
- 传递给所有实例的数据库连接
- 每个实例都有自己的数据库连接
这是我的代码示例。
正在将连接传递给构造函数:
<?php
class Model {
public $db;
public function __construct($db) {
$this->db = $db;
}
}
正在连接到模型构造函数中的数据库。
class Model {
protected $db;
public function __construct() {
$this->db = new Detabase_Con();
}
}
用户模型
class Users extends Model {
public function showUsers() {
$this->db->select("SELECT * FROM users");
}
}
Singleton数据库访问
这是非常有争议的,但可以确保数据库连接不会重复,并且更容易在现有代码中使用。
class DB {
private $instance=null;
private static function instance() {
if (self::$instance === null) {
self::$instance = new PDO(/*connection foo*/);
}
return self::$instance;
}
public static function __callStatic($method, $args) {
return call_user_func_array(array(self::instance(), $method), $args);
}
}
include('DB.php'); // the file containing the class
$sth = DB::prepare("SELECT foo");
$sth->execute();
$row = $sth->fetch();
这在很大程度上借鉴了这个答案中提出的简单PDO包装器。包装是为精确的需求而写的,所以按原样使用
依赖项注入
这可能意味着你必须更好地考虑你的代码,也许还要重写其中的大部分。重点是在类/函数签名中键入提示你的连接,以明确它取决于PDO:
class user{
function construct(PDO $db,$user_id) {
// define your user with the infos from $db
}
}
$db = new PDO(/*connection foo*/);
$wants_db = new user($db,$user_id);
如果重写所有函数签名以传递$db
,您可能会发现它并没有更好,而且可能是真的。您可能需要重新设计应用程序的某些部分,以便不是所有的功能或方法都依赖于数据库连接。
如何选择
单例方法现在更容易了,可能适合您的需求,但会带来问题。如果您意识到将其作为参数传递是一种更好的方法,那么您就不想启动此操作并重构代码
依赖注入兼容的方法现在更难了,因为它意味着代码的更改,但这种能量意味着最终您将拥有更好/更干净/更容易维护的代码。
在对singleton进行了实验之后,我认为实现注入的数据库访问更好。我认为它代表了固体的一部分。我想说,这为php oop脚本中的数据库访问提供了一种更好的、更经得起未来考验的方法。
让所有的类从包含数据库连接的类继承是个非常糟糕的主意。这不是MVC本身的问题,而是对面向对象编程方法的滥用
因此,第一种方法看起来要好得多。
就我个人而言,我喜欢将数据库连接作为一个单例。例如:
<?php
class MyDatabase extends PDO
{
public static function SharedInstance() {
static $instance = false;
if(!$instance) {
if(!($instance = new MyDatabase('mysql:.....', '', '', array(PDO::ATTR_PERSISTENT => true)))) {
throw new Exception('Cannot connect to database');
}
$instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$instance->exec('SET NAMES utf8'); // MySQL only
}
return $instance;
}
// ... More methods and utils for the database class, if you need to extend it
}
?>
然后,在代码的任何地方(在任何范围内),我都可以通过简单地执行以下操作来回忆到与数据库的连接:
$db = MyDatabase::SharedInstance();
// And use it as a normal PDO object. For example:
$db->exec('some query');
您不需要使用PDO;您也可以使用MySQLi和其他数据库驱动程序来实现这一点。但是,当数据库驱动程序具有面向对象的接口时,它的工作效果最好。
附言:只是为了确保,总是使用事先准备好的陈述。上面对$db->exec
的调用只是公开PDO接口的一个示例。
为我的想法辩护
我知道单身人士是有争议的,我并不是说他们是解决任何问题的最佳方案。然而,在这种情况下,它们可以而且确实有意义。以下是我在评论中收到的一些批评:
它们使单元测试更加困难
由于我没有覆盖__construct()
方法以防止从MyDatabase
类初始化其他对象,因此您应该能够编写没有任何问题的测试。您正在对类名进行硬编码
假设我们没有使用singleton。许多用户会为数据库连接使用全局变量;硬编码的东西";。无论如何,编程是硬编码的东西,所以我不明白为什么这应该是一个问题。最糟糕的情况:只运行find&替换所有项目文件。任何编辑都可以轻松做到这一点。很难连接到多个(SQL)数据库
首先,我敢打赌,SO上只有0.01%的开发人员真正需要连接到同一PHP应用程序中的多个SQL数据库。对于那些这样做的人来说,单身可能是他们处理的最后一个问题
因此,使用这种方法,您仍然可以连接到NoSQL数据库和缓存,实际上您可以在MyDatabase类中包含一些缓存逻辑。如果您需要使用PDO连接到另一个SQL数据库,那么只需创建PDO类的另一个实例(没有什么可以阻止您这样做!),或者创建另一个singleton。同样,这不是问题。您允许SQL注入
由于这只是PDO的一个包装,我想说PDO首先允许SQL注入。如果您不知道如何正确使用准备好的语句或引用查询参数,那么这不是我的错。(您允许SQL注入),因为它配置不好
我在上面的代码中没有看到任何配置。只有一个使用PDO的数据库连接。这很糟糕,因为这篇SO帖子这么说
引用自维基百科:
Ipse dixit,拉丁语";他自己说;是一个用来识别和描述一种武断的教条式陈述的术语,说话者希望听众接受这种陈述。通过赤裸裸地断言一个命题"是"来为其辩护的谬论;就是这样";通过选择完全退出来扭曲论点:索赔人宣称一个问题是内在的,不能改变。
然而,SO上的其他帖子也为单身人士辩护。然而,最终重要的是保持开放的心态,而不仅仅是把自己困在教条主义的墙后面。