tequila/mongodb-driver-wrapper

包装MongoDB驱动类以访问其内部结构

此包的官方仓库似乎已丢失,因此该包已被冻结。

dev-master 2017-05-01 21:31 UTC

This package is auto-updated.

Last update: 2024-08-15 04:02:01 UTC


README

此库在新的官方PHP MongoDB驱动周围提供了一个轻量级的包装。这可能对您有所帮助

  • 如果您不想使用围绕PHP MongoDB驱动的高层抽象,例如Tequila MongoDB PHP库或官方的MongoDB PHP库,而您打算使用本地驱动类,请阅读以下内容,了解使用此库可能带来的好处。
  • 如果您正在编写围绕新PHP MongoDB驱动的抽象,例如替代的高层驱动库。

如果这不符合您的需求,您可以尝试使用高级工具,例如基于此库的Tequila MongoDB PHP库

通过包装驱动类,此库允许您执行一些仅使用原生驱动时无法完成的功能。以下是一些功能。

安装

此库需要PHP 5.6或更高版本,PHP 7.0或更高版本。它可能与MongoDB 2.4+一起工作,但将仅提供MongoDB 3.0+的支持。由于此库包装了官方PHP MongoDB驱动,因此需要安装此驱动。

$ pecl install mongodb

应使用Composer安装此库

$ composer require tequila/mongodb-driver-wrapper

为何要使用此库

您的代码变得可测试

假设您正在编写使用MongoDB驱动类与MongoDB服务器通信的代码。您需要为您的代码编写测试。由于MongoDB驱动有自己的测试,因此您不需要通过向数据库发出真实查询来重复它们。您想做的,是编写针对您的代码的测试,并检查您的代码是否正确调用MongoDB\Driver\Manager方法,因为此类是PHP与MongoDB通信的唯一入口。

让我们假设您想测试以下代码

<?php 

class Database
{
    public function __construct(\MongoDB\Driver\Manager $manager, $databaseName)
    {
        // ...
    }
    
    public function createCollection($collectionName, array $options = [])
    {
        // ...
    }
}

$db = new Database(new \MongoDB\Driver\Manager(), 'myapp');
$db->createCollection('logs', ['capped' => true, 'size' => 1000000]);

要确保此代码正常工作,您实际上需要检查MongoDB\Driver\Manager::executeCommand()是否以适当的参数调用。在这种情况下,您将需要使用mocks。但是这里有一个问题 - 您无法模拟MongoDB\Driver\Manager以检查其方法executeCommand()是否被调用,因为此类是最终的。您也无法模拟MongoDB\Driver\Command和其他驱动器的原生类。因此,您有两种测试代码的方式,这取决于驱动器。

  • 创建功能测试,实际上会对MongoDB发出调用,然后在MongoDB服务器上检查结果。这不是最佳解决方案,因为您的测试将依赖于已安装和启用的MongoDB服务器。此外,您还将做额外的工作 - 测试整个链而不是仅测试您的代码。
  • 不要测试代码的行为——例如测试你的 Database 实例在向其构造函数传递正确参数时不会抛出异常,但不要测试它的 createCollection() 方法,因为这个方法实际上会执行一些操作。但这也不太好,因为你将无法确定你的代码是否真正经过测试和稳定。

这就是这个库可以提供帮助的地方:它定义了具有与驱动类几乎相同接口的类。你可以使用这些类来代替原生驱动类,模拟它们,从而使你的代码可测试。例如,Tequila\MongoDB\Manager 类封装了原生的 MongoDB\Driver\Manager 类,Tequila\MongoDB\Manager::executeQuery() 接受 Tequila\MongoDB\Query 实例而不是 MongoDB\Driver\Query。这允许你调用 Tequila\MongoDB\Query::getFilter() 方法并检查你是否正在执行预期的查询。这可以使你的代码更可测试和稳定。

你的代码变得更加灵活

这个库是以高级代码为前提编写的。目前,每个应用程序都需要有能力来分析发送到数据库服务器的请求。并且,分析工具的主要目标之一是在尽可能低的级别拦截对数据库的请求。假设你正在使用官方的 MongoDB PHP Library。要分析请求,你可以扩展 MongoDB\Collection 类,并像这样装饰对其 MongoDB\Driver\Manager 实例的调用

<?php

namespace MyApplication;

use MongoDB\Collection;

class ProfilerAwareCollection extends Collection
{
    private $profiler;
    
    public function setProfiler(Profiler $profiler)
    {
        $this->profiler = $profiler;
    }
    
    public function findOneAndUpdate($filter, $update, array $options = [])
    {
        $profilerEntry = [
             'command' => 'findOneAndUpdate',
             'filter' => $filter,
             'update' => $update,
             'options' => $options,
        ];
        
        $response = parent::findOneAndUpdate($filter, $update, $options);
        
        $profilerEntry['response'] = $response;
        $this->profiler->addEntry($profilerEntry);
        
        return $response;
    }
}

看起来不错,但问题是,与分析器条目一起保存的 $options 数组并不是实际发送到 MongoDB 服务器的选项。这是因为 FindOneAndReplace 命令将其输入选项转换为 FindAndModify 命令可以接受的格式,而 FindAndModify 命令又将其输入选项转换为 MongoDB 服务器可以接受的格式。结果——你的分析器将仅保存命令的输入选项,而你将不知道实际发送到 MongoDB 服务器的请求是什么。

当然,你可以装饰每个单独的命令或编写自己的命令。但如果你需要自己编写一切,使用这个库就没有意义了。这太困难了,并且会导致错误:如果你更改分析器,你必须修复每个命令中的使用。因此,最好的解决方案是能够在请求发送到数据库之前添加你的逻辑。通过在你的方法中接受 Tequila\MongoDB\Manager,你可以非常容易地实现这个目标:只需扩展 Tequila\MongoDB\Manager 类并装饰它的一到三个主要方法以拦截数据库请求

<?php

namespace MyApplication;

use MongoDB\Driver\ReadPreference;
use Tequila\MongoDB\Manager;
use Tequila\MongoDB\CommandInterface;

class ProfilerAwareManager extends Manager
{
    private $profiler;
        
    public function setProfiler(Profiler $profiler)
    {
        $this->profiler = $profiler;
    }
    
    public function executeCommand($databaseName, CommandInterface $command, ReadPreference $readPreference)
    {
        $server = $this->selectServer($readPreference);
        $profilerEntry = $command->getOptions($server);
        $response = parent::executeCommand($databaseName, $command, $readPreference);
        
        $profilerEntry['response'] = $response;
        $this->profiler->addEntry($profilerEntry);
        
        return $response;
    }
}

这个库是 MIT 许可的。如果你认为这个库可以针对你的需求进行改进,请创建一个问题。我们非常欢迎贡献。