【CakePHP】ドラッグアンドドロップでリストの並び替え→DB保存

# はじめに
cakephpでの実装ですが、ロジックは
Laravelやphp以外の他の言語であっても応用できると思います。

記事一覧画面、並び替え画面、コントローラ、この3つのファイルを編集していきます。


大まかな流れとしては
①記事一覧画面で「並び替え」ボタンを押すことで並び替え画面に遷移。
②ドラッグアンドドロップで好きな順番に並び替え
③「保存」ボタンを押すことで記事一覧画面に戻りつつ、並び替えられた状態。
です。

# view
記事一覧画面

```php5:index.ctp
<tbody>
    <?php 
	   foreach ($article_list as $key => $article)
    ?>
    <tr id='<?php echo $key; ?>'>
	   <td><?php echo $key+1; ?></td>
       <td><?php echo $article['Article']['id']; ?></td>							
	   <td class="actions dragged"><span></span></td>
    </tr>
	<?php }  ?>

<a id="sort-button" href="/article/sort">並び替え</a>
</tbody>
```

記事一覧画面です。DBから記事を引っ張ってきて並べています。



```php5:index.ctp
<a id="sort-button" href="/article/sort">並び替え</a>
```


このリンクを押すと並び替え画面に遷移します。

並び替え画面

```php5:sort.ctp
<?php echo $this->Form->create('null',['url' => ['controller' => 'Article','action' => 'sort_by'],'type' => 'post',]);?>
<tbody class="sortable">
    <?php 
	   foreach ($article_list as $key => $article)
    ?>
    <tr id='<?php echo $key; ?>'>
	   <td><?php echo $key+1; ?></td>
       <td><?php echo $article['Article']['id']; ?></td>							
	   <td class="actions dragged"><span></span></td>
    </tr>
	<?php }  ?>

<div id="submit">
		<?php echo $this->Form->button('保存する', ['type' => 'submit']); ?>
</div>
</tbody>
```


並び替え画面も基本的には一覧画面と同じですが、並び順を保存するための
フォームが追加されています。submitすることで、並び順を保存するとともに、ふたたび一覧画面に戻ります。



# controller
```php5:ArticleController.php
//記事一覧画面
    public function article_list() {
        $article_list = array();
                
        $article_list = $this->Article->find('all', array('order'=>Array('order' => 'asc', 'created' => 'desc')));

        $this->set(compact('article_list'));

    }

//並び替え画面
public function sort() { 
        //並び順、orderの昇順,登録日の降順
        $article_list = $this->Article->find('all', array('order'=>Array('order' => 'asc', 'created' => 'desc')));

        $this->set(compact('article_list'));
    }



//並び替え実行
public function sort_by() {     
        $result = $_POST['result'];
        $result = explode(',', $result);

        $article_list = $this->Article->find('all', array('order'=>'order asc'));

        for ($i = 0; $i < count($article_list); $i++) {
            $num = $result[$i];
            $article_list[$num]['Article']['order'] = $i;
            $this->Article->save($article_list[$num]);
        }

        $this->redirect('article_list');
        return;
    }
```



以上が今回扱うファイルです。ではここから実装に入っていきます。



```php5:sort.ctp

//sort.ctpの下部に追加
<script>
	$(".sortable").sortable({
		placeholder: 'ui-state-highlight',
					 start: function(event, ui){
							ui.placeholder.height(ui.helper.outerHeight());
    			     },
					helper: fixPlaceHolderWidth,
					handle: '.dragged',
					cursor: 'move',
	});
	
   $(".sortable").disableSelection();
   
   $("#submit").click(function() {
		var result = $(".sortable").sortable("toArray");
		$("#result").val(result);
		$("form").submit();
   });
</script>

```
jquery-uiのsortableを導入することで、ドラッグアンドドロップで順番を入れ替えられるようになっています。
sortableの導入方法や使い方はこちらの記事を参照してください。

[ドラッグ＆ドロップで要素を並び替えることができるSortable.js](https://spyweb.media/2017/11/02/js-drag-and-drop-sortable-plugin/)


そしてsubmitにより並び替えた順番をArticleコントローラのsort_byアクションに送信しています。



```php5:ArticleController.php
//並び替え実行
public function sort_by() {     
        $result = $_POST['result'];  //postされた並び順を受け取る
        //$_POST['result']内にはテーブル内要素のIDが上から順番コンマ区切りの文字列で格納されています。
        //1,2,3,4 だったものを 4,3,1,2　と並び替えれば　$_POST['result']="4,3,1,2"です。
        

        $result = explode(',', $result);  //配列に格納

        $article_list = $this->Article->find('all', array('order'=>'order asc'));
        //orderは順番を定義するArticleのカラムです。一覧画面ではこの数字の少ない順に上から表示しています。


        for ($i = 0; $i < count($article_list); $i++) {    //この部分は以下詳しく解説します。
            $num = $result[$i];
            $article_list[$num]['Article']['order'] = $i;
            $this->Article->save($article_list[$num]);
        }

        $this->redirect('article_list');
        return;
    }
```



for文の部分が理解しづらいので詳しく見ていきましょう。
例えばもともと1,2,3,4と並んでいたものを4,2,1,3と並び替えた場合


```php5:ArticleController.php
//繰り返し一回目の処理
//$resultは並び替えられpostされてきた[4,3,1,2]が入っています。
for ($i = 0; $i < count($article_list); $i++) {   
            $num = $result[$i];   //[4,3,1,2]の[1]番目なので、仮ナンバー$numには4が入ります。

            $article_list[$num]['Article']['order'] = $i;  //テーブル上で4番目の要素のorderが[1]になります。

            $this->Article->save($article_list[$num]);  //DBに保存します。
        }
```


```php5:ArticleController.php
//繰り返し二回目の処理
//$resultは並び替えられpostされてきた[4,3,1,2]が入っています。
for ($i = 0; $i < count($article_list); $i++) {   
            $num = $result[$i];   //[4,3,1,2]の[2]番目なので、仮ナンバー$numには3が入ります。

            $article_list[$num]['Article']['order'] = $i;  //テーブル上で3($num)番目の要素のorderが[2]になります。

            $this->Article->save($article_list[$num]);  //DBに保存します。
        }
```

要素の数だけ繰り返します。



```php5:ArticleController.php
//一覧ページ
    public function article_list() {
        $article_list = array();   
        $article_list = $this->Article->find('all', array('order'=>Array('order' => 'asc', 'created' => 'desc')));
        $this->set(compact('article_list'));

    }

```

一覧画面ではorderの昇順に要素を表示してるのでこれでOKです。

# まとめ



# 参考サイト
- [ドラッグ＆ドロップでli要素の並べ替え→DB書き換え](https://qiita.com/ao_love/items/4f74bdcd345e9e8ff810)
- [ドラッグ＆ドロップで要素を並び替えることができるSortable.js](https://spyweb.media/2017/11/02/js-drag-and-drop-sortable-plugin/)