PHP、MVC、DI(C)、多数据库
我正在使用一个简单的项目来帮助我理解并使用DI实现MVC,为单元测试做准备。网站将根据请求的页面显示不同的数据表。困难在于数据可能来自不同的数据源(例如mysql或oracle),因此我尝试实现DI以减少在两者之间切换所需的代码。我的问题是: 1) 在何处实例化数据源(可以是mysql、oracle,甚至是一个普通的旧数组),并将其发送到需要它的对象(DI是最好的模式吗) 2) 在事物结构的何处放置特定于其源的数据查询?根据我下面的例子,我有一个类Foo,它可以查询MySQL数据库中的数据,但是如果我以后移动到Oracle数据库中,就必须用不同的语法重写它。我是否只需要创建两个版本FooMysql和FooOracle,然后告诉其他地方使用哪个版本 下面是一些基本的、不完整的代码,演示了我所看到的概念。希望你能理解我将要做的事情,并为我指明正确的方向。我不是在寻找一个已经构建好的框架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数据库中的数据,但是
$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());
}
}
}