π Managing Soft Deletes
Using MySQL Update for Soft Deletes
In a typical workflow, a user clicks a link or button to deactivate their profile. This link passes the user's unique ID through the query string and routes them to a confirmation screen.
Instead of deleting user records, a soft delete updates the user's status in the database β typically by setting an is_blocked column to 1. This preserves the data while disabling the account.
π‘ Pro Tip: Use a two-step process: first show the userβs details, then ask for confirmation, and finally submit the change using a secure POST request.
Deactivation Steps
- A user clicks a
Deactivate Profilelink, which includes theiridin the query string (e.g.,deactivate.php?id=2). - The controller reads the
idfrom$_GET, retrieves the matching record from the database, and loads thedeactivate.phpview with user details. - The view displays a confirmation message with a POST form and hidden
idfield. - When the form is submitted, the controller reads the
idfrom$_POSTand passes it to the model'sdeactivateUser()method. - The model runs a parameterized UPDATE query that sets
is_blockedto 1 for the matching user. - If successful, the controller redirects to the homepage or a success screen. If not, it redirects back to the profile or shows an error.
π Project Structure
This structure outlines how the components of the soft delete system work together:
project-root/
βββ controllers/
β βββ UserController.php β logic for displaying and processing the form
βββ models/
β βββ UserModel.php β database interaction for user records
βββ views/
β βββ partials/
β β βββ header.php β shared HTML header and navigation
β β βββ footer.php β shared footer and closing tags
β βββ profile/
β βββ create.php β displays the registration form
β βββ deactivate.php β *new* confirmation form for deactivation
β βββ edit.php β displays the edit profile form
β βββ show.php β displays the user profile view
β βββ partials/
β βββ form-fields.php β shared form fields for create and edit views
βββ deactivate.php β *new* entry point for soft delete POST requests
βββ profile.php β entry point for profile view/edit/update
βββ register.php β entry point for user registration
Adding a Deactivate Link
Place this button in your views/profile/show.php file after displaying the user's profile details:
<a class="btn btn-danger" href="deactivate.php?id=<?= htmlspecialchars($user['id']) ?>">
Deactivate Profile
</a>
This link directs the user to deactivate.php where they can confirm or cancel the action.
β οΈ Important: This link only works if a valid user id exists in your database. Always validate IDs before using them in queries to prevent errors and misuse.
Entry Point: deactivate.php
This file serves as the entry point for handling the soft delete request. It loads the controller and calls the deactivate() method after the form is submitted.
deactivate.php
<?php
require_once 'controllers/UserController.php';
UserController::deactivate();
?>
Place this file in your project root directory. It acts as a lightweight router that safely delegates logic to the controller without exposing internal paths.
Controller Logic
Add the following method to your UserController class. This method handles both the GET request to load the confirmation view and the POST request to perform the soft delete:
public static function deactivate() {
$id = $_POST['id'] ?? $_GET['id'] ?? null;
// Check server request method is POST
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Check for POST $id and db update success
if ($id && UserModel::deactivateUser($id)) {
header("Location: index.php?deactivate=success");
exit;
}
header("Location: index.php?error=failed");
exit;
}
// Check for GET $id, select db record and load the deactivate.php view
if ($id && $user = UserModel::getUserById($id)) {
require 'views/profile/deactivate.php';
return;
}
header("Location: index.php?error=notfound");
exit;
}
π§ Best Practice: Keeping both GET and POST logic in the same controller method simplifies routing and avoids spreading logic across multiple files.
Deactivation View
Create a new file at views/profile/deactivate.php to display the confirmation form and user info. This prevents accidental or unauthorized deactivations by requiring a POST submission.
<?php include 'views/partials/header.php'; ?>
<h2>Confirm Deactivation</h2>
<p>Are you sure you want to deactivate this account? You must contact the webmaster to reactivate it.</p>
<form method="post" action="deactivate.php">
<input type="hidden" name="id" value="<?= htmlspecialchars($user['id']) ?>">
<button type="submit" name="deactivate" class="btn btn-danger">Confirm Deactivation</button>
<a href="profile.php?id=<?= htmlspecialchars($user['id']) ?>" class="btn btn-secondary">Cancel</a>
</form>
<?php include 'views/partials/footer.php'; ?>
β οΈ Security Tip: Always use a POST request to finalize sensitive changes like deactivation. Avoid relying on GET requests for destructive actions.
Model Logic
Step 1: Update the Database Schema
Add an is_blocked column to the users table to track deactivation status:
ALTER TABLE users ADD COLUMN is_blocked TINYINT(1) NOT NULL DEFAULT 0;
Step 2: Add Model Method
Include this method in your UserModel class to flag a user as deactivated:
public static function deactivateUser($id) {
$db = static::getDB();
$stmt = $db->prepare("UPDATE users SET is_blocked = 1 WHERE id = :id LIMIT 1");
return $stmt->execute([':id' => $id]);
}
π‘οΈ Security Tip: Always use parameterized queries when updating the database. This protects your app from SQL injection vulnerabilities.
Summary / Takeaways
- Soft deletes preserve data by marking records as inactive rather than removing them
- Route soft delete requests through a dedicated entry point like
deactivate.php - Use a two-step process: confirmation view followed by POST submission
- Handle routing and logic inside the controller for clarity
- Use an
is_blockedcolumn to track deactivation status in the database
Last updated: August 8, 2025 at 1:21 PM