レガシー管理ページをちょっと動的にするためだけのVue.jsとPHP

こんにちは！ 片手間JSerの者です！ レガシーPHPの方から来ました！

みんなPHPで動的な管理画面とか作りたいじゃないですか。私などは怠惰ななまけものですので、動的なページ、特にブラウザ側での動きがあるページを作るのはだるいなあ、とか思ってしまふわけですよ。

きちんと設計したアプリケーションも良いものなのですが、水は低きに流れると申しまして、Vue.jsは雑に動くコードを異常に簡単に書くこともできます。

## PHPだけで片手間

スゴイ雑に、こんなページを作ってみます。これは、何かの商品の一覧と説明文を登録する画面ってことにします。

![katatema-vue-php.gif](https://qiita-image-store.s3.amazonaws.com/0/4121/bc0992d3-24f6-f666-e89e-2d6395842d65.gif)

このコードの思想としては、一個づつ追加して保存すると最後に空欄ができるのでJavaScriptがなくてもコンテンツを追加するアプリケーションが実現できる！！！ って感じです。

* ソースコード: https://github.com/zonuexe/katatema-vue/blob/master/public/katatema1.php
* うごくサンプル: https://katatema-vue.herokuapp.com/katatema1.php

セキュリティとかあんまり気にしてないから、コピペするときは気をつけてね。特に一般公開するときは。

```php:katatema1.php
<!DOCTYPE html>
<title>アイテム管理画面</title>
<form method="post">
    <table border>
        <tr>
            <th>Name</th>
            <th>Description</th>
        </tr>
        <?php foreach ($items as $n => $item): ?>
            <tr>
                <td><input name="items[<?= h($n) ?>][name]" value="<?= h($item->name) ?>"></td>
                <td><input name="items[<?= h($n) ?>][desc]" value="<?= h($item->desc) ?>"></td>
            </tr>
        <?php endforeach; ?>
        <!-- 新しく追加したいから空の行を追加しておくよ -->
        <tr>
            <td><input name="items[<?= h($n + 1) ?>][name]" value=""></td>
            <td><input name="items[<?= h($n + 1) ?>][desc]" value=""></td>
        </tr>
    </table>
    <button type="submit">保存</button>
</form>
```

僕はJavaScriptはとてもニガテなので、PHPと静的なHTMLだけで事を済ませられないかと逃げます。
仕事でもそんな感じで通そうとするのですが、複数の要素を一度に追加したいってリクエストが容赦なく来ます。

## Vue.jsで片手間

そんなときに出会ったのがVue.jsです。難しいりくつはわからないが、適当にオブジェクトを突っ込んでやるだけで適当に動かしてくれる。Vue.jsはそんなふうに見えたわけです。

* ソースコード: https://github.com/zonuexe/katatema-vue/blob/master/public/katatema-vue.php
* うごくサンプル: https://katatema-vue.herokuapp.com/katatema-vue.php

```html:katatema-vue.php
<!DOCTYPE html>
<title>アイテム管理画面</title>
<style>[v-cloak] { display: none; }</style>
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.1.5/vue.js"></script>
<!-- ここにオブジェクトをJSONにしてつっこむ -->
<script id="json-vue" data-json="<?= h($json, ENT_QUOTES) ?>"></script>

<section id="app">
    <form method="post">
        <table v-cloak border id="items">
        <tr>
            <th>Name</th>
            <th>Description</th>
        </tr>
        <tr v-for="(item, index) in items">
            <td><input :name="'items[' + index + '][name]'" :value="item.name"></td>
            <td><input :name="'items[' + index + '][desc]'" :value="item.desc"></td>
        </tr>
        </table>
        <button type="button" v-on:click="add">追加</button>
        <button type="submit">保存</button>
    </form>
</section>
<script>
 var v = new Vue({
     el: "#app",
     // JSONをデコードしてVueにつっこむ
     data: JSON.parse(document.getElementById('json-vue').dataset.json),
     methods: {
         add: function (event) {
             // 新しい行を追加するために
             v.$data.items.push({});
             return false;
         }
     }
 });
</script>
```

Vue.jsの初期化の必要があるだけ行数は増えちゃってるんですけど、テンプレートの二重管理とかjQueryでHTMLノード作ったりとかの手間を考慮すれば、ほとんどチュートリアルにあるような内容だけで完成しました。

![katatema-vue-vue.gif](https://qiita-image-store.s3.amazonaws.com/0/4121/8a486b98-7825-3e02-dff3-134c9b2ec47c.gif)


変更点を整理しますと、いままでPHPのコード `<?= h($item->name) ?>` でテンプレートに直接出力してたものをやめて、Vue.jsのテンプレートエンジンにおまかせすることにしました。

メソッドについてVue.jsを書いたことのない型に説明をしますと、`v.$data`で`new Vue`につっこんだデータにアクセスできる(はず)なので、ここに`.push()`で要素をつっこんでやります。空欄の新しい行を追加してやりたいだけなので、空オブジェクト(`{}`)をつっこんでやるだけで十分だと私の直感が告げます。

[リストレンダリング #配列の変化を検出](https://jp.vuejs.org/v2/guide/list.html#配列の変化を検出)を見る限り、`push()`, `pop()`, `shift()`, `unshift()`, `splice()`, `sort()`, `reverse()`あたりの配列操作メソッドを利用してデータを操作するだけで、Vue.jsは変更を検知してくれるらしいのです。Vue.jsおてがるすぎでは。

## 挫折

……しかし、説明書をよく読まないマンである私はここで壁に当たりました。

![katatema-vue-vue-failure.gif](https://qiita-image-store.s3.amazonaws.com/0/4121/5ce51bb5-e3a9-25c4-590f-e5e473665592.gif)

途中まで入力した状態で、「追加」ボタンを押すと、その内容が消えるのです。これは弱った。

## Vue.jsにはうまい仕組みがあることを知る

困ったなあ、困ったなあと悩んだのですが、マニュアルを適当に検索してると[フォーム入力バインディング - vue.js](https://jp.vuejs.org/v2/guide/forms.html)のようなページがあることに気付きます。ひょっとして、`:value`の代りに`v-model`を利用すれば良いのではないか。

* ソースコード: https://github.com/zonuexe/katatema-vue/blob/master/public/katatema-vue2.php
* うごくサンプル: https://katatema-vue.herokuapp.com/katatema-vue2.php

```html
        <tr v-for="(item, index) in items">
            <td><input :name="'items[' + index + '][name]'" v-model="item.name"></td>
            <td><input :name="'items[' + index + '][desc]'" v-model="item.desc"></td>
        </tr>
```

これだけの変更で本当に動くのかな……?

![katatema-vue-vue-model.gif](https://qiita-image-store.s3.amazonaws.com/0/4121/04374c49-74c5-bff5-0012-a096ac399f87.gif)


やりましたね、フォームの入力値などの指定に`:value`(または`v-bind:value`)を使ってはだめで、`v-model`で指定するのが正解だったようです。

## まとめ

JavaScriptフレームワークを導入するのって大変なんじゃないかな？ みたいなイメージがあったのですが、なんかレガシーっぽい管理画面を動的な感じにリメイクするのにもVue.jsは威力を発揮してくれることがわかりました。

予備知識がなくてもブラウザで動くコードが書けるのは、片手間野郎としてはこの上なく嬉しいです。

きちんとフレームワークの全機能を知ってアプリケーションの設計ができるようになるのはそれはそれで良いことなのですが、技術は何かの目的を達するためにあるものなので、「あまり勉強をしなくても動くものが作れる」のは良いことなのではないかと感じます。もしかすると、Vue.jsよりも簡単なものはあるのかもしれませんが。

余談。このサンプルコードを動かすのに久々にHerokuを使ってみたんですが、`git push`するだけでPHPを超カンタンにデプロイできて無料なのはスゴイなって感じでしたヾ(〃＞＜)ﾉﾞ