Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Laravel: drag-and-drop repositioning with auto-save of DB entries

Laravel: drag-and-drop repositioning with auto-save of DB entries

Use case: Database entries are represented in a table. By grabbing and moving a row up or down the table, you can change the entries' order/position. The changes are submitted automatically via ajax.

  • Uses jQueryUI (custom download: sortable is needed)
  • newly created elements are added to the top (see route /jobs/create)
// head
{{ HTML::style('scripts/vendor/jquery-ui/jquery-ui.min.css') }}
// handle in each table row
<a class="handle">Move</a>
// end of body
@section('additional-scripts')
{{ HTML::script('scripts/vendor/jquery-ui/jquery-ui.min.js') }}
<script>
$('table.db tbody').sortable({
'containment': 'parent',
'revert': true,
helper: function(e, tr) {
var $originals = tr.children();
var $helper = tr.clone();
$helper.children().each(function(index) {
$(this).width($originals.eq(index).width());
});
return $helper;
},
'handle': '.handle',
update: function(event, ui) {
$.post('{{ route('jobs-reposition') }}', $(this).sortable('serialize'), function(data) {
if(!data.success) {
alert('Whoops, something went wrong :/');
}
}, 'json');
}
});
$(window).resize(function() {
$('table.db tr').css('min-width', $('table.db').width());
});
</script>
@endsection
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateJobs extends Migration {
public function up()
{
Schema::create('jobs', function($table)
{
$table->increments('id');
$table->string('profession', '256');
$table->text('description');
$table->integer('position')->unsigned();
$table->boolean('visible')->default(false);
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('jobs');
}
}
<?php
Route::post('/jobs/create', function(){
// Validation [...]
Jobs::increment('position');
$jobs = new Jobs;
// [...]
$jobs->position = 1;
$jobs->save();
return Redirect::route('home');
}
});
Route::post('/jobs/reposition', function()
{
if(Input::has('item'))
{
$i = 0;
foreach(Input::get('item') as $id)
{
$i++;
$item = Jobs::find($id);
$item->position = $i;
$item->save();
}
return Response::json(array('success' => true));
}
else
{
return Response::json(array('success' => false));
}
});
@ChristopherDosin

This comment has been minimized.

Copy link

commented Sep 16, 2014

Great snippet !
Could you show us your database structure ?

@tillsanders

This comment has been minimized.

Copy link
Owner Author

commented Jan 12, 2015

Sry, I didn't receive any notifications about your comment. I added my DB structure.

@gildniy

This comment has been minimized.

Copy link

commented Aug 25, 2015

Wow I like this, am gonna build a small repository based on the idea!

@jesseleite

This comment has been minimized.

Copy link

commented Aug 31, 2015

Thanks for sharing!

I'm curious though... If you are saving position right on the Job itself, then changing position of one item means a bunch of other items' positions will be edited in the process. What if you want to offer ability to also order by updated_at? Now ordering by updated_at won't show an accurate representation of actual Job edits, because they'll be re-timestamped every time you perform a reposition.

I guess you could have a Model observer which would listen for position-only item edits, and pass in the original updated_at to prevent it from being re-timestamped.

How would you handle this?

@tillsanders

This comment has been minimized.

Copy link
Owner Author

commented Nov 9, 2015

@jesseleite: Very interesting question. To be honest, this script was intended for a small website, so it is not that sophisticated. However, I think the best solution would be to create an additional table. What do you think?

@Photoshopper

This comment has been minimized.

Copy link

commented Jun 10, 2016

How did you get access to Input::get('item') ?
I can't see where you pass data to the route "jobs/reposition"

@FadiNouh1

This comment has been minimized.

Copy link

commented Sep 6, 2016

thanks for this nice explanation ,
can you also put the PHP (html tags) code in the Jobs file?
@Photoshopper : it's from JQueryui with "serialize" method , but you have to call each item (li for example) with id="item_{{jobs_id}}" and then you will get it as array of item[]

@exigopro

This comment has been minimized.

Copy link

commented Oct 20, 2016

Could you maybe post a full working example by using your method please? :)

@brandy

This comment has been minimized.

Copy link

commented Dec 2, 2016

@tillsanders any chance you could post the table just to make sure mines close ? I'm looking for a simple way to do this, however I'm new to JQuery and have no idea what the table would look like

@poseso

This comment has been minimized.

Copy link

commented Jan 19, 2017

Could you post a full working example by using your method please? 👍

@danpalmieri

This comment has been minimized.

Copy link

commented Sep 1, 2017

Thanks for sharing!

@danpalmieri

This comment has been minimized.

Copy link

commented Sep 4, 2017

The code generated is:
item[]=4&item[]=8&item[]=6&item[]=9
is that right?

@robertnicjoo

This comment has been minimized.

Copy link

commented May 8, 2018

would you mind share table.db tbody (view table) to see what is where exactly and which part of JavaScript get what elements?

Thanks.

@eiphyu154

This comment has been minimized.

Copy link

commented Sep 5, 2019

Input::has('item') is false why?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.