PHP、MVC、DI(C)、多数据库

PHP、MVC、DI(C)、多数据库,php,model-view-controller,dependency-injection,Php,Model View Controller,Dependency Injection,我正在使用一个简单的项目来帮助我理解并使用DI实现MVC,为单元测试做准备。网站将根据请求的页面显示不同的数据表。困难在于数据可能来自不同的数据源(例如mysql或oracle),因此我尝试实现DI以减少在两者之间切换所需的代码。我的问题是: 1) 在何处实例化数据源(可以是mysql、oracle,甚至是一个普通的旧数组),并将其发送到需要它的对象(DI是最好的模式吗) 2) 在事物结构的何处放置特定于其源的数据查询?根据我下面的例子,我有一个类Foo,它可以查询MySQL数据库中的数据,但是

我正在使用一个简单的项目来帮助我理解并使用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)在何处实例化数据源(可以是mysql、oracle,甚至是一个普通的旧阵列),并将其发送到需要它的对象(DI是最好的模式吗?

使用DI,您将拥有一个单独的DI容器,用于处理创建控制器所需的依赖项实例。DI是一个很好的选择,因为它将消除流/控制语句(if/else、switch/case)的需要,这些语句必须确定连接到哪个DB,并有助于简化单元测试。使用DI,您告诉对象这是您将连接到的对象

2)在事物的结构中,将特定于其源的数据查询放在哪里?根据我下面的例子,我有一个类Foo,它可以查询MySQL数据库中的数据,但是如果我以后移动到Oracle数据库中,就必须用不同的语法重写它。我是否只需要创建两个版本的FooMysql和FooOracle,然后告诉其他地方使用哪个版本?

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

在您的示例中还有一些代码气味,请尝试摆脱类的更新。
此外,视图不应负责处理要传递的依赖项。
您肯定需要了解如何创建DI容器,以及如何在名称空间中组织代码。
您还需要创建一个前端控制器,您的DI容器可以加载到其中,或者DI容器可以是您的前端控制器

希望这能有所帮助。

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

使用DI,您将拥有一个单独的DI容器,用于处理创建控制器所需的依赖项实例。DI是一个很好的选择,因为它将消除流/控制语句(if/else、switch/case)的需要,这些语句必须确定连接到哪个DB,并有助于简化单元测试。使用DI,您告诉对象这是您将连接到的对象

2)在事物的结构中,将特定于其源的数据查询放在哪里?根据我下面的例子,我有一个类Foo,它可以查询MySQL数据库中的数据,但是如果我以后移动到Oracle数据库中,就必须用不同的语法重写它。我是否只需要创建两个版本的FooMysql和FooOracle,然后告诉其他地方使用哪个版本?

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

在您的示例中还有一些代码气味,请尝试摆脱类的更新。
此外,视图不应负责处理要传递的依赖项。
您肯定需要了解如何创建DI容器,以及如何在名称空间中组织代码。
您还需要创建一个前端控制器,您的DI容器可以加载到其中,或者DI容器可以是您的前端控制器


希望这能有所帮助。

1)DIC是我倾向的方向,所以我想我会继续朝这个方向发展2)我已经在连接数据库方法,我更感兴趣的是如何处理不同的查询语法。请参阅更新的问题。忽略我对#2的评论,这是答案(我已经在做了)。至于代码的味道,我从现有代码中继承了我的MVC知识,我一直觉得这些代码很糟糕,但还没有找到一个实现良好DI的完整示例。有什么见解吗?您所做的是为视图提供调用依赖项的任务。不应这样做b/c视图只负责显示数据。当调用类的任务被赋予单个DI容器类时,您将根据web请求上下文隔离传递的内容。我相信如果你搜索DI会有所帮助,我强烈推荐《在.NET中清除代码和依赖注入》(这可能是一本.NET的书,但它适用于所有领域)1)DIC是我倾向于的地方,所以我想我会继续这个方向2)我已经在连接数据库方法,我更感兴趣的是如何处理不同的查询语法。请参阅更新的问题。忽略我对#2的评论,这是答案(我已经在做了)。至于代码的味道,我从现有代码中继承了我的MVC知识,我一直觉得这些代码很糟糕,但还没有找到一个实现良好DI的完整示例。有什么见解吗?您所做的是为视图提供调用依赖项的任务。这不应该通过b/c来完成
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());
        }
    }
}