radmen / adonis-lucid-soft-deletes

Experimental implementation of soft-deletes for Adonis Lucid
MIT License
32 stars 8 forks source link

Adonis Lucid Soft Deletes

This is an experimental Lucid Addon which adds support for soft deletes. It's based on PR #315 created by me.

This code should be treated as experimental and likely unstable. You're using it on your own responsibility.

Installation

adonis install @radmen/adonis-lucid-soft-deletes

# for yarn users
adonis install @radmen/adonis-lucid-soft-deletes --yarn

Next, make sure to read the instructions.md file.

Usage

Removing model

Default delete() method will mark model as soft-deleted (it will set current date in deleted_at attribute).

If you'd like to really remove the model pass { force: true } as a first argument to the method.

Restoring model

To restore deleted model use restore() method. Note that it will work only for soft-deleted models.

Scopes

Trait will append global scope which includes only records with deleted_at = NULL.

It's possible to query model with deleted records. To do so, use withTrashed() method.

If you want to get only deleted records, use onlyTrashed() method.

Getters

Trait will append the following getters to the model:

Hooks

For either soft or force delete, beforeDelete/afterDelete hooks will be triggered.

When a model is being restored it will trigger beforeRestore/afterRestore hooks.

Hacks, workarounds

isDeleted

By default, Adonis Models can't be updated if they've been deleted. When this happens isDeleted returns TRUE and no change is possible. This prevents from using soft deletes so the trait changes this behavior. isDeleted will always return FALSE.

This may generate some unexpected problems, yet they didn't occur to me.

Monkey patches

To make things work I had to monkey patch some of Adonis Lucid components:

Model

Lucid/QueryModel

If a Model uses soft-deletes delete() method will mark matching records as soft-deleted.

To remove matching records pass { force: true } as a first argument.

Known bugs

Global scope with OR statements

It's possible to ignore global scope by mistake. This can happen when one uses orWhere methods (in general any OR statement):

User.where('name', 'Jon').orWhere('name', 'Array')

To avoid that you should group such statements:

User.where(function () {
  this.where('name', 'Jon')
    .orWhere('name', 'Array')
})

Testing

To run tests use npm test. By default, it will use sqlite as default database backend.

It's possible to run tests against postgres or mysql database backends. This requires Docker to be installed.

If you want to run tests using mysql driver:

NODE_ENV=mysql .travis/setUp.sh
NODE_ENV=mysql npm test

# remove the database
.travis/tearDown.sh

Available databases:

Why separate package?

I've created hacky, simple trait implementing basics for soft deletes. It was taken from a project of mine, where it was good enough to be used.

Since this feature is on Adonis roadmap I thought that it can be used as a baseline for official Adonis implementation. I've started a PR and left some questions. Some of them were answered, some of them not. This prevented me from further development.

Since there's a high demand for this feature, I was asked to publish it as a separate package and allow the community to check it and leave feedback.
After some consideration, I think that it's a good idea.

The goal of this package is to test it, gather feedback, improve and eventually back-port to Adonis.
I'm not an active Adonis user so community support is required.