PHPになるべく詳しくならずにWebアプリを作る (Slim + Vue.js)


![Vueにゃん](https://qiita-image-store.s3.amazonaws.com/0/32596/254e604e-3ea1-678d-5011-56528c6e10f6.png "CNGmE6AU8AE5H84.png")


PHPは簡単に動かせるのがいいところですね。
その良さを生かしつつ、薄いフレームワークの組み合わせで掲示板を作ってみたいと思います。


# 経緯

 * Node.js + Express使いだが、実際のところNode.jsをサーバで動かしていい案件はまれ
 * PHP案件はいっぱいだし、動くレンタルサーバも多いぞ
 * 動作環境としてのPHPは魅力なので触っておきたい
 * PHPにあまり詳しくないし、コードを書いている時間よりも調べ物に時間の方が長くなるのが辛い
 * PHPフレームワークも分厚いものが多く、調べ物に時間が取られてやはり辛い
 * 何でも屋になりたいわけではないので、PHPのことは詳しく知らない自分のままでいたい

# 作りたいもの

とりあえず一行掲示板。

# 考えた構成

![スクリーンショット 2015-08-25 18.06.38.png](https://qiita-image-store.s3.amazonaws.com/0/32596/d27f4c76-2e26-e95f-3220-096e0bb5ed33.png "スクリーンショット 2015-08-25 18.06.38.png")


 * SPA（PHPを書く量を最低限にするため）
 * PHP5.5 + ビルトインサーバ
 * Slim Framework2 (3も気になるがとりあえず)
 * Vue.js
 * Sqlite（mysqlを使うのがかったるい）

また、Sqliteなら、さくらインターネットの最下位プランでも動作するだろうという目論見。



# SlimでJSONサーバを書く

Slimはsinatraライクに書ける。
JSONを吐くだけなら下記のようにする。

```
$app->get("/items/", function() (){
	echo '{"message" : "Hello, World"}';
}
```

とりあえず上記のように、適当なJSONを返すようにしておいて、先にフロントエンドを作るのが楽。


## JSONを返す設定

JSONしか扱わないのであれば、ルーティング処理全体にContent-Typeを設定できる。下記のようにする。

```php
$app = new \Slim\Slim();
$app->response->headers->set('Content-Type', 'application/json');
```


## テーブルの作成

普通にPDOからSQLiteを使う。
１テーブル程度の小規模アプリなので、わざわざORMを使わなくても良さそう。


```php
$db = new PDO('sqlite:bbs.db');
$db->exec('CREATE TABLE IF NOT EXISTS bbs(id INTEGER PRIMARY KEY, contents TEXT)');
```


## 記事一覧の取得

json_encodeを使って、DBからfetchしたものをそのままjsonに変換する。

```php
$app->get("/items/", function() use ($app, $db){
	echo json_encode($db->query('SELECT id, contents FROM bbs ORDER by ID DESC')->fetchAll(PDO::FETCH_ASSOC));
}
```

## 記事の投稿

```php
$app->post("/items/", function() use ($app, $db){
	$text = $app->request->post('text');
	$db->prepare('INSERT INTO bbs(contents) VALUES(?)')->execute([$text]);
	$result["status"] = "success";
	$result["text"] = $text;
	echo json_encode($result);
});
```


# フロントエンドを作る

ビューのフレームワークとしてVue.js、Ajaxにはsuperagentを採用する。requireはbrowserifyで解決した。
フレームワークは別にVue.jsでなくても構わない。AngularでもReactでもjQueryでも素JSでも出来る事をやっている。


main.js

```js
var Vue = require("vue");
var request = require("superagent");

new Vue({
	el: "#main",
	data: {
		text: "",
		items: []
	},
	methods: {
		fetchPosts: function(){
			var self = this;
			request
				.get("./api.php/items/")
				.end(function(err, res){
					self.items = res.body;
				})
		},
		createPost: function(text){
			var self = this;
			request
				.post("./api.php/items/")
				.type('form')
				.send({text: text})
				.end(function(err, res){
					if(err){
						console.error(err);
					}
					self.text = "";
					self.fetchPosts();
				});
		}
	},
	ready: function(){
		this.fetchPosts();
	}
})
```

index.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>BBS</title>
	<link rel="stylesheet" href="https://cdn.rawgit.com/mblode/marx/master/css/marx.min.css">
	<link rel="stylesheet" href="./index.css">
</head>
<body>
	<main id="main">
		<h1>BBS</h1>
		<textarea v-model="text" class="bbs__textarea"></textarea>
		<button v-on="click: createPost(text)">投稿</button>	
		<div class="bbs__item" v-repeat="item: items">
			<p>
				{{item.contents}}
			</p>
			<p class="bbs__item__id">
				No: {{item.id}}
			</p>
			<small></small>
		</div>

	</main>
	<script src="dist/bundle.js"></script>
</body>
</html>
```

リクエスト先のURLが`./api.php/〜`などとなっている。たいていは`.htaccess`でapi.phpを削除するなどして、綺麗なURLに直すところ。

だが、SPAにしてしまえば、URLの遷移はJavaScript側に任せることができる。APIのURLが汚い分には、見ている人にはわからないので、このままでいいことにする。

Vue.jsでルーティングを行なう場合は、`vue-router`や`director`を使うことになるが、今回はやらない。


余談だが、.htaccessに依存するということは、IISやnginxにデプロイしたり、ビルトインサーバで動作させることができなくなるので、いずれバッドノウハウになるのではと思う。

# 完成品

とりあえずPHPのビルトインサーバで動作させる。

```sh
php -S localhost:8000
```


![スクリーンショット 2015-08-25 16.53.27.png](https://qiita-image-store.s3.amazonaws.com/0/32596/e225168d-0e3d-7905-8c00-95c2a5a85388.png "スクリーンショット 2015-08-25 16.53.27.png")

レポジトリはこちら：[GitHub](https://github.com/hashrock-sandbox/example-slim-vue-sqlite-bbs)


# 終わりに

ちゃんとするなら、ユーザ管理とか削除編集、ページネーションなんかも入れないとですね。
あと、今回[marx](http://matthewblode.com/marx/)というCSSフレームワークを使ってますが、Bootstrapと比べて余計な色付けが少なく、とても使いやすいのでおすすめです。