grefon/mongo-model

v1.0.1 2022-10-07 20:40 UTC

This package is auto-updated.

Last update: 2024-09-08 00:54:59 UTC


README

GitHub Workflow Status GitHub release (latest by date) Packagist PHP Version GitHub

俄语

MongoModel 允许您方便地像处理对象一样处理 MongoDB 文档。

实现

  • 属性类型化
  • 必填属性
  • 默认值
  • 变更历史
  • 对象操作

MongoModel 与 MongoDB PHP 库 一起工作

入门

Composer

通过 Composer 安装此包。编辑 composer.json 中的 require

{
    "require": {
        "grefon/mongo-model": "*"
    }
}

并运行 composer update

或者

在您的命令行中运行此命令

composer require grefon/mongo-model

初始化连接

// Loading composer
require __DIR__ . '/vendor/autoload.php';

use MongoDB\Client;
use MongoModel\MongoDB;

// Initialization of connection to MongoDB
/**
 * @param Client $client - MongoDB Client
 * @param string $base   - name of base
 * @param bool   $debug  - debug
 */
MongoDB::init(new Client('mongodb://127.0.0.1/'), 'baseName', true);

启用调试的 MongoDB 类会将请求及其状态记录到 MongoDB::$trace

您的第一个模型

在 MongoDB 中创建名为 users 的集合。

创建新的 PHP 类 User 继承 ModelMongoDB

use MongoModel\ModelMongoDB;

/**
 * User Class
 *
 * @property string  userId
 * @property string  surname
 * @property string  name
 * @property string  email
 * @property int     rating
 * @property boolean ban
 * @property string  timeCreate
 * @property string  timeUpdate
 *
 */
class User extends ModelMongoDB
{

    static protected $collection;
    static public    $collectionName = 'users';
    static public    $primaryKey     = 'userId';

    static public    $fieldsModel    = [
        'userId'      => ['string'],
        'surname'     => ['string'],
        'name'        => ['string', 'required'],
        'email'       => ['string'],
        'rating'      => ['int', 'default' => 0],
        'ban'         => ['boolean', 'default' => false],
        'timeCreate'  => ['datetime', 'timeCreate'],
        'timeUpdate'  => ['datetime', 'timeUpdate']
    ];

}

每个模型类都必须继承 ModelMongoDB。它必须包含 4 个主要静态变量

  • $collection - 此变量将存储 \MongoDB\Collection 实例
  • $collectionName - MongoDB 中的集合名称
  • $primaryKey - primary key 名称(代替 _id
  • $fieldsModel - 模型字段(对象属性)数组

$fieldsModel

在 $fieldsModel 中,数据模型被描述为一个关联数组。键是对象的属性名,值是属性属性的数组。

属性数组中的第一个值必须是数据类型。

当从 MongoDB 加载数据或保存时,对象属性将进行类型化。

数组中的所有后续属性都是自由的,但其中一些是保留的

创建新对象

示例: examples/new.php

$user = new User(['name' => 'Will', 'surname' => 'Smith']);
$user->save();

或者

$user = new User;
$user->name = 'Will';
$user->surname = 'Smith';
$user->save();

或者

$user = new User;
$user->save(['name' => 'Will', 'surname' => 'Smith']);

或者

$user = User::new(['name' => 'Will', 'surname' => 'Smith'], false);

这些示例中的每个都会在 MongoDB 中创建一个新的文档

{
    "_id" : ObjectId("63399434089c8c26344ff2df"),
    "surname" : "Smith",
    "name" : "Will",
    "email" : null,
    "rating" : NumberInt(0),
    "ban" : false,
    "timeCreate" : ISODate("2022-10-02T13:37:56.000+0000"),
    "timeUpdate" : ISODate("2022-10-02T13:37:56.000+0000")
}

也可以创建具有您自己的 ID 的文档

$userID = User::new(['userId' => 'user123', 'name' => 'Bob', 'rating' => 15]);
{
    "_id" : "user123",
    "surname" : null,
    "name" : "Bob",
    "email" : null,
    "rating" : NumberInt(15),
    "ban" : false,
    "timeCreate" : ISODate("2022-10-02T13:38:51.000+0000"),
    "timeUpdate" : ISODate("2022-10-02T13:38:51.000+0000")
}

如果 ID 长度为 24 个字符,则尝试将字符串转换为 ObjectId

在静态方法 User::new(array $data, bool $returnID = true) 中,数据数组作为第一个参数传递,第二个参数 - 是否只返回创建的文档的 ID 或整个对象实例; $returnID = false - 返回整个对象。

对象操作

加载

YourModelClass::get($data)

示例: examples/get.php

按 ID

$user = User::get('63399434089c8c26344ff2df');

按属性

$user = User::get(['name' => 'Bob', 'rating' => ['$gte' => 10]]);

通过属性加载时,会在 $fieldsModel 中检查必需字段 namerating 的存在。如果键中有点,则可以按内部属性进行搜索。

$user = User::get(['phones.number' => 123456789);

如果键以 $ 开头,也可以搜索 MongoDB 语法。

// The first found document with a rating less than zero or banned will be returned
$user = User::get(['$or' => [['rating' => ['$lt' => 0]], ['ban' => true]]]);

如果文档在 MongoDB 中找不到,则 User::get 将返回 null

save 方法

save(array $data = null)

示例: examples/save.php

该方法将当前对象实例保存到 MongoDB 文档中。只有更改过的属性才会发送到数据库。

if ($user = User::get('63399434089c8c26344ff2df')) {

    $user->rating = '20'; // string will be converted to int
    $user->save(['email' => 'test@test.com']);
    
}
{
    "_id" : ObjectId("63399434089c8c26344ff2df"),
    "surname" : "Smith",
    "name" : "Will",
    "email" : "test@test.com",
    "rating" : NumberInt(20),
    "ban" : false,
    "timeCreate" : ISODate("2022-10-02T13:37:56.000+0000"),
    "timeUpdate" : ISODate("2022-10-02T14:12:02.000+0000")
}

如果对象已更新且不是首次创建,则具有 onlyCreation 属性的属性将被忽略,而具有 history 属性的属性将检查更改并写入历史记录。

saveField 方法

saveField(string $field, $value = '__EMPTY__')

示例: examples/save.php

仅将当前对象的单个属性存储在 MongoDB 中。

轻量级保存方法。不创建更改历史。自动更新时间属性。

$user->email = 'test@test.com';
$user->rating = 50;
$user->saveField('rating');

或者

$user->email = 'test@test.com';
$user->saveField('rating', 50);

即使您已更改 email,其新值也不会写入 MongoDB。只有 rating 值将保存到数据库。

saveFields 方法

saveFields($fields = null)

示例: examples/save.php

仅将当前对象的特定属性保存到 MongoDB。

轻量级保存方法。不创建更改历史。自动更新时间属性。

$user->surname = 'TEST';
$user->email = 'test@test.com';
$user->rating = 50;

// Saves only rating
$user->saveFields('rating');

// Saves only rating and email
$user->saveFields(['rating', 'email']);

在您的 User 类的自身方法中,您可以向受保护的 changedFields 数组中追加。

class User extends ModelMongoDB
{

    // ..............................

    function changeEmail(string $email) {
        
        $this->email = $email;
        $this->changedFields[] = 'email';
        return $this;
    
    }
    
    function resetRating() {
        
        $this->rating = 0;
        $this->changedFields[] = 'rating';
        return $this;
    
    }

}
if ($user = User::get('63399434089c8c26344ff2df')) {

    if (!empty($_POST['email'])) {
    
        // Change email
        $user->changeEmail($_POST['email']);
    
    }
    
    // Reset the rating and save the changed object properties in MongoDB
    $user->resetRating()
         ->saveFields();

}

delete 方法

delete()

示例: examples/delete.php

从 MongoDB 中删除文档。

if ($user = User::get('63399434089c8c26344ff2df')) {

    $user->delete();

}

getArray 方法

getArray($includeField = null, bool $skipHidden = true)

示例: examples/getArray.php

if ($user = User::get('63399434089c8c26344ff2df')) {

    print_r($user->getArray());
    
}
[
    'userId'      => '63399434089c8c26344ff2df',
    'surname'     => 'Smith',
    'name'        => 'Will',
    'email'       => null,
    'rating'      => 0,
    'ban'         => false,
    'timeCreate'  => '2022-10-02 13:37:56',
    'timeUpdate'  => '2022-10-02 13:37:56'
]

默认情况下,getArray 方法不返回在 $fieldsModel 中具有 hidden 的属性。您可以通过传递 $skipHidden = false 来禁用此功能。

$includeField

您可以传递单个属性或属性数组,这些属性在 $fieldsModel 中指定。例如

// Will return only properties that have required in their attributes
$user->getArray('required');

// Will return only properties that have card and short in their attributes
$user->getArray(['card', 'short']);

带缓存加载

YourModelClass::getFromCache($data)

示例: examples/getFromCache.php

当使用缓存加载时,对象实例存储在内存中,在重新加载时,不会调用 MongoDB。与 get() 方法一样,您可以按 ID 或属性进行加载。

当在代码的不同部分处理对象时很有用。

$userId = '63399434089c8c26344ff2df';
$email = $_POST['email'] ?? null;
$name = $_POST['name'] ?? null;
// ...................................

// In the first section of code
if ($email) {

    $user = User::getFromCache($userId);
    $user->email = $email;

}

// ...................................

// In the second section of code
if ($name) {

    $user = User::getFromCache($userId);
    $user->name = $name;

}

// ...................................
if ($email or $name) {

    User::getFromCache($userId)->save();

}

对象操作

itemsGet 方法

YourModelClass::itemsGet($data = null, $fields = null, $orderBy = null, $limited = null, array $settings = [])

示例: examples/itemsGet.php

在 MongoDB 中查找文档,并返回对象实例数组或它们的定义属性。

搜索数据

$data

  • null - 在所有文档中无条件搜索
  • stringint - 按 ID 搜索
  • array - ID 数组或属性关联数组
// Returns an array of all documents
User::itemsGet(); 

// Returns an array with documents that have _id = 26
User::itemsGet(26); 

// Returns an array with documents that have _id = user_12
User::itemsGet('user_12'); 

// Returns an array with documents that have _id = ObjectId("63399434089c8c26344ff2df")
User::itemsGet('63399434089c8c26344ff2df'); 

// Returns an array with documents that have _id = user_12 or ObjectId("63399434089c8c26344ff2df")
User::itemsGet(['user_12', '63399434089c8c26344ff2df']); 

// Returns an array with documents that have name = Will and surname = Smith
User::itemsGet(['name' => 'Will', 'surname' => 'Smith']);

搜索方式与 get() 方法相同,但结果总是一个包含所有找到的文档的数组。

返回字段

$fields

如果 $fields = null,则 itemsGet 将返回对象实例数组。您可以对每个实例执行与加载后处理相同的行为。

foreach (User::itemsGet(['rating' => ['$lt' => 10]], null) as $user) {

    $user->rating += 5;
    $user->saveField('rating');

}

如果将属性传递给 $fields 作为字符串,则 itemsGet 的结果将返回一个包含指定属性的关联数组。

foreach (User::itemsGet(['name' => 'Will'], 'rating') as $userId => $rating) {

}

如果您将属性数组传递给 $fields,那么将返回一个关联数组给 itemsGet 结果,其中键将是 _id,值是包含指定字段的 stdClass

foreach (User::itemsGet(['name' => 'Will'], ['name', 'surname', 'rating']) as $userId => $item) {

    echo $item->name . ' ' . $item->surname . ' has a rating ' . $item->rating;
    // Will Smith has a rating 10
    // Will Duk has a rating 7
    // ..........

}

排序

$orderBy

指定搜索时按哪个字段排序。

// Ascending
User::itemsGet(['name' => 'Will'], null, 'rating');

// Descending
User::itemsGet(['name' => 'Will'], null, ['rating', 'DESC']);

分页

$limited

指定要搜索的文档数量和缩进(跳过和限制)。

// Returns the first 10 documents found
User::itemsGet(null, null, null, 10);

// Returns the found document from 21 to 30
User::itemsGet(null, null, null, [20, 10]);

请求设置

$settings

这是在 MongoDB 中进行搜索的设置。例如,您可以建议一个索引

User::itemsGet(['name' => 'Will'], null, 'rating', 10, ['hint' => 'index_name_rating']);

在设置中,您还可以传递更复杂的条件,以便根据多个字段进行排序。

itemsHas 方法

YourModelClass::itemsHas($data = null)

示例: examples/itemsHas.php

根据给定条件检查集合中是否存在文档。

$data 传递了 搜索数据,如同在 itemsGet 方法中。

返回 truefalse

if (User::itemsHas(['email' => 'mail@test.com'])) {
    
    die('Email busy');

}

itemsCount 方法

YourModelClass::itemsCount($data = null, array $settings = [])

示例: examples/itemsCount.php

返回符合查询条件的文档数量。

$data 传递了 搜索数据,如同在 itemsGet 方法中。

$settings - 请求设置

// How many documents have a 'rating' greater than 100
echo User::itemsCount(['rating' => ['$gt' => 100]]);

// We suggest the index and count up to a maximum of 20
echo User::itemsCount(['rating' => ['$gt' => 100]], ['hint' => 'my_index', 'limit' => 20]);

itemsDelete 方法

YourModelClass::itemsDelete($data = null)

示例: examples/itemsDelete.php

从集合中删除所有匹配查询的文档。

$data 传递了 搜索数据,如同在 itemsGet 方法中。

返回被删除的文档数量。

// Delete all documents with 'rating' property less than zero
echo User::itemsDelete(['rating' => ['$lt' => 0]]);

itemsNew 方法

YourModelClass::itemsNew(array $items, bool $returnID = true)

示例: examples/itemsNew.php

创建多个对象并将 insertMany 插入到 MongoDB 中。

该方法类似于 YourModelClass::new(),唯一的不同之处在于第一个参数是数据数组的数组。

返回创建的对象的 ID 数组,或者对象数组(如果 $returnID = false)。

$newUsersID = User::itemsNew(
    [
        [
            'name'   => 'Ben',
            'rating' => 77
        ],
        [
            'name'    => 'Robin',
            'surname' => 'Collins'
        ]
    ],
    true
);

变更历史

当使用 save() 方法保存对象时,将创建一个更改历史记录。历史记录将写入对象的 history 类型属性。检查具有 history 属性的属性是否已更改。

class User extends ModelMongoDB
{

    // ..............................

    static public $fieldsModel = [
        'userId'  => ['string'],
        'surname' => ['string'],                                          // DO NOT track changes
        'name'    => ['string', 'required'],                              // DO NOT track changes
        'email'   => ['string', 'history'],                               // Tracking changes
        'phones'  => ['array', 'history',                                 // Tracking changes
                      'historyPrepare' => 'historyPrepareArray:number', 
                      'historyValue' => 'historyValuePhones'
                     ],
        'history' => ['history']                                          // History array
    ];

}

如果具有 history 属性的对象的属性已更改,则会在历史记录数组中添加一个条目,其中包含更改的日期和一个列出所有已更改属性的 changes 对象。每个属性都是一个包含两个元素的数组(旧值,新值)。

{
    "datetime" : "2022-10-05 17:48:41",
    "changes" : {
        "phones" : [
            [],
            ["(+1) 555331188"]
        ],
        "email" : [
            "test@gmail.com",
            "email@gmail.com"
        ]
    }
}

historyPrepare 属性

historyPrepare 属性中,您可以指定自己的方法,该方法将用于比较 old value === new value

除了比较简单的数据类型(字符串、整数、浮点数、布尔值)之外,还有 2 个方法

  • historyPrepareArray 用于比较数组
  • historyPrepareObject 用于比较对象

historyPrepare 属性和到 historyPrepareArrayhistoryPrepareObject 方法的自己的方法将传递两个值

  1. 要按某种方式标准化的值
  2. 一个可选的变量,从冒号后的属性值。例如,数字将传递到 'historyPrepare' => 'historyPrepareArray:number'

假设用户有一个 phones 属性 - 它是一个包含电话的 stdClass 对象数组

[
    {
        "code" : 1,
        "phone" : 555331188,
        "number" : 1555331188,
        "formatted" : "(+1) 555331188",
        "timeAdd" : "2022-10-05 17:48:41"
    },
    {
        "code" : 12,
        "phone" : 7774477,
        "number" : 127774477,
        "formatted" : "(+1) 7774477",
        "timeAdd" : "2022-10-05 19:31:02"
    }
]

从给定的示例 'historyPrepare' => 'historyPrepareArray:number',电话数组将通过 number 字段进行比较。当保存时,historyPrepareArray 方法将比较数据转换为字符串 [127774477,1555331188]。在最后一次保存之后,对 快照 的数据也会进行相同的转换。如果存在变化,将进行进一步的比较并在历史记录中记录。

historyValue 属性

您将处理并返回要写入历史的值的自定义 方法

请参阅示例:examples/User.php

checkRecordHistory 方法

checkRecordHistory(array $changes)

默认返回 true;如果返回 false,则不会添加更改历史。

在已经编译更改历史的情况下,在保存期间调用。

$changes 传递当前更改。

当您需要记录更改历史,但有时在某些情况下不记录时,此方法很有用。

例如,您可以检查当前历史的尺寸,并且不写入超过100个条目

class User extends ModelMongoDB
{

    // ..............................

    protected function checkRecordHistory($changes) {

        return count($this->history) < 100;    
    
    }

}

帮助开发者

触发器

class User extends ModelMongoDB
{

    // ..............................

    function afterLoad() {
        
        // If the rating in MongoDB was less than 500
        if (empty($this->rating) or $this->rating < 500) {
        
            $this->rating = 500;
        
        }
    
    }
    
    function preSave() {
        
        // If after all the manipulations the rating has become less than -100
        if ($this->rating < -100) {
        
            $this->ban = true;
        
        }
    
    }

}

快照

getSnapshot($field)getSnapshotUpdate($field)

在从 MongoDB 加载数据后,创建当前状态的快照。在此过程中,您可能需要找出数据库中的数据或自上次保存以来的数据。

if ($user = User::get('63399434089c8c26344ff2df')) {

    $user->name = 'Jack';
    echo $user->name; // Jack
    echo $user->getSnapshot('name'); // Will
    echo $user->getSnapshotUpdate('name'); // Will
    
    // Save name Jack
    $user->saveFields('name');
    
    $user->name = 'Bob';
    echo $user->name; // Bob
    echo $user->getSnapshot('name'); // Will
    echo $user->getSnapshotUpdate('name'); // Jack
    
    // Save name Dan
    $user->save(['name' => 'Dan']);
    
    $user->name = 'Test';
    echo $user->name; // Test
    echo $user->getSnapshot('name'); // Will
    echo $user->getSnapshotUpdate('name'); // Dan   
    
}

getCollection 方法

YourModelClass::getCollection()

返回一个 MongoDB 集合。可用于直接查询。

use MongoModel\MongoDB;

// Ban all users with a rating less than -100
MongoDB::execute(User::getCollection(), 'updateMany', [
    'rating' => ['$lt' => -100]
], [
    '$set' => [
        'ban' => true
    ]
]);

collectionInfo 方法

MongoDB::collectionInfo(string $collection)

示例:examples/collectionInfo.php

返回有关集合的信息。

use MongoModel\MongoDB;

print_r(MongoDB::collectionInfo('users'));
stdClass Object
(
    [count] => 27
    [storageSize] => 36864
    [indexSize] => 36864
    [indexCount] => 1
    [size] => 6189
    [avgObjSize] => 229
)