定制 kdabrow/time-machine 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

kdabrow/time-machine

Composer 安装命令:

composer require --dev kdabrow/time-machine

包简介

Laravel / Lumen library that allows to change dates in db

README 文档

README

GitHub Workflow Status (branch) Packagist Version Packagist Downloads

Time machine

This package allows to move in time database data. It automatically selects all fields that store datetime and move them by given period or to particular date, relatively from it current value. See example to check more details.

Motivation

This package might be useful in a pre-prod environment to test log lasting processes. For example generating customer invoice. Usually invoices are generated in a 30 days period. You might move
customer and all it's data into previous invoice cycle (30 days in to past) and effectively simulate whole invoice cycle like that customer was created 30 days ago.

Installation

First install main package:

composer require kdabrow/time-machine

Then install database driver package:

Database Driver
mysql composer require kdabrow/time-machine-mysql
postgres composer require kdabrow/time-machine-postgres

Advice is to install those packages as --dev dependencies.

Usage

Time traveller setup

First create TimeTraveller. Only Eloquent model can be TimeTraveller. Basic configuration look like this:

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

Modify query

TimeMachine by default selects all rows related to given to TimeTraveller model. You're able to provide your conditions to restrict query

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

Additional columns

It's possible to add additional columns into query.

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

Exclude fields

Sometimes is need to omit some fields that would usually be selected to change.

<?php

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

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

Set up keys

Records selected to time travel are based on primary key from the model. You're able to overwrite it.

<?php

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

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

Time machine and direction of move

After TimeTravellers are created, create TimeMachine.

<?php

use Kdabrow\TimeMachine\TimeMachine;

$timeMachine = new TimeMachine();

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

Move to the past

First create DateChooser. It is information about period or date.

<?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();

Move to the future

First create DateChooser. It is information about period or date.

<?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();

Move to particular date

First create DateChooser. It is information about period or date.

<?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();

Examples

Move customer, it's payments and orders 10 days in the past

Database structure before and after change:
customers (before)

id email date_of_birth activated_at created_at updated_at
100 tesla@test.com 1856-07-10 2000-06-15 12:12:12 1999-06-15 12:12:12 1999-06-15 12:12:12

customers (after)

id email date_of_birth activated_at created_at updated_at
100 tesla@test.com 1856-07-10 2000-06-05 12:12:12 1999-06-05 12:12:12 1999-06-05 12:12:12

payments (before)

id customer_id amount paid_at created_at updated_at
200 100 120 2020-03-24 13:10:45 2020-03-24 10:10:45 2020-03-24 13:10:45

payments (after)

id customer_id amount paid_at created_at updated_at
200 100 120 2020-03-14 13:10:45 2020-03-14 10:10:45 2020-03-14 13:10:45

orders

id payment_id name sent_at created_at updated_at
300 200 Engine 2020-03-25 2020-03-24 13:11:22 2020-03-25 15:32:17

orders

id payment_id name sent_at created_at updated_at
300 200 Engine 2020-03-15 2020-03-14 13:11:22 2020-03-15 15:32:17

Script:

<?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();

Testing

Run tests from docker container

docker-compose exec php vendor/bin/phpunit

or directly from your machine

vendor/bin/phpunit

kdabrow/time-machine 适用场景与选型建议

kdabrow/time-machine 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 6.07k 次下载、GitHub Stars 达 5, 最近一次更新时间为 2020 年 06 月 15 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「database」 「php」 「testing」 「laravel」 「integration」 「dates」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 kdabrow/time-machine 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 kdabrow/time-machine 我们能提供哪些服务?
定制开发 / 二次开发

基于 kdabrow/time-machine 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 6.07k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 5
  • 点击次数: 11
  • 依赖项目数: 2
  • 推荐数: 0

GitHub 信息

  • Stars: 5
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2020-06-15