Laravelのモデルデータをvue.jsを使って非同期にソートする

## はじめに
今回作成したいのは、いわゆる「ソート機能」です。

こんなやつ↓
![tes4.gif](https://qiita-image-store.s3.amazonaws.com/0/140786/b0bda370-c7ab-2d31-16f8-fab02729b404.gif)


[Vue.jsのフィルターを使ってもcheckedを維持する – カバの樹](https://www.kabanoki.net/1659)

vue.jsを使って非同期に処理をします。
データはDBから取得したデータ（今回はLaravelをつかって）を利用します。

(jQueryを使ってモデルに紐付いていないデータをソートする方法は、色々あったのですがvueとLaravelを組み合わせて、データベースのデータを追加で取得する方法が無かったので今回記事を書きました。)


## 実装
``` index.js
const API_GET_DATA = '/api/data/get/'; // ①

new Vue({
  data(){
    return {
      order: '',
      orderItems: [ // ②
        {name: 'おすすめ順', value: 'recommend'},
        {name: '費用安い順', value: 'lowinitial'},
        {name: '近い順', value: 'nearly'},
      ]
    }
  },
  watch: {
    /**
    * 一覧の初期値取得
    * 初期表示はオススメ順でソート
    */
    order() {
      axios.post(API_GET_DATA, {
        order: this.order // ③
      })
      .then(res => {
        this.$store.commit('setValue', res.data);
      });
    }
  }
}
```
①実際に取得するデータのコントローラーまでのルーティングを記載しておきます。

②vue側のdataで予め、orderに渡す情報を記載しておきます。下の`sort.html`で利用します。

③そして、laravel側の`DataGetController.php`に対して、selectで選択した値を渡す為に、apiに対してリクエストを投げます。
今回、帰ってきた値はvueのstoreに保存するようにしています。

``` sort.html
<div class="facility__sort">
    <p class="facility__sortHeader">並び替え：</p>
    <div class="selectbox">
        <select v-model="order" class="selectbox__input">
            <option v-for="(item, index) in orderItems" :value="item.value">{{ item.name }}</option>
        </select><i class="selectbox__icon"></i>
    </div>
</div>
```
v-forで先程dataに記載した`orderItems`を利用して、valueとラベルを表示させます。
それ以外はなんの変哲もないselectフォームになります。

``` DataGetController.php
/**
 * @param Request $request
 *
 * @return ApiResponse
*/
public function main(Request $request)
{
    $order_type = $request->input('order', 'recommend');

    $data = $this->main_service->getByOrder($order_type);
    return new ApiResponse(['data' => $data]);
}
```

`DataGetController.php`では、モデルを取得するメソッドを呼び出します
（各自設計が異なりそうなので、今回は詳細は省略します。）

リクエストで取得した`$order_type`の中には、`orderItems`で選択されたvalueが値に入っています。
（例: 'おすすめ順' -> value: 'recommend'）

流れとしては、それぞれのモデル等で、以下のようなデータの紐づけを定義しておきます。
取得した`orderItems`の値に応じて、ソートする際にどのようなカラムが必要になるのかを考え、
それぞれのカラムを定義します。

こちらをサービス等で取得し、あとはorderByなどに渡してあげれば、クエリが発行できるので、それで取得できた値をvue側にreturnすると完了です。

``` DataModels.php
const ORDER_CONDITIONS = [
  'recommend' => [ // おすすめ順
    'order' => null
  ],
  'lowinitial' => [ // 費用安い順
    'order' => [
      'initial_price_min' => 'ASC',
     ],
   ],
  'nearly' => [ // 近い順
    'order' => [
      'distance' => 'ASC',
     ],
   ],
];
```

``` service.php
/**
 * 引数で受け取ったorder_typeに応じてそれぞれのソートを呼び出す。
 *
 * @param $city_id
 * @param $per_page
 * @param $page
 * @param $order_type
 * @return mixed
 */
public function getByOrder($order_type)
{
  if (! isset(DataModel::ORDER_CONDITIONS[$order_type])) {
    $order_type = 'recommend'; 
   }

  $order_conditions = DataModel::ORDER_CONDITIONS[$order_type];

  return $this->data->getByOrderType($order_condition);
}
```

例えば、以下のようにDataModelに定義した、並び替え情報をもとに`$order_condition`

(近い順だとこんな感じのデータ)

```
'order' => [
  'distance' => 'ASC',
 ],
```

を取得できますので、これを`orderBy($order_condition)`としてあげると、`distanceカラム`でソートされます。
こんな感じで値が取得できますので、そちらをリターンしてあげると、vue側でstoreに保存というような一連の流れを作ることができました。

## おわりに
いかがだったでしょうか。

意外と簡単に非同期で、ソート機能を実現できました。（私はめちゃめちゃ時間がかかりましたが:joy:）
vueとwebアプリケーションフレームワークの融合はかゆいところに手が届く感じがして、大変重宝しております。

また、機能単位でvueとフレームワークの実装をご紹介できたらと思います。