kdabrow/time-machine

Laravel / Lumen 库,允许在数据库中更改日期

1.2.0 2024-05-02 14:22 UTC

This package is auto-updated.

Last update: 2024-09-02 15:16:41 UTC


README

GitHub Workflow Status (branch) Packagist Version Packagist Downloads

时间机器

此包允许在数据库数据中移动时间。它会自动选择存储日期时间的所有字段,并根据给定的时间段或特定日期移动它们,相对于当前值。请查看示例以获取更多详细信息。

动机

此包可能有助于在预生产环境中测试持续时间较长的进程。例如,生成客户发票。通常,发票是在30天期限内生成的。您可以将客户及其所有数据移动到以前的发票周期(30天前的过去),并有效地模拟整个发票周期,就像客户是在30天前创建的一样。

安装

首先安装主包

composer require kdabrow/time-machine

然后安装数据库驱动包

建议将这些包作为 --dev 依赖项安装。

使用方法

时间旅行者设置

首先创建 TimeTraveller。只有 Eloquent 模型可以是 TimeTraveller。基本配置如下所示

<?php

use Kdabrow\TimeMachine\TimeTraveller;
use App\Models\User;

// provide model instance
$traveller = new TimeTraveller(new User());

// or class name
$traveller = new TimeTraveller(User::class);

修改查询

TimeMachine 默认选择与给定的 TimeTraveller 模型相关的所有行。您可以为查询提供条件以限制查询

<?php

use Illuminate\Database\Eloquent\Builder;
use App\Models\User;
use App\Models\Group;
use Kdabrow\TimeMachine\TimeTraveller;
use Kdabrow\TimeMachine\Result;
use Illuminate\Support\Arr;

// Result contains all previously changed models from all TimeTravellers
// For example move Users only from previously changed Group
$traveller = new TimeTraveller(
    User::class, 
    function(Builder $builder, Result $result) {
        if ($result->isSuccessful(Group::class)) {
            return $query->whereIn(
                'group_id', 
                $result->getSuccessful(Group::class)->pluck('id')
            );
        }
    }
);

附加列

可以向查询中添加附加列。

<?php

use App\Models\User;
use Kdabrow\TimeMachine\TimeTraveller;
use Kdabrow\TimeMachine\Database\Column;
use Kdabrow\TimeMachine\Result;
use Illuminate\Database\Eloquent\Model;

$traveller = new TimeTraveller(User::class);
$traveller->alsoChange('date_of_creation');

// It's possible to provide callback and determine how given field should be changed
// Column object has information about currently modified column
// Model is selected instance of User
// Result contains previously moved in time data 
// In this example only move if no errors appeared during previous time travels 
$traveller->alsoChange(
    'date_of_creation', 
    function($currentValue, Column $column, Model $model, Result $result) {
        if (count($result->getAllFailed())) {
            return $currentValue;
        }
        
        return null;
    }
);

排除字段

有时需要排除一些通常会被选中的字段以进行更改。

<?php

use App\Models\User;
use Kdabrow\TimeMachine\TimeTraveller;

$traveller = new TimeTraveller(User::class);
$traveller->exclude('date_of_birth');

设置键

选择进行时间旅行的记录基于模型的键。您可以重写它。

<?php

use App\Models\User;
use Kdabrow\TimeMachine\TimeTraveller;

$traveller = new TimeTraveller(User::class);
$traveller->setKeys(['uuid']);

时间机器和移动方向

创建 TimeTravellers 后,创建 TimeMachine。

<?php

use Kdabrow\TimeMachine\TimeMachine;

$timeMachine = new TimeMachine();

// now add previously created TimeTravellers
$timeMachine->take($traveller1);
$timeMachine->take($traveller2);
$timeMachine->take($traveller3);

回到过去

首先创建 DateChooser。这是有关时间段或日期的信息。

<?php

use Kdabrow\TimeMachine\DateChooser;
use Kdabrow\TimeMachine\Result;
use DateInterval;

// move by DateInterval
$chooser = new DateChooser(new DateInterval("P1D")); // Move by 1 day

// or move by some specific amount of seconds
$chooser = new DateChooser(3600); // Move by 1 hour

// Now set up direction and start travel
/** @var Result $result */
$result = $timeMachine
    ->toPast($chooser)
    ->start();

走向未来

首先创建 DateChooser。这是有关时间段或日期的信息。

<?php

use Kdabrow\TimeMachine\DateChooser;
use Kdabrow\TimeMachine\Result;
use DateInterval;

// move by DateInterval
$chooser = new DateChooser(new DateInterval("P1D")); // Move by 1 day

// or move by some specific amount of seconds
$chooser = new DateChooser(3600); // Move by 1 hour

// Now set up direction and start travel
/** @var Result $result */
$result = $timeMachine
    ->toFuture($chooser)
    ->start();

移动到特定日期

首先创建 DateChooser。这是有关时间段或日期的信息。

<?php

use Kdabrow\TimeMachine\DateChooser;
use Kdabrow\TimeMachine\Result;
use DateTime;

// put DateTimeInterface object
$chooser = new DateChooser(new DateTime("2020-15-16 12:12:12")); // Move datetime columns to 2020-15-16 12:12:12

// or provide datetime string 
$chooser = new DateChooser("2020-15-16 12:12:12"); // Move to 2020-15-16 12:12:12

// Now set up direction and start travel
/** @var Result $result */
$result = $timeMachine
    ->toDate($chooser)
    ->start();

示例

将客户、其付款和订单移动10天到过去

更改前后数据库结构
customers (更改前)

customers (更改后)

payments (更改前)

payments (更改后)

orders

orders

脚本

<?php

use Kdabrow\TimeMachine\TimeTraveller;
use Kdabrow\TimeMachine\Result;
use Kdabrow\TimeMachine\TimeMachine;
use App\Models\Customer;
use App\Models\Payment;
use App\Models\Order;
use Illuminate\Support\Arr;

$customerId = 100;

$customerTraveller = new TimeTraveller(
    Customer::class, 
    function(Builder $builder, Result $result) use ($customerId) {
        return $builder->where('id', '=', $customerId);
    }
);
$customerTraveller->exclude('date_of_birth');

$paymentTraveller = new TimeTraveller(
    Payment::class,  
    function(Builder $builder, Result $result) use ($customerId) {
        if ($result->isSuccessful(Customer::class)) {
                return $builder->whereIn(
                    'customer_id', $result->getSuccessful(Customer::class)->pluck('id')
                );
            }    
        }
);

$orderTraveller = new TimeTraveller(
    Order::class,  
    function(Builder $builder, Result $result) use ($customerId) {
        if ($result->isSuccessful(Payment::class)) {
            return $builder->whereIn(
                'payment_id', $result->getSuccessful(Payment::class)->pluck('id')
            );
        }
    }
);

$timeMachine = new TimeMachine();

$result = $timeMachine
    ->take($customerTraveller)
    ->take($paymentTraveller)
    ->take($orderTraveller)
    ->toPast(new DateInterval("P10D"))
    ->start();

// Get instances that failed time travel
$failed = $result->getAllFailed();

$sucessful = $result->getAllSuccessful();

测试

从 docker 容器中运行测试

docker-compose exec php vendor/bin/phpunit

或直接从您的机器上运行

vendor/bin/phpunit