PHPackages                             abdelilah-ezzouini/revertvel - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. abdelilah-ezzouini/revertvel

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

abdelilah-ezzouini/revertvel
============================

Drop a Revertvel snapshot before any risky Laravel operation — revert it anytime.

15PHP

Since Jun 4Pushed 1mo agoCompare

[ Source](https://github.com/AbdelilahEzzouini/revertvel)[ Packagist](https://packagist.org/packages/abdelilah-ezzouini/revertvel)[ RSS](/packages/abdelilah-ezzouini-revertvel/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependenciesVersions (1)Used By (0)

revertvel
=========

[](#revertvel)

Drop a **Revertvel** snapshot before any risky Laravel operation — revert it anytime with a single command.

Instead of manually writing rollback scripts after something goes wrong, you define the undo logic **before** you run the operation. you just run `php artisan revertvel:revert {id}`.

---

Installation
------------

[](#installation)

```
composer require abdelilah-ezzouini/revertvel
```

Laravel auto-discovers the service provider. No manual registration needed.

### Publish the config (optional)

[](#publish-the-config-optional)

```
php artisan vendor:publish --tag=revertvel-config
```

### Run the migration

[](#run-the-migration)

```
php artisan migrate
```

This creates the `revertvel_snapshots` table.

---

Configuration
-------------

[](#configuration)

```
// config/revertvel.php
return [
    'table' => 'revertvel_snapshots', // change if it conflicts with an existing table
];
```

---

Usage
-----

[](#usage)

### 1. Capture a snapshot before your risky operation

[](#1-capture-a-snapshot-before-your-risky-operation)

Wrap the **revert logic** (not the forward logic) in `Revertvel::capture()`.
The closure you pass is what will run **if you ever need to undo**.

> **Critical — always use fully-qualified class names (FQCN) inside the closure.**
>
> The closure is serialized to a string and stored in the database. When it is later unserialized and executed, PHP has no knowledge of the `use ClassName` imports that existed in your original file. Any bare alias like `DB` or `Carbon` will cause a fatal "Class not found" error at revert time.
>
> ✅ `\Illuminate\Support\Facades\DB::table(...)`
> ❌ `DB::table(...)` — will fail at revert time

> **Also — never capture Eloquent model instances or class objects via `use()`.**
> Serialize them to plain arrays first with `->toArray()`. Primitives and arrays travel safely; objects may not deserialize correctly across requests.

```
use AbdelilahEzzouini\Revertvel\Facades\Revertvel;
use Illuminate\Support\Facades\DB;

// 1. Collect the state you need to restore — as plain arrays, not objects
$originalRows = DB::table('orders')
    ->where('status', 'pending')
    ->get()
    ->map(fn($r) => (array) $r)
    ->all();

// 2. Capture the revert closure — use FQCN for every class inside
$snapshotId = Revertvel::capture('Bulk close pending orders', function () use ($originalRows) {
    \Illuminate\Support\Facades\DB::transaction(function () use ($originalRows) {
        foreach ($originalRows as $row) {
            \Illuminate\Support\Facades\DB::table('orders')
                ->where('id', $row['id'])
                ->update(['status' => $row['status']]);
        }
    });
});

// 3. Now perform the risky forward operation
DB::table('orders')->where('status', 'pending')->update(['status' => 'closed']);

$this->info("Done. Revert anytime: php artisan revertvel:revert {$snapshotId}");
```

> **Tip:** Call `Revertvel::capture()` inside the same `DB::transaction()` as your forward operation.
> If the transaction rolls back, the snapshot is also discarded — no orphaned records.

```
DB::transaction(function () use (&$snapshotId) {
    $snapshotId = Revertvel::capture('label', function () {
        // ⚠️ Use FQCN here — no class aliases available at revert time
        \Illuminate\Support\Facades\DB::table('...')->update([...]);
    });

    // ... your forward operation ...
});
```

---

### 2. List snapshots

[](#2-list-snapshots)

```
# All snapshots (latest 20)
php artisan revertvel:list

# Only pending (not yet reverted)
php artisan revertvel:list --pending

# Only reverted
php artisan revertvel:list --reverted

# Increase limit
php artisan revertvel:list --limit=50
```

---

### 3. Revert a snapshot

[](#3-revert-a-snapshot)

```
php artisan revertvel:revert 7
```

The command shows a summary and asks for confirmation. Use `--force` to skip the prompt:

```
php artisan revertvel:revert 7 --force
```

Reverting a snapshot that was already reverted shows a warning but can be re-run with `--force`.

---

Programmatic revert
-------------------

[](#programmatic-revert)

```
use AbdelilahEzzouini\Revertvel\Facades\Revertvel;

Revertvel::revert(7);
```

---

How it works
------------

[](#how-it-works)

1. You pass a plain PHP `Closure` to `Revertvel::capture()`.
2. The package serializes it using [`laravel/serializable-closure`](https://github.com/laravel/serializable-closure) and stores it as a `longText` column in the database — all captured variables travel with it.
3. `revertvel:revert` unserializes and calls the closure. No handler classes, no external dependencies, no class naming required.
4. **Why FQCN?** The closure is stored as a serialized string. When deserialized in a future request or CLI run, PHP re-evaluates the closure body in a blank context — the `use Illuminate\...` import statements from your original file are gone. Only fully-qualified names like `\Illuminate\Support\Facades\DB` are guaranteed to resolve.

---

AI Rules / Copilot Instructions
-------------------------------

[](#ai-rules--copilot-instructions)

Publish a ready-made rule file for your AI coding assistant (GitHub Copilot, Cursor, etc.) so it always generates correct `Revertvel::capture()` closures:

```
php artisan vendor:publish --tag=revertvel-ai-rules
```

This drops `.github/copilot-instructions/revertvel.md` into your project. GitHub Copilot picks it up automatically; for Cursor, move or symlink it to `.cursor/rules/revertvel.md`.

The rules file instructs the AI to:

- Always use FQCN inside every capture closure
- Never capture Eloquent objects — only plain arrays
- Always wrap revert logic in `\Illuminate\Support\Facades\DB::transaction()`
- Always call `capture()` inside the same transaction as the forward operation

---

Running tests
-------------

[](#running-tests)

Inside the package directory:

```
composer install
./vendor/bin/phpunit
```

---

Changelog
---------

[](#changelog)

### v1.0.0

[](#v100)

- Initial release

---

License
-------

[](#license)

MIT

###  Health Score

21

—

LowBetter than 17% of packages

Maintenance59

Moderate activity, may be stable

Popularity7

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/e522959c7b140ba3ecf6099621004443830d7ae47453f36d2b5cc4fe066270e6?d=identicon)[AbdelilahEzzouini](/maintainers/AbdelilahEzzouini)

---

Top Contributors

[![AbdelilahEzzouini](https://avatars.githubusercontent.com/u/23565045?v=4)](https://github.com/AbdelilahEzzouini "AbdelilahEzzouini (2 commits)")

### Embed Badge

![Health badge](/badges/abdelilah-ezzouini-revertvel/health.svg)

```
[![Health](https://phpackages.com/badges/abdelilah-ezzouini-revertvel/health.svg)](https://phpackages.com/packages/abdelilah-ezzouini-revertvel)
```

###  Alternatives

[laravel-admin-ext/helpers

Helpers extension for laravel-admin

136291.6k8](/packages/laravel-admin-ext-helpers)[stephenjude/laravel-wallet

A simple wallet implementation for Laravel

26611.9k](/packages/stephenjude-laravel-wallet)[fisharebest/algorithm

Implementation of standard algorithms in PHP.

71110.5k2](/packages/fisharebest-algorithm)[laravel-admin-ext/grid-sortable

Sort the grid data by drag and drop rows

42123.7k](/packages/laravel-admin-ext-grid-sortable)[devforest/laravel-dynamic-report-generator

A package for dynamic report generation with drag-and-drop interface in Laravel

3215.7k](/packages/devforest-laravel-dynamic-report-generator)[magepsycho/magento2-seosuite

Magento 2 FREE SEO Suite

106.5k](/packages/magepsycho-magento2-seosuite)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
