PHP
HTML
JavaScript
laravel
vue.js

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

はじめに

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

こんなやつ↓
tes4.gif

Vue.jsのフィルターを使ってもcheckedを維持する – カバの樹

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とフレームワークの実装をご紹介できたらと思います。