Skip to content

Instantly share code, notes, and snippets.

@andresilvagomez
Last active August 3, 2023 18:08
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save andresilvagomez/87f6f9a95387679dc9f963b3aa061358 to your computer and use it in GitHub Desktop.
Save andresilvagomez/87f6f9a95387679dc9f963b3aa061358 to your computer and use it in GitHub Desktop.
Soft deletes for Adonis 4.
// $ adonis make:trait SoftDeletes
class SoftDeletes {
register(Model, customOptions = {}) {
const deletedAtColumn = customOptions.name || `${Model.table}.deleted_at`;
Model.addGlobalScope(builder => {
builder.whereNull(deletedAtColumn);
}, 'adonis_soft_deletes');
Model.queryMacro('withTrashed', () => {
this.ignoreScopes('adonis_soft_deletes');
return this;
});
Model.queryMacro('onlyTrashed', () => {
this.ignoreScopes('adonis_soft_deletes');
this.whereNotNull(deletedAtColumn);
return this;
});
Model.prototype.delete = async () => {
this[deletedAtColumn] = new Date();
await this.save();
};
Model.prototype.restore = async () => {
this[deletedAtColumn] = null;
await this.save();
};
Model.prototype.forceDelete = async () => {
await Model.query()
.where(Model.primaryKey, this[Model.primaryKey])
.ignoreScopes()
.delete();
};
}
}
module.exports = SoftDeletes;
@zahidmuhammadzaki
Copy link

zahidmuhammadzaki commented Oct 22, 2019

@andresilvagomez I think line 5 should be like below to support relationship query.

const deletedAtColumn = customOptions.name || `${Model.table}.deleted_at`;

@andresilvagomez
Copy link
Author

@zahidmuhammadzaki right, I updated it, thanks !!!

@madrussa
Copy link

madrussa commented Jan 8, 2020

This didn't work for me using Adonis 4.1... the arrow functions bind the this scope to the trait and not the model instance. Also the ${Model.table}.deleted_at breaks on Postgres, it probably could do with not understanding the connection grammar/dialects.

class SoftDeletes {
  register(Model, customOptions = {}) {
    const deletedAtColumn = customOptions.name || `deleted_at`;

    Model.addGlobalScope(builder => {
      builder.whereNull(deletedAtColumn);
    }, 'adonis_soft_deletes');

    Model.queryMacro('withTrashed', () => {
      this.ignoreScopes('adonis_soft_deletes');
      return this;
    });

    Model.queryMacro('onlyTrashed', () => {
      this.ignoreScopes('adonis_soft_deletes');
      this.whereNotNull(deletedAtColumn);
      return this;
    });

    Model.prototype.delete = async function () {
      this[deletedAtColumn] = new Date();
      await this.save();
    };

    Model.prototype.restore = async function () {
      this[deletedAtColumn] = null;
      await this.save();
    };

    Model.prototype.forceDelete = async function () {
      await Model.query()
        .where(Model.primaryKey, this[Model.primaryKey])
        .ignoreScopes()
        .delete();
    };
  }
}

module.exports = SoftDeletes;

The above worked better... but I've not tested relations.

@kyawswar
Copy link

I want to know. How to use softdelete?
I use the trait. Show the error.
this.save() is not a function.
Please instruct me. How to do that?
Thanks

@devchuksemeka
Copy link

This didn't work for me using Adonis 4.1... the arrow functions bind the this scope to the trait and not the model instance. Also the ${Model.table}.deleted_at breaks on Postgres, it probably could do with not understanding the connection grammar/dialects.

class SoftDeletes {
  register(Model, customOptions = {}) {
    const deletedAtColumn = customOptions.name || `deleted_at`;

    Model.addGlobalScope(builder => {
      builder.whereNull(deletedAtColumn);
    }, 'adonis_soft_deletes');

    Model.queryMacro('withTrashed', () => {
      this.ignoreScopes('adonis_soft_deletes');
      return this;
    });

    Model.queryMacro('onlyTrashed', () => {
      this.ignoreScopes('adonis_soft_deletes');
      this.whereNotNull(deletedAtColumn);
      return this;
    });

    Model.prototype.delete = async function () {
      this[deletedAtColumn] = new Date();
      await this.save();
    };

    Model.prototype.restore = async function () {
      this[deletedAtColumn] = null;
      await this.save();
    };

    Model.prototype.forceDelete = async function () {
      await Model.query()
        .where(Model.primaryKey, this[Model.primaryKey])
        .ignoreScopes()
        .delete();
    };
  }
}

module.exports = SoftDeletes;

The above worked better... but I've not tested relations.

Using Adonis 4.1; In my case, I changed the arrow function to a normal function and that's it

Model.prototype.delete = async function() {
      this[deletedAtColumn] = new Date();
      await this.save();
    };

    Model.prototype.restore = async function() {
      this[deletedAtColumn] = null;
      await this.save();
    };

@devchuksemeka
Copy link

I want to know. How to use softdelete?
I use the trait. Show the error.
this.save() is not a function.
Please instruct me. How to do that?
Thanks

Adonis Js 4.2

Model.prototype.delete = async function() {
      this[deletedAtColumn] = new Date();
      await this.save();
    };

    Model.prototype.restore = async function() {
      this[deletedAtColumn] = null;
      await this.save();
    };

@JonhnyDev
Copy link

JonhnyDev commented Mar 9, 2022

It only works when you first search for the value and then delete it, if you do as the query below does not work.
"Bulk Deletes"

work:
let get = await Test_Softdelete.query().where('value', 10).first() if(get){ await get.delete() return get }
doesn't work with an array of data like fetch.
dont working:
await Test_Softdelete.query().where('value', 10).delete()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment