PHP,MVC,DI(C),多个数据库

时间:2013-06-12 21:32:33

标签: php model-view-controller dependency-injection

我正在使用一个简单的项目来帮助我理解并使用DI实现MVC以准备单元测试。该网站将根据请求的页面显示不同的数据表。难点在于数据可能来自不同的数据源(例如mysql或oracle),所以我试图实现DI来减少在两者之间切换所需的代码。我的问题是这些:

1)在何处实例化数据源(可能是mysql,oracle甚至是一个普通的旧数组)并将其传递给需要它的对象(DI甚至是最好的模式)?

2)在什么东西的结构中把数据查询特定于其来源?根据我下面的示例,我有一个类Foo,它在MySQL数据库中查询数据,但如果我稍后移动到Oracle数据库,则必须重写所有语法。我只是创建了两个版本FooMysql和FooOracle并告诉某些地方可以使用哪些?

这是一些基本的,不完整的代码,展示了我所看到的概念。希望你了解我的目标,并指出我正确的方向。我不是在寻找一个已经构建的框架。

$c = new Controller('foo');
print_r($c->getContent());

class Controller
{
    private $view = null;

    public function __construct($viewName)
    {
        $this->view = new $viewName($dbc); // where to get and pass dbc based on view
    }
}

interface ViewInterface
{
    public function getContent();
}

class FooView
{
    private $foo = null;

    public function __construct(DatabaseConnection &$dbc)
    {
        $this->foo = new Foo($dbc);
    }

    public function getContent()
    {
        return $this->foo->getAll();
    }
}

class BarView
{
    private $bar = null;

    public function __construct(DatabaseConnection &$dbc)
    {
        $this->bar = new Bar($dbc);
    }

    public function getContent()
    {
        return $this->bar->getAll();
    }
}

interface StorageInterface
{
    public function getAll();
}

class Foo implements StorageInterface
{
    private $dbc = null;

    public function __construct(DatabaseConnection &$dbc)
    {
        $this->dbc = $dbc;
    }

    public function getAll()
    {
        // mysql code to get all foos
    }
}

class Bar implements StorageInterface
{
    private $dbc = null;

    public function __construct(DatabaseConnection &$dbc)
    {
        $this->dbc = $dbc;
    }

    public function getAll()
    {
        // oracle code to get all bars
    }
}

1 个答案:

答案 0 :(得分:1)

1)在哪里实例化数据源(可能是mysql,oracle甚至是一个普通的旧数组)并将其传递给需要它的对象(DI甚至是最好的模式)?

使用DI,您将拥有一个单独的DI容器,用于处理控制器所需的依赖项实例。 DI是一个很好的选择,因为它将不再需要流程/控制语句(if / else,switch / case),这些语句必须弄清楚你连接的DB是什么,并使其易于单元测试。使用DI,您可以告诉对象这是您要连接的内容。

2)在什么东西的结构中把数据查询特定于其来源?根据我下面的示例,我有一个类Foo,它在MySQL数据库中查询数据,但如果我稍后移动到Oracle数据库,则必须重写所有语法。我只是创建了两个版本的FooMysql和FooOracle,并在某处使用哪些内容?

不,您将为数据库类创建一个接口。这样,当您将依赖项传递给控制器​​时,它将与数据库的类型无关。

实施例

public interface IDatabase {
    function connect();
    function query();
}

public class MySQLDB implements IDatabase {
    function connect() {
        // specific connection properties for mysql
    }
    function query() {
    }

}

public class PostgresDB implements IDatabase {
    function connect() {
        // specific connection properties for postgres
    }
    function query() {
    }
}

public class FooController {
    private $dataBase = null;

    public FooController(IDatabase $dataBase) {
        // guard clause
        if ($database == null)
            throw new Exception("IDatabase");

        $this->dataBase = $dataBase;
    }

    public index() {
        $this->dataBase->query();
    }
}

public class BarController {
    private $dataBase = null;

    public FooController(IDatabase $dataBase) {
        // guard clause
        if ($database == null)
            throw new Exception("IDatabase");

        $this->dataBase = $dataBase;
    }

    public index() {
        $this->dataBase->query();
    }
}

// will be hooked into your http request lifecycle
// to figure out what controller and even method is being called
// this will then return your composed controller
// we are newing up dependencies here, but they are all isolated in one location
// this way the rate of change among classes are not affecting each other
public class DIContainer {
    public resovleController() {
        // find out the http request info
        if (FooController) {
            return new FooController(new MySQLDB());
        } elseif (BarController) {
            return new FooController(new PostgresDB());
        }
    }
}

在你的例子中也有一些代码味道,尝试摆脱新的类。
此外,视图不应负责处理要传递的依赖项。
您肯定需要了解如何创建DI容器,以及在命名空间中组织代码。
您还需要创建一个前置控制器,DI容器可以装入其中,或者DI容器可以作为前端控制器

希望这有帮助。