Öncelikle o plugini değil şu plugini kullanıyorsunuz:
https://github.com/ilikenwf/nestedSortable
Ötekisi eski ve bu tavsiye ettiğim işimize yarayacak birkaç özelliğe sahip. Ayrıca aktif diyebiliriz.
Görsel tasarıma bakmayın, hepsinin yaptığı aynı. Kendi ihtiyacınıza göre değiştirebilirsiniz.
Demo için:
http://ilikenwf.github.io/example.html
Öncelikle işlem yaptığımız modelin Category olduğunu varsayıyorum. Alanlar da id(primary), parent_id(int), depth(int) ve name(varchar)
Bu yapı adjacency list olarak adlandırılıyor. Araştırma yapabilirsiniz. depth alanıda derinlik değerini tutacağız. Bazı işlemler yaparken işinize yardımcı olacaktır.
Derinlik şu şekilde değer alıyor:
Kategori 0
Alt Kategori 1
Alt Kategori 1
Alt Kategori 2
Alt Kategori 1
nestedSortable plugininin toArray şeklinde işimizi çok kolaylaştıracak bir yöntemi var:
var data = $('ol.sortable').nestedSortable('toArray', {startDepthCount: 0});
Bu bize şöyle bir çıktı verecek:
[
{"id":null,"parent_id":null,"depth":0,"left":1,"right":14},
{"id":"2","parent_id":null,"depth":1,"left":2,"right":9},
{"id":"4","parent_id":"2","depth":2,"left":3,"right":6},
{"id":"6","parent_id":"4","depth":3,"left":4,"right":5},
{"id":"5","parent_id":"2","depth":2,"left":7,"right":8},
{"id":"7","parent_id":null,"depth":1,"left":10,"right":11},
{"id":"3","parent_id":null,"depth":1,"left":12,"right":13}
]
Biz bu JSON veriyi PHP içinde json_decode($json, true) şeklinde diziye çevirdiğimizde şöyle bir PHP dizisi elde etmiş olacağız:
Array
(
[0] => Array
(
[id] =>
[parent_id] =>
[depth] => 0
=> 1
=> 14
)
[1] => Array
(
[id] => 2
[parent_id] =>
[depth] => 1
=> 2
=> 9
)
[2] => Array
(
[id] => 4
[parent_id] => 2
[depth] => 2
=> 3
=> 6
)
[3] => Array
(
[id] => 6
[parent_id] => 4
[depth] => 3
=> 4
=> 5
)
[4] => Array
(
[id] => 5
[parent_id] => 2
[depth] => 2
=> 7
=> 8
)
[5] => Array
(
[id] => 7
[parent_id] =>
[depth] => 1
=> 10
=> 11
)
[6] => Array
(
[id] => 3
[parent_id] =>
[depth] => 1
=> 12
=> 13
)
)
Burada left, right değerleri işimize yaramıyor. Bu değerler nested set modeli için. Bizim model adjacency olduğu için gerek yok.
Şimdi gelelim veritabanından gelen veri ile nestedSortable oluşturmaya.
nestedSortable için temel HTML yapısını verebilen şöyle bir sınıf hazırladım:
class NestedSortable
{
public function build($items, $parentId = null, & $i = 0)
{
$result = null;
$i = count($items);
foreach ($items as $item) {
if ($item->parent_id == $parentId) {
// Burada item_1 gibi li elementine kategori id'sini yazdırıyoruz.
// nestedSortable kaynağına baktım. Bu şekilde id'yi alıp hiyerarşiyi oluşturuyor. Yani gerekli.
$result .= '<li id="item_'.$item->id.'">';
// Burada data-name ile kategori adının nestedSortable'nin toArray yöntemiyle
// elde edilen çıktıya name anahtarı dahil edilmesini sağlıyoruz.
// Kaynak koduna baktım, li içinde div elementinin data-* değerlerini dahil ediyor.
// Yani siz buraya ekleme yapabilirsiniz.
$result .= '<div data-name="'.$item->name.'">' . $item->name . '</div>';
$result .= $this->build($items, $item->id, $i);
$result .= '</li>';
$i--;
}
}
$leadClass = $i == 1 ? 'sortable' : null;
return !is_null($result) ? '<ol class="' . $leadClass . '">' . $result . '</ol>' : null;
}
}
Örneğin:
$categories = Category::all();
$sortableHTML = (new NestedSortable)->build($categories);
Bu bize şöyle bir HTML çıktı veriyor:
<ol class="sortable">
<li id="item_1">
<div data-name="Bilgisayar">Bilgisayar</div>
<ol class="">
<li id="item_2">
<div data-name="Dizüstü">Dizüstü</div>
<ol class="">
<li id="item_3">
<div data-name="Oyuncu Dizüstü">Oyuncu Dizüstü</div>
</li>
<li id="item_4">
<div data-name="Workstation">Workstation</div>
</li>
</ol>
</li>
</ol>
</li>
</ol>
Plugini de kullandığımızda istediğimiz sonuca ulaşmış oluyoruz:
$('ol.sortable').nestedSortable({
handle: 'div',
items: 'li',
toleranceElement: '> div',
// İlk element mecburi ama null, bu seçenek onu çıkarmamızı sağlıyor.
// Karıştırmayın, root element sistem için gerekli, bizim kategorilerimizle ilgisi yok yani
// Sonuçta ağacın dalları ve yaprakları tutabilmesi için bir başlangıç gövdesi olmak zorunda
excludeRoot:true
});
Sayfada bir düğme aracılığı ile veriyi ajax ile bir rotaya gönderelim:
<ol class="sortable">
<li id="item_1">
<div data-name="Bilgisayar">Bilgisayar</div>
<ol class="">
<li id="item_2">
<div data-name="Dizüstü">Dizüstü</div>
<ol class="">
<li id="item_3">
<div data-name="Oyuncu Dizüstü">Oyuncu Dizüstü</div>
</li>
<li id="item_4">
<div data-name="Workstation">Workstation</div>
</li>
</ol>
</li>
</ol>
</li>
</ol>
<button type="button" id="update">Güncelle</button>
<script>
$(function() {
var $updateBtn = $('#update');
$('ol.sortable').nestedSortable({
handle: 'div',
items: 'li',
toleranceElement: '> div',
excludeRoot:true
});
$updateBtn.on('click', function() {
var data = $('ol.sortable').nestedSortable('toArray', {startDepthCount: 0});
$.ajax({
url:'/admin/categories/reorder',
method:'POST',
data: data,
success: function(res) {
alert(res.message);
}
});
});
});
</script>
CategoryController.php:
public function reorder(Request $request)
{
// nestedSortable'den gelen veriyi diziye çevirelim
// json_decode'nin 2. parametresi assoc = true json ile gelen objelerin de array olmasını sağlayacak
// yani tamamen saf bir array elde etmiş olacağız.
$sortedData = json_decode($request->get('data'), true);
// Kullanmayacağımız left ve right değerlerini kaldıralım
foreach($sortedData as & $sorted) {
unset($sorted['left'], $sorted['right']);
}
// Gelen veri zaten tablomuzdaki ile eşdeğer olduğu için hiç zahmete girmeden:
Category::truncate();
Category::insert($sortedData);
return response()->json([
'message' => 'Güncellendi';
]);
}
Ne yazık ki uzun, karışık bir konu ve kendi sisteminize göre bir çok yeri değiştirmeniz gerekebilir.