PHP-从Model加载数据库对象的最佳方式,但只能有一个实例


PHP - The best way to load Database object from Model, but to have only one instance?

这是我的问题,我构建了一个很小的PHP MVC框架。现在,当我在控制器中时,我应该能够加载一个模型。我有像Users.php、Pages.php等数据库表一样命名的模型。

所有控制器都扩展BaseController,所有模型都扩展BaseModel,这样我就可以为所有控制器提供一些方法。像任何控制器一样,我可以使用loadModel方法,如下所示:

$usersModel = $this->loadModel("Users");

现在$usersModel将是表示用户数据库表的对象,我应该从中打开数据库连接,并从用户表中获取、更新和删除内容。

为了从我的模型中获得数据库连接,baseModel有方法loadDatabase(),我这样使用它:

$database = $this->loadDatabase();

这将返回PDO周围的薄层类,所以从那里我可以使用这样的东西:

$data = $database->fetchAssoc("SELECT * FROM users WHERE name = ?", array("someone"));

这就是我将一些$数据从Model返回到Controller的方式。

因此,基本上,控制器可以加载一个模型,然后调用该模型上的方法,返回一些数据或更新或删除等。

现在,控制器可以加载多个模型。每个模型都应该加载数据库,而这正是它变得复杂的地方。我只需要一个到数据库的连接。

这就是loadDatabase方法的样子:

    public function loadDatabase()
    {
        // Get database configuration  
        require_once("Application/Config/Database.php");
        // Build configuration the way Database Object expects it
        $dns      = "{$db_config['db_driver']}:host={$db_config['db_host']};dbname={$db_config['db_name']}";
        $username = $db_config['db_user'];
        $password = $db_config['db_pass'];
        $options  = $db_config['db_options'];
        // Return new Database object
        return new 'Core'Database'Database($dns, $username, $password, $options);
    }

因此,在我加载数据库对象之前,我必须加载数据库连接的配置,正如数据库对象__constructor所期望的那样。然后我安装新的数据库对象并返回它。

现在我被卡住了,我不知道这是从模型加载数据库的正确方法吗?我应该如何设置,我应该如何从模型内部加载数据库,这样数据库对象总是只有一个实例。因为如果我从控制器做这样的事情

$users = $this->loadModel("Users");
$pages = $this->loadModel("Pages");
$users->doSomethingThatNeedsDatabase();
$users->doSomethingThatNeedsDatabase();
$pages->doSomethingThatNeedsDatabase();

我将创建3个数据库对象:(

所以我的问题是,我应该如何从模型内部加载数据库,以及该方法在BaseModel中应该如何?如果我想使用2个数据库,这个设置会有一些大问题。在不使用singleton模式的情况下如何实现这一点?

目前,我还有这样的东西:

public function getDatabase()
{
    $reg = 'Core'Registry'Registry::getInstance();
    $db = $reg->getObject("database");
    if(!isset($db))
    {
        require_once("Application/Config/Database.php");
        $dns      = "{$db_config['db_driver']}:host={$db_config['db_host']};dbname={$db_config['db_name']}";
        $username = $db_config['db_user'];
        $password = $db_config['db_pass'];
        $options  = $db_config['db_options'];                
        $db = new 'Core'Database'Database($dns, $username, $password, $options); 
        $reg->setObject('database', $db);
    }
    return $reg->getObject('database');
}

这是Registry模式,我可以在其中保存共享对象。所以当Model请求DB连接时,我可以检查DB类是否在注册表中,并返回它,如果没有,我会实例化,然后返回。。。最令人困惑的是,我需要加载配置数组。。。

那么,从模型加载数据库的最佳方式是什么呢?

谢谢你的阅读,这是一个很长的问题,但这让我很困扰,我希望有人能帮我解决这个问题。。。谢谢

解决这个问题的方法不对。

与其每次手动创建一个新的"模型"然后进行配置,不如创建一个为您做这件事的结构(极其简化的版本):

class ModelFactory
{
    protected $connection = null;
    // --- snip --
    public function __construct( $connection )
    {
        $this->connection = $connection;
    }
    // --- snip --
    public function buildMapper( $name )
    {
        $instance = new {$name}( $this->connection );
        return $instance;
    }
    // --- snip --
}

这个类将在index.php或bootstrap.php中使用,或者任何其他用作应用程序入口点的文件:

// at the bootstrap level
$modelFactory = new ModelFactory( new PDO(...) );
// i am assuming that you could get $controllerName 
// and $action from routing mechanism
$controller = new {$controllerName}( $modelFactory );
$controller->{$action}();

你遇到的主要问题实际上是误解了Model是什么。在一个合适的MVC中,Model是一个层,而不是一个特定的类。模型层由多个类/实例组成,具有两个主要职责:

  • 域业务逻辑
  • 数据访问和存储

第一组中的实例通常被称为域对象或业务对象(有点像极客和书呆子的情况)。他们处理验证、计算和不同的条件,但不知道信息是如何存储的以及存储在哪里的。无论数据来自SQL、远程REST API还是MS Word文档的屏幕截图,它都不会改变您制作发票的方式。

另一组主要由数据映射器组成。它们存储和检索域对象中的信息。这通常是您的SQL所在的位置。但是映射器并不总是直接映射到DB架构。在经典的多对多结构中,可能有1个或2个或3个映射器为存储提供服务。映射器通常每个域对象一个。。但即便如此,这也不是强制性的。

在控制器中,它看起来都是这样的。

public function actionFooBar()
{
    $mapper = $this->modelFactory->buildMapper( 'Book' );
    $book = $this->modelFactory->buildObject( 'Book' );
    $patron = $this->modelFactory->buildObject( 'Patron' );
    $book->setTitle('Neuromancer');
    $mapper->fetch( $book );
    $patron->setId( $_GET['uid']);
    if ( $book->isAvailable() )
    {
        $book->lendTo( $user );
    }
    $mapper->store( $book );
}

也许这会给你一些方向的指示。

一些额外的视频材料:

  • 高级OO模式(幻灯片)
  • 全球状态与辛格尔顿
  • 不要找东西

这些模型使用依赖注入模式的最佳方式。

public function loadModel() {
    $db = $this->loadDatabase();
    $model = new Model();
    $model->setDatabase($db);
    return $model;
}

其中loadDatabase()应该在初始化后返回数据库连接的简单实例。

试试这个:

class Registry
{
    /** @return Registry */
    public static function getInstance() {}
    public function getObject($key) {}
    public function setObject($key, $value) {}
    /** @return bool */
    public function isObjectExists($key) {}
}
class Db
{
    const DB_ONE = 'db_one';
    const DB_TWO = 'db_two';
    protected static $_config = array(
        self::DB_ONE => array(),
        self::DB_TWO => array(),
    );
    public static function getConnection($dbName) {}
}

abstract class BaseModel
{
    abstract protected function _getDbName();
    protected function _getConnection()
    {
        $dbName = $this->_getDbName();
        if (!Registry::getInstance()->isObjectExists($dbName)) {
            Registry::getInstance()->setObject($dbName, Db::getConnection($dbName));
        }
        return Registry::getInstance()->getObject($dbName);
    }
}
class UserModel extends BaseModel
{
    protected function _getDbName()
    {
        return Db::DB_ONE;
    }
}
class PostModel extends BaseModel
{
    protected function _getDbName()
    {
        return Db::DB_TWO;
    }
}

祝你好运!

相关文章: