Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

phayes0289's avatar

How Do I enable Drag and Srop Sorting to be Saved Into the Database?

This is my Livewire Component for displaying shortcuts sorted by a database field named “sort order”.

<?php

namespace App\Http\Livewire;

use Livewire\Component;
use App\Models\Shortcut;

class ShortcutList extends Component
{

    public $search;
    public $sortBy = 'sortorder';
    public $sortDirection = 'asc';

    protected $queryString = [
        'search' => ['except' => ''],
        'sortBy',
        'sortDirection',
    ];

    public function render()
    {
        $shortcuts = Shortcut::query()
            ->filter(['search' => $this->search])
            ->orderBy($this->sortBy, $this->sortDirection)
            ->get();

        return view('livewire.shortcut-list', [
            'shortcuts' => $shortcuts,
        ])->layout('layouts.master');
    }

    public function sortBy($field)
    {
        if ($this->sortBy === $field) {
            $this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
        } else {
            $this->sortBy = $field;
            $this->sortDirection = 'asc';
        }
    }
}


This is my full page Livewire blade template that currently displays the shortcuts in the correct sort order.

@section('headerStyle')
<link rel="stylesheet" media="screen, print" href="{{ URL::asset('css/fa-solid.css') }}">
<link rel="stylesheet" media="screen, print" href="{{ URL::asset('css/fa-brands.css') }}">
<style>
.crd-controls{
    background-color: white;
    margin:8px;

}
    </style>
@stop

@section('content')
<main id="js-page-content" role="main" class="page-content">
    @component('common-components.breadcrumb')
    @slot('item1') Directory @endslot
    @slot('item2')  @endslot
    @endcomponent
    <div class="subheader">
        <h1 class="subheader-title">
            <i class='subheader-icon fal fa-plus-circle'></i> Shortcuts
            <small>
              
            </small>
        </h1>
    </div>

    <form wire:submit.prevent="search" class="mb-3">
            <!-- BEGIN input-group -->
            <div class="input-group input-group-lg mb-0 shadow-1 rounded">

                <input type="text" class="form-control shadow-inset-2" id="search" name="search"
                       aria-label="type 2 or more letters" placeholder="Search shortcuts..." value="{{ $search }}">
                <div class="input-group-append">
                    <button class="btn btn-primary hidden-sm-down waves-effect waves-themed" type="submit"><i
                            class="fal fa-search mr-lg-2"></i><span class="hidden-md-down">Search</span></button>

                </div>
            </div>
            <div align="center" class="mb-0"> [ <a href="{{route('shortcut.index')}}">Clear Search</a>]</div>
            <!-- END input-group -->
        </form>


            <table class="table table-bordered">
                <thead>
                <tr>
                    <th> Shortcut</th>
                    <th style="width:50px">Sort Order</th>
                    <th style="width:150px">Tools</th>
                </tr>
                </thead>
                <tbody>
                @foreach ($shortcuts as $shortcut)
                <tr>
                    <td><h5 class="card-title mb-1">{{$shortcut->title}}</h5>
                        <p class="card-text"><a href="{{$shortcut->title}}">{{$shortcut->url}}</a></p></td>
                    <td><input type="text" value="{{ $shortcut->sortorder }}" style="width:50px"></td>
                        <td class="text-right"><!-- Create New -->
                        <a class="btn btn-primary btn-icon waves-effect waves-themed" href="#!" role="button" data-toggle="modal" data-target="#custom_shortcut"><i
                                class="fal fa-fw fa-plus"></i></a>
                        <!-- Edit -->
                        <a class="btn  btn-primary btn-icon waves-effect waves-themed" href="/posts/edit/" role="button" data-toggle="modal" data-target="#editshortcut_{{$shortcut->id}}"><i
                                class="fal fa-fw fa-edit"></i></a>
                        <!-- Trash -->
                        <a class="btn btn-primary btn-icon waves-effect waves-themed" href="{{route('shortcut.destroy', $shortcut->id)}}" role="button"><i
                                class="fal fa-fw fa-trash"></i></a></td>
                </tr>


                <!-- Modal Edit -->
                    <form action="{{route('shortcut.update', $shortcut->id)}}" method="post" name="form1" id="form1" class="needs-validation" >
                    @csrf
                    @method("PUT")

                    <!-- Modal center Large -->
                    <div class="modal fade" id="editshortcut_{{$shortcut->id}}" tabindex="-1" role="dialog" aria-hidden="true">
                        <div class="modal-dialog modal-lg modal-dialog-centered" role="document">
                            <div class="modal-content">
                                <div class="modal-header">
                                    <h5 class="modal-title">Edit Shortcut</h5>
                                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                                        <span aria-hidden="true"><i class="fal fa-times"></i></span>
                                    </button>
                                </div>
                                <div class="modal-body">
                                    <div class="col-xl-12 col-md-12 mt-2">
                                        <label class="form-label" for="title">Shortcut Title</label>
                                        <input type="text" id="title" name="title" class="form-control" value="{{$shortcut->title}}">


                                        <label class="form-label" for="url">Shortcut URL</label>
                                        <input type="text" id="url" name="url" class="form-control" value="{{$shortcut->url}}">



                                    </div>
                                </div>
                                <div class="modal-footer">
                                    <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
                                    <button type="submit" class="btn btn-primary">Save changes</button>
                                </div>
                            </div>
                        </div>
                    </div>


                </form>


                @endforeach
                </tbody>
            </table>


        <!-- Modal Create -->
        <form action="{{route('shortcut.create')}}" method="post" name="form1" id="form1" class="needs-validation" >

            @csrf
            <!-- Modal center Large -->
            <div class="modal fade" id="custom_shortcut" tabindex="-1" role="dialog" aria-hidden="true">
                <div class="modal-dialog modal-lg modal-dialog-centered" role="document">
                    <div class="modal-content">
                        <div class="modal-header">
                            <h5 class="modal-title">Add a Shortcut</h5>
                            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                                <span aria-hidden="true"><i class="fal fa-times"></i></span>
                            </button>
                        </div>
                        <div class="modal-body">
                            <div class="col-xl-12 col-md-12 mt-2">
                                <label class="form-label" for="title">Shortcut Title</label>
                                <input type="text" id="title" name="title" class="form-control" value="" required>


                                <label class="form-label" for="url">Shortcut URL</label>
                                <input type="text" id="url" name="url" class="form-control" value="" required >



                            </div>
                        </div>
                        <div class="modal-footer">
                            <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
                            <button type="submit" class="btn btn-primary">Save changes</button>
                        </div>
                    </div>
                </div>
            </div>


        </form>

        @if ($errors->any())
        {!! implode('', $errors->all('<div>:message</div>')) !!}
    @endif
</main>
@stop

@section('footerScripts')

@stop

I want to be able to click and drag a shortcut, in the form of an entire row in order to set a new sort order in the database for all the shortcuts. How can I do that?

0 likes
1 reply
LaryAI's avatar
Level 58

To enable drag and drop sorting and save the new sort order into the database, you can use the SortableJS library along with Livewire. Here's how you can implement it:

  1. Install SortableJS and Livewire using npm:
npm install sortablejs livewire
  1. Import SortableJS and Livewire in your JavaScript file:
import Sortable from 'sortablejs';
import { Livewire } from 'livewire';

window.Sortable = Sortable;
window.Livewire = Livewire;
  1. Update your Livewire component to include the necessary JavaScript and Livewire methods:
use Livewire\Component;
use App\Models\Shortcut;

class ShortcutList extends Component
{
    public $search;
    public $sortBy = 'sortorder';
    public $sortDirection = 'asc';

    protected $queryString = [
        'search' => ['except' => ''],
        'sortBy',
        'sortDirection',
    ];

    public function render()
    {
        $shortcuts = Shortcut::query()
            ->filter(['search' => $this->search])
            ->orderBy($this->sortBy, $this->sortDirection)
            ->get();

        return view('livewire.shortcut-list', [
            'shortcuts' => $shortcuts,
        ])->layout('layouts.master');
    }

    public function sortBy($field)
    {
        if ($this->sortBy === $field) {
            $this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
        } else {
            $this->sortBy = $field;
            $this->sortDirection = 'asc';
        }
    }

    public function updatedShortcuts($newSortOrder)
    {
        foreach ($newSortOrder as $index => $shortcutId) {
            Shortcut::where('id', $shortcutId)->update(['sortorder' => $index + 1]);
        }
    }
}
  1. Update your Livewire blade template to include the necessary HTML and Livewire directives:
<!-- ... -->

<table class="table table-bordered" wire:sortable="updatedShortcuts">
    <thead>
        <tr>
            <th>Shortcut</th>
            <th style="width:50px">Sort Order</th>
            <th style="width:150px">Tools</th>
        </tr>
    </thead>
    <tbody wire:sortable="updatedShortcuts">
        @foreach ($shortcuts as $shortcut)
        <tr wire:sortable.item="{{ $shortcut->id }}" wire:key="shortcut-{{ $shortcut->id }}">
            <td>
                <h5 class="card-title mb-1">{{ $shortcut->title }}</h5>
                <p class="card-text"><a href="{{ $shortcut->title }}">{{ $shortcut->url }}</a></p>
            </td>
            <td>
                <input type="text" value="{{ $shortcut->sortorder }}" style="width:50px">
            </td>
            <td class="text-right">
                <!-- ... -->
            </td>
        </tr>
        @endforeach
    </tbody>
</table>

<!-- ... -->
  1. Add the necessary JavaScript code to initialize SortableJS and Livewire:
document.addEventListener('livewire:load', function () {
    let shortcutsTable = document.querySelector('.table tbody');
    let sortable = Sortable.create(shortcutsTable, {
        handle: '.sortable-handle',
        animation: 150,
        onEnd: function (event) {
            let newSortOrder = Array.from(shortcutsTable.children).map(function (row) {
                return row.getAttribute('wire:sortable.item');
            });
            Livewire.emit('updatedShortcuts', newSortOrder);
        }
    });
});

With these changes, you should now be able to click and drag a shortcut row to set a new sort order. The Livewire updatedShortcuts method will be triggered when the sorting is finished, and it will update the sort order in the database for all the shortcuts.

Please or to participate in this conversation.