PHPは簡単に動かせるのがいいところですね。
その良さを生かしつつ、薄いフレームワークの組み合わせで掲示板を作ってみたいと思います。
経緯
- Node.js + Express使いだが、実際のところNode.jsをサーバで動かしていい案件はまれ
- PHP案件はいっぱいだし、動くレンタルサーバも多いぞ
- 動作環境としてのPHPは魅力なので触っておきたい
- PHPにあまり詳しくないし、コードを書いている時間よりも調べ物に時間の方が長くなるのが辛い
- PHPフレームワークも分厚いものが多く、調べ物に時間が取られてやはり辛い
- 何でも屋になりたいわけではないので、PHPのことは詳しく知らない自分のままでいたい
作りたいもの
とりあえず一行掲示板。
考えた構成
- 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を設定できる。下記のようにする。
$app = new \Slim\Slim();
$app->response->headers->set('Content-Type', 'application/json');
テーブルの作成
普通にPDOからSQLiteを使う。
1テーブル程度の小規模アプリなので、わざわざORMを使わなくても良さそう。
$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に変換する。
$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));
}
記事の投稿
$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
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
<!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のビルトインサーバで動作させる。
php -S localhost:8000
レポジトリはこちら:GitHub
終わりに
ちゃんとするなら、ユーザ管理とか削除編集、ページネーションなんかも入れないとですね。
あと、今回marxというCSSフレームワークを使ってますが、Bootstrapと比べて余計な色付けが少なく、とても使いやすいのでおすすめです。