Skip to content

Instantly share code, notes, and snippets.

@treetop1500
Created March 22, 2017 14:12
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save treetop1500/c0412aac990029425d6dbb193cf1b6cd to your computer and use it in GitHub Desktop.
Save treetop1500/c0412aac990029425d6dbb193cf1b6cd to your computer and use it in GitHub Desktop.
Sortable Drag and Drop Entities with Symfony and HTML5
var adminNs =
{
initDraggableEntityRows: function() {
var dragSrcEl = null; // the object being drug
var startPosition = null; // the index of the row element (0 through whatever)
var endPosition = null; // the index of the row element being dropped on (0 through whatever)
var parent; // the parent element of the dragged item
var entityId; // the id (key) of the entity
function handleDragStart(e) {
dragSrcEl = this;
entityId = $(this).attr('rel');
dragSrcEl.style.opacity = '0.4';
parent = dragSrcEl.parentNode;
startPosition = Array.prototype.indexOf.call(parent.children, dragSrcEl);
console.log("start: "+startPosition);
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', this.innerHTML);
console.log(entityId);
}
function handleDragOver(e) {
//console.log('drag over: '+ e.target);
if (e.preventDefault) {
e.preventDefault(); // Necessary. Allows us to drop.
}
e.dataTransfer.dropEffect = 'move'; // See the section on the DataTransfer object.
return false;
}
function handleDragEnter(e) {
//console.log('drag enter: '+ e.target);
this.classList.add('over');
}
function handleDragLeave(e) {
//console.log('drag leave: '+ e.target);
this.classList.remove('over'); // this / e.target is previous target element.
}
function handleDrop(e) {
//console.log('drop: '+ e.target);
//console.log(e.currentTarget);
//console.log(dragSrcEl);
if (e.stopPropagation) {
e.stopPropagation(); // stops the browser from redirecting.
}
// Don't do anything if dropping the same column we're dragging.
if (dragSrcEl != this) {
endPosition = Array.prototype.indexOf.call(parent.children, this);
console.log("end: "+endPosition);
// Set the source column's HTML to the HTML of the column we dropped on.
dragSrcEl.innerHTML = this.innerHTML;
this.innerHTML = e.dataTransfer.getData('text/html');
// do the ajax call to update the database
$.ajax({
url: '/admin/productcategory/sort/'+entityId+'/'+endPosition,
})
.done(function(res) {
$("table.sortable tbody").replaceWith($(res).find("table.sortable tbody"));
})
.fail(function(err) {
alert("An error occurred while sorting. Please refresh the page and try again.")
})
.always(function() {
adminNs.initDraggableEntityRows();
});
}
return false;
}
function handleDragEnd(e) {
//console.log('drag end: '+ e.target);
this.style.opacity = '1'; // this / e.target is the source node.
[].forEach.call(rows, function (row) {
row.classList.remove('over');
});
}
var rows = document.querySelectorAll('table.sortable > tbody tr');
[].forEach.call(rows, function(row) {
row.addEventListener('dragstart', handleDragStart, false);
row.addEventListener('dragenter', handleDragEnter, false);
row.addEventListener('dragover', handleDragOver, false);
row.addEventListener('dragleave', handleDragLeave, false);
row.addEventListener('drop', handleDrop, false);
row.addEventListener('dragend', handleDragEnd, false);
});
},
/**
* Primary Admin initialization method.
* @returns {boolean}
*/
init: function() {
...
this.initDraggableEntityRows();
return true;
}
};
$(function() {
adminNs.init();
});
services:
...
gedmo.listener.sortable:
class: Gedmo\Sortable\SortableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ "@annotation_reader" ] ]
<table class="sortable">
<thead>
<tr>
<th>&nbsp;</th>
<th>Id</th>
<th>Sort</th>
<th>Title</th>
</tr>
</thead>
<tbody>
{% for category in productCategories %}
<tr draggable="true" rel="{{ category.id }}">
<td><i class="fa fa-arrows"></i></td>
<td><a href="{{ path('admin_productcategory_show', { 'id': category.id }) }}">{{ category.id }}</a></td>
<td>{{ category.position }}</td>
<td>{{ category.title }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<?php
namespace Common\ContentBundle\Entity;
...
use Gedmo\Mapping\Annotation as Gedmo;
class ProductCategory
{
...
/**
* @var integer $position
*
* @Gedmo\Sortable()
* @ORM\Column(name="position", type="integer")
*/
private $position;
...
/**
* @return int
*/
public function getPosition()
{
return $this->position;
}
/**
* @param int $position
*/
public function setPosition($position)
{
$this->position = $position;
}
...
}
<?php
namespace Common\ContentBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Common\ContentBundle\Entity\ProductCategory;
/**
* ProductCategory controller.
*/
class ProductCategoryController extends Controller
{
...
/**
* Resorts an item using it's doctrine sortable property
* @param integer $id
* @param integer $position
* @return \Symfony\Component\HttpFoundation\Response
*/
public function sortAction($id, $position)
{
$em = $this->getDoctrine()->getManager();
$productCategory = $em->getRepository('CommonContentBundle:ProductCategory')->find($id);
$productCategory->setPosition($position);
$em->persist($productCategory);
$em->flush();
$request = new Request();
return $this->indexAction($request);
}
}
admin_productcategory_sort:
path: /admin/productcategory/sort/{id}/{position}
defaults: { _controller: "CommonContentBundle:ProductCategory:sort" }
methods: GET
[draggable] {
user-select: none;
-webkit-user-drag: element;
cursor: move;
}
table.sortable {
i.fa.fa-arrows {
color: #999;
}
tr.over {
border: 1px dashed $primary-color;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment