Mini-course: How To Create Admin Panel in Laravel 5.4


In this chapter we will learn how to delete the entry – both single one and in bulk.

Delete Form and Destroy Method

I will repeat the code from resources/views/authors/index.blade.php where we have Delete button:

    <td>
        <a href="{{ route('authors.edit', $author->id) }}" class="btn btn-default">Edit</a>
        <form action="{{ route('authors.destroy', $author->id) }}" method="POST"
              style="display: inline"
              onsubmit="return confirm('Are you sure?');">
            <input type="hidden" name="_method" value="DELETE">
            {{ csrf_field() }}
            <button class="btn btn-danger">Delete</button>
        </form>
    </td>

As you can see, there’s a form with POST method and hidden field _method=DELETE. As Laravel resource-controllers work, this form submission will automatically go controller’s method destroy(). So we fill it in app/Http/Controllers/AuthorsController.php:

    public function destroy($id)
    {
        $author = Author::findOrFail($id);
        $author->delete();
        return redirect()->route('authors.index')->with(['message' => 'Author deleted successfully']);
    }

Basically, that’s it. You can add more security checks here – like if logged-in user has right to delete this record (but we will talk about roles-permissions a little later in next lesson).


Soft Deletes

Laravel has a really convenient way of “hiding” records instead of actually deleting, in other words – making them inactive and not show in the lists or other Eloquent functions. This function is called soft deleting, here’s how it works.

First, in your migrations you need to add timestamp field deleted_at, by using softDeletes() method. In our case we edit the initial migration, like this:

    Schema::create('authors', function (Blueprint $table) {
        $table->increments('id');
        $table->string('first_name');
        $table->string('last_name');
        $table->timestamps();
        $table->softDeletes();
    });

Now, in the Model file we need to do two things – add SoftDeletes trait and set $dates property:

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Author extends Model
{
    use SoftDeletes;

    protected $fillable = ['first_name', 'last_name'];

    protected $dates = ['deleted_at'];

}

Now, whenever we call delete() method on the entry (like $author->delete();), it will actually fill in deleted_at field with current timestamp, which will mean that record is soft-deleted. In other words, if deleted_at is NULL, then record is “alive” and not deleted.

The biggest question is how to restore a soft-deleted record? Of course, you can set deleted_at to NULL manually in the database, but within Laravel app you just need to find a record using withTrashed() method, and then call restore() on it:

    $author = Author::withTrashed()->findOrFail($id);
    $author->restore();

If, when using Soft Deletes, you still want to delete the record permanently, then you should use method forceDelete().


Bulk Deleting with Checkboxes

If you want to delete several entries at once, it’s really inconvenient to do by clicking Delete buttons and then get redirected every time. Of course, it makes much more sense to have a column with checkboxes to tick and then a “mass-delete” button at the bottom of the table. So let’s do exactly that.

    <td><input type="checkbox" class="checkbox_delete" 
       name="entries_to_delete[]" value="{{ $author->id }}" /></td>

Next, let’s add a “check all” checkbox in the first column heading:

    <th>
        <input type="checkbox" class="checkbox_all">
    </th>    

Now, let’s add some JavaScript to make it work – in fact, let’s use JQuery for it.

resources/views/layouts/app.blade.php:

    <!-- Scripts -->
    <script src="/js/app.js"></script>
    <script src="https://code.jquery.com/jquery-3.1.1.js"></script>

    @yield('scripts')

Here we load jQuery and create a section scripts which can be filled individually for every Blade view that extends layout.app. So, in our resources/views/authors/index.blade.php we add this at the bottom:

@section('scripts')
    <script>
        $(".checkbox_all").click(function(){
            $('input.checkbox_delete').prop('checked', this.checked);
        });
    </script>
@endsection

Visually, we have something like this:

laravel checkboxes

Now, how do we delete the checked entries? To be honest, there are multiple ways to do it, because it involves custom coding – there’s no such feature in Laravel default.

First, we need to create a form with a special button to delete entries by checkboxes. Not only that, we cannot use those checkboxes directly as a form, cause each line in the table contains a form for deletion, and we cannot create a form within a form, by definition of browser behavior. So, we create a form with hidden field and button under the table:

    ...
    </table>
    <form action="{{ route('authors.mass_destroy') }}" method="post"
          onsubmit="return confirm('Are you sure?');">
        {{ csrf_field() }}
        <input type="hidden" name="_method" value="DELETE">
        <input type="hidden" name="ids" id="ids" value="" />
        <input type="submit" value="Delete selected" class="btn btn-danger" />
    </form>

Now, we create a Route entry to support this form submit – in routes/web.php:

    Route::delete('authors/mass_destroy', 'AuthorsController@massDestroy')->name('authors.mass_destroy');
    Route::resource('authors', 'AuthorsController');

Note that this Route::delete() entry must go before the Route::resource for the same controller, otherwise it gets overridden and doesn’t work.

Now, visually we have this:

laravel mass-delete

Next step – to fill that hidden input field ids – we need to do it with jQuery – on any change of any checkbox, we regenerate the list which will be comma-separated.

So this is now our JavaScript section at the bottom of resources/views/authors/index.blade.php

    <script>
        function getIDs()
        {
            var ids = [];
            $('.checkbox_delete').each(function () {
                if($(this).is(":checked")) {
                    ids.push($(this).val());
                }
            });
            $('#ids').val(ids.join());
        }

        $(".checkbox_all").click(function(){
            $('input.checkbox_delete').prop('checked', this.checked);
            getIDs();
        });

        $('.checkbox_delete').change(function() {
            getIDs();
        });
    </script>    

I don’t want to get to deep into front-end stuff, because this course is about Laravel – you can do this part your way, differently, or maybe even without jQuery, that’s your choice.

In the end of this we have ids filled in before submitting the mass-delete form. Now let’s get back to our controller and process it.

app/Http/Controllers/AuthorsController.php:

    public function massDestroy(Request $request)
    {
        $authors = explode(',', $request->input('ids'));
        foreach ($authors as $author_id) {
            $author = Author::findOrFail($author_id);
            $author->delete();
        }
        return redirect()->route('authors.index')->with(['message' => 'Authors deleted successfully']);
    }

Not much to explain here – we just get all IDs in comma-separated string, then transform then into array and loop through that array to delete entries one by one.

That’s it for the deleting process in our course. Basically, we’ve covered all the CRUD operations – create, read, update and delete. In fact, there’s one method that we haven’t touched – it’s show() method in Controller, but, in my personal opinion, edit() method covers almost the same functionality, if you want to take a look at the entry in detail, just in a format of a form.

We have one more topic to cover – but it’s a big one: roles and permissions. Are you ready? Let’s go!