如何使用依赖注入构建自己的MVC PHP应用程序?

ffx8fchx  于 2023-05-05  发布在  PHP
关注(0)|答案(1)|浏览(133)

对不起,如果帖子的标题不清楚,我会尽量简单,因为我真的认为这个问题有一个简单的答案。
首先,我将添加一些代码(不是真实的的代码,只是为了测试):
型号:

class User{
    public $id;
    public $name;
    public $age;
}

接口:

interface IData{
   public function search(User $user)

}

类存储库:

class DataRepository{
    protected $db;
    public $conn;
    public $data;

    // Passing the interface DB to the constructor
    public function __construct(DB $db)
    {
        $this->db = $db;
    }

    public function search(User $user){
        $query = " SELECT * FROM test_db WHERE id_user = ". $user->id . " and name = '%" . $user->name . "%'";
        $this->conn = $this->db->connection();
        $this->data = $this->conn->query($query);
        $dataList = [];

        foreach ($this->data as $row)
        {
            $dataList [] = (object)array(
                "name" => $row["name"],
                "age" => $row["age"],
            );
        }

        return $dataList;
    }

}

控制器:

class UserController{
    public $Idata;

    // Passing the interface IData to the constructor
    function __construct(IData $Idata) {
        $this->Idata = $Idata;
    }

    // Function to search the user and return an array with the data
    function printData(){
        $Mdata = new User();
        $Mdata->id = 5;
        $Mdata->name = "Carl";
        $Mdata->age = 39;

        $result = $this->Idata->search($Mdata);

        return $result;
    }
}

浏览次数:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Test Page</title>
</head>
<body>

<?php

// We create an instance of UserController

$UserData = new UserController();

// Print the data
echo "<pre>";
    print_r($UserData);
echo "</pre>"

?>
    
</body>
</html>

所以我试图学习使用MVC的存储库模式,将DB层与控制器分离并封装,一切看起来都很棒,直到我需要在主控制器(UserController)上使用IData接口时,我认为我出现了错误
$UserData = new UserController() -> returns an error: Expected 1 arguments. Found 0 .
我知道这个错误的含义,它需要一个参数(接口),但是在视图端示例化接口感觉很奇怪,因为稍后会将其作为参数传递给构造器。
我试着:

<?php

$IData = new IData();

$UserData = new UserController($IData);

echo "<pre>";
    print_r($UserData);
echo "</pre>"

?>

它返回以下错误:Fatal error: Uncaught Error: Cannot instantiate interface IData
我的主要问题是,有没有办法只在UserController的构造函数上使用接口,而不必在视图上定义它?
如果不可能(肯定会有),我该怎么办?(我不能使用关键字implemante,因为我试图使用存储库模式,从构造函数进行依赖注入)

up9lanfz

up9lanfz1#

这更多的是一个如何在PHP中完成依赖注入的问题。

前置条件

首先,您应该通过声明DataRepository实现接口来修复代码:

class DataRepository implements IData
{
    // ...
}

这是没有办法的事。PHP需要显式声明它。

先想想...

然后考虑以下几点:

  1. UserController依赖于IData
  2. DataRepository应该实现IData
    1.您的代码应该知道,当示例化UserController时,它需要一个IData实现。但可以不止一个。
    1.您的代码如何知道哪个IData用于UserController
    对于(4),你需要一种方法来声明:
  • 所有需要IData的类都应该有DataRepository;或
    *一些需要IData的类,比如UserController,应该有DataRepository

依赖注入容器

一个简单的方法是使用依赖注入容器,如PHP-DI。它们可以灵活地声明如何实现接口依赖关系的方法。
假设您已经安装了composer的“php-di/php-di”,那么您应该让这段代码在某种公共的php /内核中运行。

require_once __DIR__ . '/vendor/autoload.php';

$db = getDB(); // some way to create the database object
$container = new DI\Container();
$container->set(IData::class, \DI\create(DataRepository::class));
$container->set(DB::class, \DI\value($db));

// ...

如果你将$container变量传递给你的视图文件,你可以简单地这样做:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Test Page</title>
</head>
<body>
<?php

// We create an instance of UserController
$UserData = $container->get(UserController::class);

// Print the data
echo "<pre>";
    print_r($UserData);
echo "</pre>"

?>
</body>
</html>

请注意,您不需要手动创建UserController的依赖项。当请求UserController时,这里的容器将搜索所有配方,以满足UserController所需的所有依赖项。因此,视图不需要知道控制器的实现细节。

更多内容

正如我之前提到的,您可能只需要将DataRepository作为IData用于一些控制器。PHP-DI和其他依赖注入容器可以让您指定创建DataRepository的方法。
对于PHP-DI,您应该阅读相关文档,或了解其他更高级的用法。

PHP常用MVC框架

我想一个更常见的MVC设计可能会有视图的控制器调用。并将控制器示例化(使用容器)作为实现细节:
工艺路线:

// Pseudo-code only
// But all MVC framework has its own router / routing code like this

// some container initialization
$container = dummyGetContainer();

// router will probably need container
$router = new MyRouter($container);

// register the handler of GET request to "/userData" 
$router->get('/userData', 'UserController::viewData');

// actually execute the routing
// internally will do:
// 1. find the handler of the request method and path
// 2. instantiate the controller with dependencies.
// 3. execute the handler against the request.
$router->route(dummyGetRequestFromEnvironment());

控制器:

class UserController{
    public $Idata;

    // Passing the interface IData to the constructor
    function __construct(IData $Idata) {
        $this->Idata = $Idata;
    }

    function viewData($request) {
        $userData = $this->getData();
        include __DIR__ . '/../views/userDataView.php';
    }

    // Function to search the user and return an array with the data
    function getData(){
        $Mdata = new User();
        $Mdata->id = 5;
        $Mdata->name = "Carl";
        $Mdata->age = 39;

        $result = $this->Idata->search($Mdata);

        return $result;
    }
}

浏览次数:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Test Page</title>
</head>
<body>
<pre>
<?php print_r($userData); ?>
</pre>

这种设计甚至更好,因为视图不应该被要求知道任何关于控制器的信息。它应该简单地表示它所提供的数据。

相关问题