【问题标题】:Instantiating ActiveRecord models without a database connection在没有数据库连接的情况下实例化 ActiveRecord 模型
【发布时间】:2017-12-10 15:43:43
【问题描述】:

我目前正在用php-activerecord 替换自制的hack-y MVC 方法的模型组件。我有一个单独的页面,在数据库服务器出现故障时显示,该页面用于显示带有访客头像、姓名和角色的用户徽章。代码如下:

if (Auth::$signed_in)
    echo Auth::$user->getAvatarWrap();
else echo (new \App\Models\User([
    'name' => 'Guest',
    'role' => 'guest',
    'avatar_url' => GUEST_AVATAR
]))->getAvatarWrap();

Auth::$signed_in 在用户登录时设置为true,这在数据库中断的情况下是不可能的,因此else 分支使用预定义的数据执行。

Pre-ActiveRecord 这将简单地将属性添加到对象,并且对getAvatarWrap 方法的调用将毫无问题地执行。现在,随着模型由 ActiveRecord 控制,无论出于何种原因,都会发生一组额外的调用。这可能是因为模型定义了关系,但我不确定。

ActiveRecord\DatabaseException: PDOException: SQLSTATE[08006] [7] could not connect to server: Connection refused
    Is the server running on host "localhost" (127.0.0.1) and accepting
    TCP/IP connections on port 5432? in /var/www/vendor/php-activerecord/php-activerecord/lib/Connection.php:260
Stack trace:
#0 /var/www/vendor/php-activerecord/php-activerecord/lib/Connection.php(260): PDO->__construct('pgsql:host=loca...', '<username>', '<password>', Array)
#1 /var/www/vendor/php-activerecord/php-activerecord/lib/Connection.php(122): ActiveRecord\Connection->__construct(Object(stdClass))
#2 /var/www/vendor/php-activerecord/php-activerecord/lib/ConnectionManager.php(33): ActiveRecord\Connection::instance('pgsql://databas...')
#3 /var/www/vendor/php-activerecord/php-activerecord/lib/Table.php(114): ActiveRecord\ConnectionManager::get_connection('pgsql')
#4 /var/www/vendor/php-activerecord/php-activerecord/lib/Table.php(90): ActiveRecord\Table->reestablish_connection(false)
#5 /var/www/vendor/php-activerecord/php-activerecord/lib/Table.php(71): ActiveRecord\Table->__construct('App\\Models\\User')
#6 /var/www/vendor/php-activerecord/php-activerecord/lib/Model.php(765): ActiveRecord\Table::load('App\\Models\\User')
#7 /var/www/vendor/php-activerecord/php-activerecord/lib/Model.php(271): ActiveRecord\Model::table()
#8 /var/www/includes/views/_sidebar.php(20): ActiveRecord\Model->__construct(Array)
#9 /var/www/includes/views/_layout.php(148): include('/var/www...')
#10 /var/www/includes/views/fatalerr.php(46): require('/var/www...')
#11 /var/www/includes/init.php(32): require('/var/www...')
#12 /var/www/includes/do.php(3): require('/var/www...')
#13 /var/www/public/index.php(1): require('/var/www...')
#14 {main} in /var/www/vendor/php-activerecord/php-activerecord/lib/Connection.php on line 262

当我知道 ActiveRecord 找不到连接并且它应该对我提供的数据感到满意时,如何告诉 ActiveRecord 停止寻找连接?在停电期间,我是否必须告别使用我的任何模型?

【问题讨论】:

  • 能否修改配置以使用 sqlite:memory 连接?
  • 我可以完全控制代码和运行它的服务器。
  • 那么这可能是一个解决方案。每个查询都会失败,但无论如何都会发生。

标签: php oop model-view-controller model phpactiverecord


【解决方案1】:

感谢a comment 我发现了 SQLite 内存数据库,这让我走上了成功之路。鉴于启用对此类数据库的支持的 pull request 已经尘封了近一年,我决定自己动手。
如果您稍后阅读本文,请务必阅读检查所述 PR 是否已合并,如果已合并,请发表评论,以便我可以更新此答案。

首先,我在 GitHub 上创建了 original repository 存储库。我故意不链接我的叉子,因为它最终可能会过时或被删除,所以我建议你自己做一个叉子。然后,我使用this answerthis answer 的组合将魔法分支从 PR 提交者的分支中获取到我的分支中。您可以通过按分叉存储库主页上的“克隆或下载”按钮获取&lt;url&gt;

$ git clone <url>
$ git remote add target https://github.com/claytonrcarter/php-activerecord.git
$ git fetch --all
$ git checkout master
$ git merge --squash target/sqlite-memory-support
$ git commit -m "Add support for SQLite :memory: databases"
$ git push

现在我的 fork 已经对我的项目的 composer.json 进行了最新更改,并将我的 require 块中的 php-activerecord/php-activerecord 的版本更改为 "dev-master",然后添加了一个带有我的 fork 的 repositories 块.同样,将 &lt;url&gt; 替换为 fork 的克隆 URL。

{
    // ...
    "require": {
        // ...
        "php-activerecord/php-activerecord": "dev-master",
        // ...
    },
    "repositories": [
        {
            "type": "vcs",
            "url":  "<url>"
        }
    ]
}

但是等等,乐趣还不止于此!现在我们已经支持内存 SQLite DB 适配器,我们实际上必须定义它。所以修改你的初始化配置:

ActiveRecord\Config::initialize(function ($cfg){
    $cfg->set_connections([
        'pgsql' => 'pgsql://'.DB_USER.':'.DB_PASS.'@'.DB_HOST.'/database?charset=utf8',

        // Add this line below. 'failsafe' can be changed to something else, of course
        'failsafe' => 'sqlite://:memory:',
    ], 'pgsql');
});

原来是 pretty difficult 来热交换默认连接,所以我做了下一个最好的事情,并创建了一个子类,将其 $connection 设置为之前定义的 failsafe。请注意,必须手动定义必填字段,因为 lib 无法知道允许您从空数据库中使用哪些字段。有趣的是,主类上的$has_many 和类似关系对子类的行为没有负面影响,所以我可以保持相对较短。

namespace App\Models;

/** @inheritdoc */
class FailsafeUser extends User {
    static $connection = 'failsafe';

    public $name, $role, $avatar_url;
}

最后,我不得不更改调用以使用新定义的类:

if (Auth::$signed_in)
    echo Auth::$user->getAvatarWrap();
else echo (new \App\Models\FailsafeUser([
    'name' => 'Guest',
    'role' => 'guest',
    'avatar_url' => GUEST_AVATAR
]))->getAvatarWrap();

瞧,脚本不再中断,我终于可以继续我的代码库的 ActiveRecord 化了。如果您想自己经历这种折磨并且有多个模型想要“离线”变体,那么您可能想要创建某种抽象类,它会自动设置您在构造函数中传递的属性通过预先设置$connection,使子实例变得稍微不那么冗长。

【讨论】:

    猜你喜欢
    • 2015-03-18
    • 1970-01-01
    • 1970-01-01
    • 2012-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-23
    • 1970-01-01
    相关资源
    最近更新 更多