Laravel Soft Delete – Create a Trash Can using PHP and MySQL

Laravel soft delete allows you to enable some models that will not be permanently deleted by just calling the delete() method. Instead, you need to call the forceDelete() method in order to permanently delete the data.

The deleted models will not be fetched from Eloquent or Query Builder queries. Instead, you need to call a separate function in order to fetch the records along with deleted ones, we will get on that later in this article. Laravel soft delete has several advantages:

  • First, it works like your PC’s recycle bin. If you accidentally deleted from record from your admin panel, it will not be deleted permanently at once.
  • Second, you can display all deleted data on a separate page.
  • You have a backup of your deleted data, so in the future, if you need some old data, you can always get it from recycle bin or trash can.
  • Can display all data to the user and highlight the data that is deleted.
  • Always be able to restore the accidentally deleted records.

Let’s get started

So let’s get started by creating a simple Laravel soft delete module. For the sake of this article, we will be implementing a trash can in our user’s model.

1. Creating a column in the user’s table

First, you need to run the following command to create a migration for soft deleting:

php artisan make:migration add_column_soft_delete_in_users_table

Then you need to open your migration file and add the following line in the Schema::table function inside up() method:

$table->softDeletes();

Then you need to run the migration using the following command:

php artisan migrate

Open your phpMyAdmin and you will see a new column at the end of the user’s table named deleted_at. Its default value will be NULL and it will have a current date & time value once you call the delete() method.

2. Laravel Soft Delete Eloquent Model

Open your user model usually in App\Models\User.php and update it as the following:

use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use HasFactory, Notifiable, SoftDeletes;

    ///////////
}

After that, whenever you call the delete() method on any user object, you will see that deleted_at the field will be updated to the current date & time of that user.

3. Display all Users with Pagination

Let’s say that you have an admin panel. We will be displaying all users (non-deleted) on one page. So first create a route for this in your routes/web.php file:

use App\Http\Controllers\UserController;

Route::group([
    "prefix" => "users"
], function () {
    Route::get("/", [UserController::class, "index"]);

    // [other routes goes here]
});

We have created a group because we will be adding more routes to it. For example, route to delete a user, route to display all deleted users, route to restore a user, and route to permanently delete a user. Run the following command if you do not have the UserController:

php artisan make:controller UserController

Now, create an index() method in your UserController class. Following is what your UserController should look like:

use App\Models\User;

public function index()
{
    $users = User::orderBy("id", "desc")->paginate();
    $trashed = User::onlyTrashed()->count();

    return view("users/index", [
        "users" => $users,
        "trashed" => $trashed
    ]);
}

Create a folder named users and inside this folder, create a file in resources/views/users/index.blade.php. This file should have the following code:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/css/bootstrap.min.css" />
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/js/bootstrap.min.js"></script>

<a href="{{ url('/users/trashed') }}">
    Trash ({{ $trashed }})
</a>

<table class="table table-bordered">
    <thead>
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Email</th>
        </tr>
    </thead>

    <tbody>
        @foreach ($users as $user)
            <tr>
                <td>{{ $user->id }}</td>
                <td>{{ $user->name }}</td>
                <td>{{ $user->email }}</td>
            </tr>
        @endforeach
    </tbody>
</table>

{{ $users->appends(request()->except("page"))->links() }}

Refresh the page now and you will see a list of all users. Now we need to create a button to delete a user.

4. Delete a User

To delete a user, we will first a heading in our <thead> tag:

<th>Actions</th>

Then in <tbody>, we need to create a form which when submit will first ask for confirmation. If confirmed, then it will mark the user as deleted.

<td>
	<form method="POST" action="{{ url('/users/delete') }}" onsubmit="return confirm('Are you sure you want to delete this user ?');">
		{{ csrf_field() }}
		<input type="hidden" name="id" value="{{ $user->id }}" required>
		<button type="submit" class="btn btn-danger">
			Delete
		</button>
	</form>
</td>

Refresh the page and you will see a delete button along with each user. Then create a route on your web.php file:

Route::post("/delete", [UserController::class, "destroy"]);

The above lines go in the [other routes goes here] section of web.php. Then create a method in your UserController to mark the user as deleted:

public function destroy()
{
    $user = User::find(request()->id);
    if ($user == null)
    {
        abort(404);
    }

    $user->delete();
    return redirect()->back();
}

You can delete the user in one line too, like this: User::destroy(request()->id); but fetching the user first has some advantages:

  • You can perform any other sub-functions before deleting a user.
  • Or check if the user exists or not, and display the proper messages accordingly.

Try deleting a user now, you will see that will no longer be displayed in your Bootstrap table. But you can still see the record in your database using phpMyAdmin. However, you will notice that the deleted_at the field is updated and now has the UTC date and time value when the delete operation was performed.

5. Show All Deleted Users

At 3rd step, we create an anchor tag that displays all the trashed user’s counts. Now is the time to create a route for it. To create a route on your routes/web file.

Route::get("/trashed", [UserController::class, "trashed_users"]);

Then create the following method in your UserController:

public function trashed_users()
{
    $trashed = User::onlyTrashed()->orderBy("id", "desc")->paginate();

    return view("users/trashed", [
        "trashed" => $trashed
    ]);
}

After that, create a file resources/views/users/trashed.blade.php. It will have the following code:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/css/bootstrap.min.css" />
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/js/bootstrap.min.js"></script>

<table class="table table-bordered">
    <thead>
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Email</th>
            <th>Deleted at</th>
            <th>Actions</th>
        </tr>
    </thead>

    <tbody>
        @foreach ($trashed as $user)
            <tr>
                <td>{{ $user->id }}</td>
                <td>{{ $user->name }}</td>
                <td>{{ $user->email }}</td>
                <td>{{ $user->deleted_at }}</td>
            </tr>
        @endforeach
    </tbody>
</table>

{{ $trashed->appends(request()->except("page"))->links() }}

Refresh the page now and you will see all deleted users in a table. You will also be able to view the date and time when they were deleted.

6. Restore the Deleted Records

First, create another <td> tag at the end of <tbody> tag. In this table cell, we will create a button that will ask for confirmation. And when confirmed, will restore the user.

<td>
    <form method="POST"
        action="{{ url('/users/restore') }}"
        onsubmit="return confirm('Are you sure you want to restore this user ?');">

        {{ csrf_field() }}

        <input type="hidden" name="id" value="{{ $user->id }}" required />
        <button type="submit" class="btn btn-success">
            Restore
        </button>
    </form>
</td>

Then create a route in your web/routes.php that will handle this request.

Route::post("/restore", [UserController::class, "do_restore"]);

After that, create the following method in your UserController:

public function do_restore()
{
    $user = User::withTrashed()->find(request()->id);
    if ($user == null)
    {
        abort(404);
    }

    $user->restore();
    return redirect()->back();
}

Refresh the page now and you will see a “Restore” button along with each delete user. On clicking that, you will be asked for confirmation. If confirmed, you will no longer see that record in the trash can. But you will start seeing that record in all users table.

If you open your database using phpMyAdmin, you will see that user will again have the deleted_at column value as NULL.

7. Permanently Delete Records

Where you are displaying all soft-deleted records in your trash can, you are already displaying a button to restore the record. Now is the time to display another button that will permanently delete the record from the database. Create a button along with the “Restore” button in your trash can:

<form method="POST"
    action="{{ url('/users/delete-permanently') }}"
    onsubmit="return confirm('Are you sure you want to permanently delete this user ?');">

    {{ csrf_field() }}

    <input type="hidden" name="id" value="{{ $user->id }}" required />
    <button type="submit" class="btn btn-danger">
        Delete
    </button>
</form>

Then, you need to create a route that will handle this request.

Route::post("/delete-permanently", [UserController::class, "delete_permanently"]);

Then you need to create a method in your UserController that will permanently delete that record from the database.

public function delete_permanently()
{
    $user = User::withTrashed()->find(request()->id);
    if ($user == null)
    {
        abort(404);
    }

    $user->forceDelete();
    return redirect()->back();
}

Refresh the page now and click on the delete button from the trash can. You will be asked for confirmation. Once confirmed, you will no longer see that record in the trash can nor in all users table. If you check the database, you will see that, that user’s row has been permanently deleted from the database.

Get our social networking site project developed in Laravel:

Social Networking Site in Laravel – Node JS, Socket IO

So that’s how you can implement a complete Laravel soft delete module in your web application.

[wpdm_package id=’1545′]

Save and Download BLOB images – PHP and MySQL

In this article, we will show you, how you can save BLOB images in a MySQL database using PHP. You will also be able to download the BLOB images as well. To upload the file to the server and save its path in the MySQL database, you can follow our this tutorial.

Table structure:

Run the following code in your index.php file to create the required table that will hold all saved BLOB images:

<?php
	$conn = mysqli_connect("localhost", "root", "", "blob");
	$sql = "CREATE TABLE IF NOT EXISTS `images` (
		`id` INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
        `image` LONGBLOB NOT NULL,
		`name` TEXT NOT NULL,
		`type` VARCHAR (11) NOT NULL
	)";
	mysqli_query($conn, $sql);
?>

Laravel developers can do it in the following way:

use Illuminate\Support\Facades\DB;

Schema::create('images', function (Blueprint $table) {
    $table->id();
    $table->longText("name")->nullable();
    $table->longText("type")->nullable();
    // $table->binary("image");
    // OR
    // $table->longText("image")->charset('binary');
    $table->timestamps();
});

DB::statement("ALTER TABLE images ADD image LONGBLOB DEFAULT NULL AFTER id");

Run your file and you will see a new table created in your phpMyAdmin.

Save BLOB images in MySQL:

First, you need to create an input field from which you can upload an image.

<form method="POST" action="upload.php" enctype="multipart/form-data">
	<p>
		<label>Upload Image</label>
		<input type="file" name="image" accept="image/*" required />
	</p>
	<input type="submit" value="Upload" />
</form>

Create a file named upload.php and write the following code in it:

<?php
	// assume you have a database named 'blob'
	$conn = mysqli_connect("localhost", "root", "", "blob");
	$image = $_FILES["image"];
	$info = getimagesize($image["tmp_name"]);
    if(!$info)
	{
		die("File is not an image");
	}
	$name = $image["name"];
    $type = $image["type"];
	$blob = addslashes(file_get_contents($image["tmp_name"]));

    // if you are developing in Laravel
    // $blob = file_get_contents($file->getRealPath());

	$sql = "INSERT INTO `images` (`image`, `name`, `type`) VALUES ('" . $blob . "', '" . $name . "' , '" . $type . "')";
	mysqli_query($conn, $sql) or die(mysqli_error($conn));
    echo "File has been uploaded.";
?>

Run the code now and you will be displayed an input field and a submit button. Select an image file and hit the submit button, if all goes well, you will see a success message. Then you can check in your database, a new row will be created, the image will be saved as a blob file and the name of the image file will be saved in name column.

Show all Saved BLOB images:

To show all saved BLOB files, you need to run the following code:

<?php
	$conn = mysqli_connect("localhost", "root", "", "blob");
	$sql = "SELECT id, name FROM `images` ORDER BY `id` DESC";
	$result = mysqli_query($conn, $sql);
	
	while ($row = mysqli_fetch_object($result))
	{
?>
	<p>
		<a href="download.php?id=<?php echo $row->id; ?>" target="_blank">
		    <?php echo $row->name; ?>
        </a>
	</p>
<?php } ?>

This will display all saved files in descending order (latest to oldest) in paragraphs. It will also display an anchor tag which when clicked will download the file.

Download BLOB images from MySQL:

To download the Blob file, simply create a file named download.php and write the following code in it:

<?php
	$conn = mysqli_connect("localhost", "root", "", "blob");
	$sql = "SELECT * FROM `images` WHERE `id` = " . $_GET["id"];
	$result = mysqli_query($conn, $sql);
	if (mysqli_num_rows($result) == 0)
	{
		die("File does not exists.");
	}
	$row = mysqli_fetch_object($result);
    header("Content-type: " . $row->type);
	echo $row->image;
?>

To convert the BLOB to base64 in PHP, do the following:

$base64 = "data:" . $row->type . ";base64," . base64_encode($row->image);
// output = data:image/jpeg;base64,9j/4AAQSkZJRgABAQAAAQABA...

Go ahead and click the file name from the list, you will see that file opened in a browser in a new tab. You can simply press Ctrl + S to download the file in your system. Using this method, you can also save your files from direct access from the URL. Since the files are not physically saved anywhere on the server, they will not be directly accessible from the URL.

And since the file can only be accessed via PHP function, you can put additional conditions like allowing download files to premium users only, etc.

[wpdm_package id=’1541′]

Prevent file access URL – htaccess

In this tutorial, we will teach you how you can prevent file access from URL using htaccess. Laravel is one of the most secured PHP frameworks, only if it’s used correctly. It can also be a huge security leak if not utilized properly. One problem I saw with many websites is that they move to production without securing their .env file.

You can always download the latest version of laravel from Github.

This file holds your site’s sensitive information like database passwords, email credentials, API keys, etc. By default, this sensitive information can easily be viewed by accessing the following URL:

https://your_domain/.env

If you see the content of your .env file, it clearly means that your website is open to hackers. To prevent this, open your .htaccess file and add the following highlighted code:

<IfModule mod_rewrite.c>

    <FilesMatch .env|.env.example>
        order allow,deny
        deny from all
    </FilesMatch>

    ...
</IfModule>

Now the .env file will be secured from the URL. You can refresh the page now and you will see a “403 Forbidden” error. So that’s how you can prevent direct file access from URL using htaccess.

Check out our social networking site project developed in Laravel.

Marquee Effect in HTML and Javascript – No library

In this article, we are going to teach you how you can display a custom marquee tag effect on your website without downloading any library. You can check the demo from here.

The problem with the HTML marquee tag

Right now, with the default <marquee> tag, you will notice that its animation is not smooth. This is the default behavior of browsers in how they treat marquee tag. Not having smooth animation is definitely not good for your user’s experience.

Solution

There are several solutions on the internet, but they all require downloading a jQuery library to achieve this. So we have created a more simple solution for this. You just need to give a unique ID or class to the node where you want to add the marquee effect.

<div id="marquee"></div>

Then write the following Javascript code to display the message like a marquee effect.

<script>
    (function () {
        const script = document.createElement("script");
        const s0 = document.getElementsByTagName("script")[0];
        script.async = true;
        script.src = "https://api.adnan-tech.com/public/js/at.js";
        script.setAttribute("crossorigin", "*");
        s0.parentNode.insertBefore(script, s0);

        script.onload = function () {
            at.loadMarquee("#marquee", "Your message goes here", {
                duration: 30, // seconds
                direction: "rtl"
            });
        };
    })();
</script>

at.loadMarquee is the function that does all the magic. It accepts three parameters:

  1. the selector (ID, class, or tag name)
  2. the message that will be displayed
  3. (optional) custom options. It has the following options:
    1. duration: (number) It is the number of seconds that will take to complete the effect.
      • type: number
      • default: 40
    1. direction: Its value can be either “rtl” (right to left) or “ltr” (left to right).
      • type: string
      • default: rtl
      • possible values: “rtl” or “ltr”

So that’s how you can display a custom marquee effect on your website without downloading any third-party library. This code has been tested on 10 websites having different frameworks. But if you are having any problems following this, feel free to contact us.

If this tutorial helps you, you can also check our Custom auto-complete view tutorial as well.

Premium projects bundle

Buy the 14 premium projects bundle for $1000. The bundle contains projects in the following technologies:

  • Node JS and Mongo DB
  • PHP & MySQL, Laravel
  • Vue JS
  • Android apps

Project’s list

  1. Single page chat application – Vue JS, Node JS, Mongo DB
  2. E-commerce single page application – Vue JS, Node JS, Mongo DB
  3. Chat app – Android, Web admin panel
  4. Email marketing tool – Laravel, Vue JS
  5. Financial ledger – Vue JS, Node JS, Mongo DB
  6. Image sharing web app – Node JS, Mongo DB / MySQL
  7. Movie ticket booking site – PHP & MySQL, MVC
  8. Realtime blog in Node JS and Mongo DB
  9. File transfer web app – Node JS + Mongo DB
  10. Realtime customer support chat widget
  11. Video streaming website – Node JS, Mongo DB
  12. Picture competition web app – Node JS, Mongo DB
  13. Questionnaire – Node JS, Mongo DB
  14. Blog website – Laravel, Google Adsense approved

You can buy each project separately too as well. Besides getting all the pro features of all 14 projects, we also provide additional services to them.

Secure payment

We allow users to make payments easily and securely using their bank accounts. You can contact us here and we will send you an invoice to make the payment.

Source code

Complete source code is included in all the projects. You will enjoy the pro version of each project.

Support

If you encounter any problem in installing the project or deployment, our technical support team is here. You can schedule a meeting with them and they will be able to assist you over AnyDesk or TeamViewer.

Use as a college project

If you are a student and are looking to get ready-to-go projects to learn about code and how real projects work, this will be beneficial for you. Also, Node JS projects are good learning points for students in advanced programming.

Customize as you want

Once you download the projects, you can customize them as per your needs. You can change the color theme, add new features to it, etc.

Get help in deployment

Once projects are set up in your local host, we will be here if you needed any help in deployment to the live server. For Node JS projects, we will assist you in deployment to Heroku. And for Mongo DB projects, we will help you with a deployment to mongodb.com.

Price: $1000

Out TrustPilot reviews

TrustPilot-reviews
TrustPilot-reviews