我有一个理论问题,我希望有人可以帮我解决。
我目前正在使用MVC设计模式在PHP中编写一个简单的Web应用程序。我已经看过并阅读了一些关于这个主题的教程,但它们通常要么过于复杂或过于简单。
所以我目前所拥有的是一个简单的User
模型:
class User {
private $username;
private $group; //user, admin, etc
// getters
}
我还有一个简单的Database
类来实现这个类:
interface DatabaseInterface {
public function connect();
public function disconnect();
public function prepare($sql = null);
public function execute($params = array());
public function rowCount();
public function fetch();
}
我的问题是,如何将此Database
课程与填充User
课程相关联?
我目前所拥有的是另一个名为UserDAO
的类,它在其构造函数中传递了Database
类的引用,它有一个名为ValidateUser()
的函数,然后使用上面的接口方法来检查用户对数据库。
class UserDAO {
private $database;
public function __construct($database) {
$this->database = $database;
}
public function validateUser($username, $password) {
$this->database->prepare('SELECT * FROM users WHERE...');
....
return true/false;
}
}
现在我通过构造函数将UserDAO
对象传递到User
类,并在ValidateUser()
类中添加另一个User
方法,基本上只调用ValidateUser()
类中的UserDAO
方法。
新的User
类:
class User {
private $username;
private $group; //user, admin, etc
private $userDAO;
public function __construct($userDAO) {
$this->userDAO = $userDAO;
}
public function validateUser($username, $password) {
if($this->userDAO->validateUser($username, $password)) {
// set stuff that i need
return true;
}
return false;
}
// getters
}
关于这一点对我来说感觉不对。有人能帮我理解这个过程通常流动的方式吗?
此外,Database
类通常是否保持静态,所以我可以使用Database::instance()
之类的东西来呼叫连接?现在我正在PHP页面的开头创建一个数据库对象并传递它。
如果有任何不清楚的地方,请随时给我发表评论,我会尽快修复它。
感谢您对帖子的长度感到满意。
答案 0 :(得分:3)
这取决于您的解决方案应该是最新的。
“最旧的”版本是在任何地方都有一个静态连接。您通过使用 singleton :
来实现这一点class database {
private $instance = NULL;
private function __construct()
public function getDb() {
return $this->instance?: $this->instance = new database();
}
}
第二个更新的解决方案是使用注册表: 您可以拥有一个由所有其他类扩展的基类,并为
之类的调用提供方法$this->getRegistry()->getDb
通常,注册表在脚本的开头填充,例如在引导程序中。 优点是您可以更好地对组件进行单元测试,因为没有静态部件,您可以为开发和生产环境定义不同的注册表...
依赖注入中的第三个“最新”和最佳方法。 通常你会有一些框架为你做这件事。我们的想法是,无论何时调用类的构造函数,所有依赖项(如数据库连接)都将由框架注入。
通过这种方式,您可以轻松定义“用户需要DB和Redis。商店需要DB,配置和FTP”...... 虽然这对于较小的项目来说是一个开销,但它对于大量且经过充分测试的项目有很大的帮助。
如果您曾经升级到多个数据库(例如一个主服务器和多个服务器),DI非常适合,因为您可以为代码的每个部分提供完成工作所需的数据库,无需更改任何一行代码。
答案 1 :(得分:1)
将数据库连接传递到UserDOA对象时,您正在创建依赖项。您的UserDOA对象现在依赖于数据库实例。
毫无疑问,管理这些依赖关系的最佳方式(不仅仅是使用您的UserDOA,而是使用其他DOA课程等)是通过 IoC (Inversion of Control)容器使用依赖注入强>
依赖注入允许您在容器中注册组件/类(以及创建类的对象实例所需的参数),然后将(在您的情况下)数据库实例注入到您指定的任何类中。这使代码更易于管理,可扩展,松散耦合并遵循SOLID principles。
我建议看一个依赖注入容器是Symfony's dependency injection component,它已被提取并可单独使用。
答案 2 :(得分:0)
为了记录,对于一个商业项目来说,做一个贫血模型实际上是唯一的出路。
因此,我们不是创建具有多个成员/运算符/函数的膨胀模型(或域对象),而是只有一个模型,只有字段的定义,我们将服务分开。
a)模型类应该很简单,如果项目模型发生变化(例如,如果我们在数据库的表中添加一个新字段),它应该会发生变化。通常模型是实体(表的镜像)
b)dao类(其中一个服务)执行crud。作为一项服务,它可能是静态的(如果不是,它应该是),所以调用它应该是最简单的:
public function validateUser($username, $password) {
if(UserDAO::validateUser($username, $password)) {
// set stuff that i need
return true;
}
return false;
}
但是,对于这种特定情况,函数validateUser与LOGIC(如果业务条件发生变化可能会更改的类)更相关。所以我们可以创建一个新的服务类来进行验证
class UserLogic {
public static validateUser($user) {
$userInDb=UserDao::getUser($user->idUser);
// here goes the logic
// why?, because it could changes, for example, validating the date, attempts, if its active...
if ($userInDb!=null && $userInDb->password=$user->password) {
return true;
}
return false;
}
}
为了调用这个函数,我们使用了:
$isValid=UserLogic::validateUser($someUser);
答案 3 :(得分:-1)
查看单身人士,例如$ this-> db = DB :: getInstance();
示例:
class Database {
// Store the single instance of Database
private static $instance;
private function __construct() {
// connection here
}
public static function getInstance() {
if (!self::$instance) {
self::$instance = new Database();
}
return self::$instance;
}
}
$db = new Database(); // the old way, don't do this.
$db = Database::getInstance(); // the singleton way - do this everywhere! it will always return the same object