[mysql]PHP 的 PDO 的单身人士选择

标签: MySQL PHP
发布时间: 2017/4/9 22:08:05
注意事项: 本文中文内容可能为机器翻译,如要查看英文原文请点击上面连接.

这是我用来连接到我班我 MySQL 数据库。 正如你可以看到我使用的 Singleton Pattern ,但几乎每篇文章说,这是一个非常坏的模式。创建一个数据库连接类的最佳方法是什么?有更好的模式吗?

class DB extends PDO {

    function __construct() {
        try {
            parent::__construct('mysql:host=' . 'localhost' . ';dbname=' . 'kida', 'root', 'root', array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'");
            parent::setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch(PDOException $e) {
            echo $e->getMessage();
        }
    }

    public static function get_instance() {
        static $instance = false;
        if(!$instance) $instance = new self;
        return $instance; //returns pdo object.
     }
}

解决方法 1:

使用单例模式 (或反模式) 被认为是坏实践,因为它使得测试您的代码很难和 depencies 非常费解直到项目变得难以管理在一些点。你只能有一个固定的对象实例时每个 php 进程。当编写自动化单元测试您的代码,您需要能够替换对象您想要测试的代码使用 prdictable 秩序的行为的测试双。当您想要测试的代码使用的单身人士时,你不能取代,与双测试。

(据我所知) 的最佳方式组织 (如您的数据库对象和其他对象使用的数据库) 的对象之间的交互将会扭转方向的 depencies。这意味着您的代码不请求的对象,它需要从外部源 (在大多数情况下一个全球性像静态 'get_instance' 方法中,从您的代码) 但相反获取其 depency 对象 (它需要的一个) 服务从外面之前需要它。通常你会使用 Depency 注入经理/容器这样一个由 symfony 项目撰写您的对象。

使用数据库对象的对象将把它注入后施工。它可以注射 setter 方法或构造函数中。在大多数情况下 (不是全部) 是更好,因为这种方式使用数据库对象的对象永远不会注入 depency (你 db 对象) 的构造函数中处于无效状态。

示例︰

interface DatabaseInterface
{
    function query($statement, array $parameters = array());
}

interface UserLoaderInterface
{
    public function loadUser($userId);
}

class DB extends PDO implements DatabaseInterface
{
    function __construct(
        $dsn = 'mysql:host=localhost;dbname=kida',
        $username = 'root',
        $password = 'root',
    ) {
        try {
            parent::__construct($dsn, $username, $password, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'");
            parent::setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch(PDOException $e) {
            echo $e->getMessage();
        }
    }

    function query($statement, array $parameters = array())
    {
        # ...
    }
}

class SomeFileBasedDB implements DatabaseInterface
{
    function __construct($filepath)
    {
        # ...
    }

    function query($statement, array $parameters = array())
    {
        # ...
    }
}

class UserLoader implements UserLoaderInterface
{
    protected $db;

    public function __construct(DatabaseInterface $db)
    {
        $this->db = $db;
    }

    public function loadUser($userId)
    {
        $row = $this->db->query("SELECT name, email FROM users WHERE id=?", [$userId]);

        $user = new User();
        $user->setName($row[0]);
        $user->setEmail($row[1]);

        return $user;
    }
}

# the following would be replaced by whatever DI software you use,
# but a simple array can show the concept.


# load this from a config file
$parameters = array();
$parameters['dsn'] = "mysql:host=my_db_server.com;dbname=kida_production";
$parameters['db_user'] = "mydbuser";
$parameters['db_pass'] = "mydbpassword";
$parameters['file_db_path'] = "/some/path/to/file.db";


# this will be set up in a seperate file to define how the objects are composed
# (in symfony, these are called 'services' and this would be defined in a 'services.xml' file)
$container = array();
$container['db'] = new DB($parameters['dsn'], $parameters['db_user'], $parameters['db_pass']);
$container['fileDb'] = new SomeFileBasedDB($parameters['file_db_path']);

# the same class (UserLoader) can now load it's users from different sources without having to know about it.
$container['userLoader'] = new UserLoader($container['db']);
# or: $container['userLoader'] = new UserLoader($container['fileDb']);

# you can easily change the behaviour of your objects by wrapping them into proxy objects.
# (In symfony this is called 'decorator-pattern')
$container['userLoader'] = new SomeUserLoaderProxy($container['userLoader'], $container['db']);

# here you can choose which user-loader is used by the user-controller
$container['userController'] = new UserController($container['fileUserLoader'], $container['viewRenderer']);

请注意如何不同的类不是相互了解。他们之间没有直接的 depencies。这通过不需要实际的类在构造函数中,但相反需要提供它需要的方法的接口。

这样您可以总是写替换为您的类,并只是取代他们在 depency 注入容器中。你不需要检查整个代码库,因为更换只是要实现相同的接口所使用的所有其他类。你知道一切都将继续工作,因为每个组件使用旧类只知道该接口,调用只知道由接口的方法。

P.S.︰ 请原谅我经常提到的 symfony 的项目,它是只是什么我最习惯。其他项目喜欢 Drupal,推进或 Zend 可能也有这样的概念。

官方微信
官方QQ群
31647020