【问题标题】:PHP - The best way to load Database object from Model, but to have only one instance?PHP - 从模型加载数据库对象的最佳方式,但只有一个实例?
【发布时间】:2012-03-01 22:27:19
【问题描述】:

这是我的问题,我构建了一个很小的 ​​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"));

这就是我将一些 $data 从模型返回到控制器的方式。

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

现在,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 个数据库,我会遇到一些大问题。 如何在不使用单例模式的情况下实现这一目标?

目前,我也有这样的:

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');

}

这是注册表模式,我可以在其中保存共享对象。因此,当模型要求数据库连接时,我可以检查数据库类是否在注册表中,然后返回它,如果没有,我会启动然后返回......最令人困惑的是我需要加载配置数组......

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

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

【问题讨论】:

  • 您可以在代码开始时配置和初始化数据库对象,然后将其插入注册表并在模型中从注册表中提取它。 $db = new DB($config); Registry::set('___DB__', $db);

标签: php database model-view-controller


【解决方案1】:

你在解决这个问题上走错路了。

您应该创建一个为您完成它的结构(极其简化的版本),而不是每次手动创建一个新的“模型”然后配置它:

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 中,模型是一个层,而不是一个特定的类。模型层由具有两个主要职责的大量类/实例组成:

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

第一组中的实例通常称为Domain ObjectsBusiness Objects(有点像geeks and nerds 的情况)。他们处理验证、计算、不同的条件,但不知道信息的存储方式和位置。它不会改变您制作发票的方式,无论数据来自 SQL、远程 REST API 还是 MS Word 文档的屏幕截图。

其他组主要由Data Mappers 组成。它们从域对象中存储和检索信息。这通常是您的 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 );

}

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

一些额外的视频资料:

【讨论】:

  • 行“$instance = new {$name}($this->connection);”在你的模型工厂类中对我来说是一个致命的错误。为了解决这个问题,我不得不从 $name 变量周围删除 {}。不知道为什么,因为它似乎是正确的。
  • @billmalarky ,好吧 .. 这些代码都没有经过实际测试。很有可能出现这样的错误。有更新的帖子,与此主题相关 herehere
【解决方案2】:

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

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

loadDatabase() 应该在哪里初始化一次,然后返回简单的数据库连接实例。

【讨论】:

    【解决方案3】:

    试试这个:

    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;
        }
    }
    

    祝你好运!

    【讨论】:

      猜你喜欢
      • 2013-06-29
      • 1970-01-01
      • 1970-01-01
      • 2014-06-16
      • 2012-09-14
      • 1970-01-01
      • 2013-05-23
      • 1970-01-01
      • 2012-01-10
      相关资源
      最近更新 更多