Drag&Dropの裏側の処理（備忘録）

Drag&Dropって結構Webアプリだと当たり前になってきてるし、フロントサイドだとJqueryUIやReactなどライブラリを使用すればすぐできる世の中だと思います。ただ裏側（サーバサイド）の処理ってどう実装するんだっけと、ふと思い書いてみました。

まず、前提としてフロントだけで完結せず、サーバサイドの実装あるものとしてます。
## 初期状態（前提条件）

↓↓↓ 順序の情報をもってるテーブル ↓↓↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516848/2a3850b5-9b19-2aa6-d3ce-dbdc01c27d63.png)
※ここでは上記テーブルをT_○○と命名しておきます
※また**order_no**でソートしたものを表示する

## Drag&Dropの手順
1. フロントサイドでDrag&Dropの操作をして、サーバサイドに以下の情報を送る。
  ・動かそうとしている**order_no**　(**from_order_no**と呼びます)
  ・動そうとしている先にいるitemの**order_no** (**to_order_no**と呼びます)
2. from_order_noとto_order_noの間にある**order_no**を増減させる。
3. 対象itemの**order_no**をto_order_noで更新する。

パッと見、文字だけだと分かりづらいので、図で例を示します。

<br />

ex.)id=2をid=4, 5の間にDrag&Dropする。
つまり、id=2の**order_no**を4で更新したい時
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516848/b8017f53-c3ad-a9b1-eea3-b99ec49b8679.png)
　↓

id=2のorder_noを更新する前に、id=3, 4の**order_no**を１減らす
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516848/d35b776e-8f49-86ce-0dd8-be56855d0479.png)
↓

id=2の**order_no**を4で更新する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516848/d8215e76-9f90-5b96-0cce-53d85e9a49be.png)
の手順を実装していきます。
ただ今回は上から下へDrag&Dropをしました(from_order_no < to_order_no)が、下から上へとDrag&Dropした(from_order_no > to_order_no)時も同じようにfrom_order_noとto_order_noの間の**order_no**を調整する必要があり、若干今回の例と異なります。
ここは考えてみてください...
## 実装
実装しちゃいます。
あ、言語はC#です。
```C#
/// <summary>
/// Drag&Dropのサーバサイド側の処理
/// </summary>
/// <param name="fromOrderNo">移動前のorder_no</param>
/// <param name="toOrderNo"  >移動先のorder_no</param>
public void DragAndDrop(int fromOrderNo, int toOrderNo)
{
    // SQLへ接続、Transactionの処理は今回省略します....

    var command = new SqlCommand();
    command.Connection = connection; // connectionは上で定義してください
    
    var headOrderNo = fromOrderNo < toOrderNo ? (fromOrderNo + 1) : toOrderNo;
    var tailOrderNo = fromOrderNo < toOrderNo ? toOrderNo : (fromOrderNo - 1);
    var step = fromOrderNo < toOrderNo ? -1 : 1;
    
    // fromOrderNoとtoOrderNoの範囲内のorderNoを調整する
    IncrementTargetRangeOrderNo(command, headOrderNo, tailOrderNo);

    // fromOrderNoとtoOrderNoを入れ替える
    ReplaceOrderNo(command, fromOrderNo, toOrderNo);    

    // SQL接続終了の処理....
}

/// <summary>
/// 指定範囲内のorder_noを増減させる
/// </summary>
/// <param name="cmd">SqlCommand</param>
/// <param name="headOrderNo">orderNoの範囲指定の先頭</param>
/// <param name="tailOrderNo">orderNoの範囲指定の末尾</param>
/// <param name="step">増減数(マイナス値を入れれば減少数)</param>
private void IncrementTargetRangeOrderNo(SqlCommand cmd, int headOrderNo, int tailOrderNo, int step = 1)
{
    cmd.CommandText = @"
UPDATE
  T_◯◯
SET
  order_no = order_no + @step
WHERE
  order_no >= @head_order_no
AND
  order_no <= @tail_order_no
";
    cmd.Parameters.Add(new SqlParameter("@step", step));
    cmd.Parameters.Add(new SqlParameter("@head_order_no", headOrderNo));
    cmd.Parameters.Add(new SqlParameter("@tail_order_no", tailOrderNo));
    cmd.ExecuteNonQuery();
}

/// <summary>
/// 指定したorder_noへ置き換える
/// </summary>
/// <param name="cmd">SqlCommand</param>
/// <param name="headOrderNo">orderNoの範囲指定の先頭</param>
/// <param name="tailOrderNo">orderNoの範囲指定の末尾</param>
private void ReplaceOrderNo(SqlCommand cmd, int fromOrderNo, int toOrderNo)
{
    cmd.CommandText = @"
UPDATE
  T_◯◯
SET
  order_no = @to_order_no
WHERE
  order_no = @from_order_no
";
    cmd.Parameters.Add(new SqlParameter("@from_order_no", fromOrderNo));
    cmd.Parameters.Add(new SqlParameter("@to_order_no", toOrderNo));
    cmd.ExecuteNonQuery();
}
```

これでいけたかな？

以上です。