3 min read

SoftDelete and Notifications

Handling legacy notifications when users have the option to delete requests.

What happens when you receive a notification to approve a request, but when you click the link, you see a 404 error? This is exactly what some of my users experienced after we implemented a "Delete" feature.

Initially, a Delete feature was intentionally excluded to ensure a complete audit trail. The company’s policy was to maintain a permanent record of all activities and requests, regardless of their final status, so that every action could be tracked and verified by auditors.

In our internal workflow engine, we use a multi-level approval process. This structure was ideal for development because once a request was created, we sent notifications to the approver via both email and the database. Since these notifications contain direct links to the request page, a "Hard Delete" would be problematic.

However, in a recent release, we had to implement the Delete feature due to high user demand. To mitigate the risk, I opted for Soft Deletes instead of a Hard Delete. Essentially, Soft Deleting does not remove the record from the database; instead, it updates a deleted_at column with a timestamp. When a request is active, this value is NULL. By adding a simple trait to the relevant models, the system automatically filters out any records where deleted_at is not null. This approach perfectly matched my requirement: it "deleted" the item from the user's list while ensuring it remained technically available for audit purposes.

At the time, I chose to simply display a 404 page for deleted records and let users figure it out—mostly because I had other high-priority tasks on my plate.

Recently, an Accounting Manager reached out because he couldn't find requests for which he had received notifications. The notification was still on his dashboard and hadn't even been marked as read. Having found some "breathing room" in my schedule, I decided it was time to implement a better solution. I followed three main steps across 11 different request types:

  • Use the withTrashed method in the Show Routes to allow the controller to retrieve deleted records.
  • Add a conditional message in the View/Controller: "This request has been deleted by the preparer." if the user is Authorized to view the request.
  • Clean up the Database notifications by deleting all notification records related to that specific request ID upon deletion.

This solved the primary confusion. While the email notifications still exist in their inboxes, clicking the link now provides a clear explanation to authorized users rather than a broken page. For anyone else, the system still returns a 404!