なあ… そろそろ掲示板しようや…
過去ログ
スレ | 内容 |
---|---|
インスパイヤされて掲示板を作りたくなった(1) | PHPとComposer、あとべんりなライブラリ |
インスパイヤされて掲示板を作りたくなった(2) | SQLite3のスキーマ設計とTwigテンプレート |
インスパイヤされて掲示板を作りたくなった(3) | ルーティングとモデルの説明 |
インスパイヤされて掲示板を作りたくなった(4) | ドキュメント生成とモデル、SQL |
インスパイヤされて掲示板を作りたくなった(番外篇) | PsySHでアプリケーションと対話する |
インスパイヤされて掲示板を作りたくなった(番外篇2) | Composer 依存関係の更新(メンテナンス) |
インスパイヤされて掲示板を作りたくなった(5) | ← イマココ |
おさらひ
- この連載のソースコードは https://github.com/zonuexe/inspire-bbs/ にあります
- この連載に含まれるソースコードはWTFPLなので、煮るなり焼くなり好きにしてください。
- 許可とか条件とか表示とか要らないし
- なんだったら著作権表示を外して自分のものにしておk、の意
- よくわかんないこと、気になったこと、感想などあればコメントしてくださいね ヾ(〃><)ノ゙
rm cache/db.sq3
sqlite3 ./cache/db.sq3 < ./tests/db/init.sql
sqlite3 ./cache/db.sq3 < ./tests/db/seed.sql
↑ ここまでの説明通りに構成してきた場合の、DBを復活させるじゅもん。
URL設計
URL設計についてはインスパイヤされて掲示板を作りたくなった(3)などで言及してきましたが、ここで一度決めます。
ページ | 内部名 | URL | 2ch |
---|---|---|---|
トップ | top_index |
/ |
http://2ch.sc/bbsmenu.html |
板 | board_index |
/news4vip |
http://viper.2ch.sc/news4vip/ |
スレッド | thread_index |
/news4vip/12345 |
http://viper.2ch.sc/test/read.cgi/news4vip/1455901382 |
レス | thread_res |
/news4vip/12345/678 |
http://viper.2ch.sc/test/read.cgi/news4vip/1455901382/12 |
スレ作成 | thread_new |
/news4vip/new |
http://viper.2ch.sc/test/bbs.cgi?new |
スレ作成(POST) | thread_create |
/news4vip/create |
http://viper.2ch.sc/test/bbs.cgi |
「内部名」はソースコード上でページを一意に定めるためのもので、テンプレートのファイル名や名前付きルーティング(後述)に利用します。
トップページ(板一覧)の作成
改めて、以下のようなルーティングを設定します。
// この配列にHTTPメソッド、URLと返したい値(クロージャ)を追加してしていくよ。
$routing_map = [];
// クロージャは、Actionを受け取って表示内容を文字列で返す
$routing_map['top_index'] = ['GET', '/', function (Action $action) use ($twig, $now) {
return $twig->render('top_index.tpl.html', [
'boards' => Model\Board::findAll(),
]);
}];
$routing_map['board_index'] = ['GET', '/:board', function (Action $action) use ($twig, $now) {
return 'あとで実装するよ…';
}, ['board' => '/\A[a-z0-9]{1,16}\z/']];
// ...
// このへんにいろんな機能を追加していくよ
// ...
$routing_map['#404'] = function (Action $action) use ($twig) {
return "そんなページありませんよ…";
};
これは、top_index
はデータを渡してレンダリングするだけの超シンプルなページです。board_index
は文字列を表示するだけの仮実装です。この状態のindex.php
はこちら。
Twigテンプレート
Twigに関してはインスパイヤされて掲示板を作りたくなった(2)に書きましたので、再確認してください。
{% extends "base.tpl.html" %}
{% block title %}いんすぱいやーBBSへようこそ☆{% endblock %}
{% block content %}
<h1>いんすぱいやーBBS 板一覧</h1>
<ul>
{% for board in boards %}
<li>{{ board.name }}</li>
{% endfor %}
</ul>
<p>ヾ(〃><)ノ゙< ようこそ</p>
{% endblock %}
板(スレッド一覧)の作成
その前に、スレッドを表現するクラスThread.php
とスレ内の発言を表現するクラスPost.php
を用意しておきます。
Post
クラスの方はカラムが多いのに伴って、行数も多めになってます。また、日時情報をDatetimeImmutable
クラスで持つことにも注意です。
ルーティング
$routing_map['board_index']
と$routing_map['thread_index']
を継ぎ足していきます。
$routing_map['board_index'] = ['GET', '/:board', function (Action $action) use ($twig, $now) {
$board = Model\Board::find($action->param['board']);
$threads = Model\Thread::findAllByBoard($board);
return $twig->render('board_index.tpl.html', [
'board' => $board,
'threads' => $threads,
]);
}, ['board' => '/\A[a-z0-9]{1,16}\z/']];
$routing_map['thread_index'] = ['GET', '/:board/:timestamp', function (Action $action) use ($twig, $now) {
return 'あとで実装…';
}, ['board' => '/\A[a-z0-9]{1,16}\z/', 'timestamp' => '/\A[1-9][0-9]*\z/']];
Twigテンプレート
ここからTwigを使ってHTMLテンプレートを組んでいきます。
{% extends "base.tpl.html" %}
{% block title %}いんすぱいやーBBSへようこそ☆{% endblock %}
{% block content %}
<h1>いんすぱいやーBBS 「{{ board.name }}」</h1>
<ol>
{% for thread in threads %}
<li>{{ thread.title }}</li>
{% endfor %}
</ol>
<p>ヾ(〃><)ノ゙< わいわい</p>
{% endblock %}
リバースルーティング
さて、複数のページを持つページの開発時にいちばんめんどくさいのはURLの管理です。テンプレート内で文字列組立をすると、URLを変更しようと思ったときに氏ぬほどつらい目に遭ひます。また、純粋にテンプレートのコードが読みにくい。
今回ルーティングエンジンとして使ってるTetoRoutingには幸運にもリバースルーティング機能、つまりパラメータをもとにURLを作成することができます。
これとTwigの拡張機能を利用すれば、いとも簡単に実現できますね ヾ(〃><)ノ゙
Twigの拡張方法についてちゃんと知りたければExtending Twigを読んでください。
$twig->addFunction(new \Twig_SimpleFunction('url_to', function ($name, array $param = []) use ($router) {
return $router->makePath($name, $param, true);
}));
実際には、以下のように$router = new \Teto\Routing\Router($routing_map)
の次の行に書くのが大事です。
// ルーターを起動する
$router = new \Teto\Routing\Router($routing_map);
$twig->addFunction(new \Twig_SimpleFunction('url_to', function ($name, array $param = []) use ($router) {
return $router->makePath($name, $param, true);
}));
// Actionオブジェクトにはいろんな情報が詰まってるよ
$action = $router->match($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);
$content = call_user_func($action->value, $action);
クロージャについて
閑話休題。「クロージャ」と呼ばれることもあるPHPの
function()
式、つまり$f = function ($a, $b) use ($v) { ... };
のような式を「ローカル変数に代入できる函数」としか見てないのだとしたら、それはクロージャの実力を安く見積もりすぎです。もし興味があれば、クロージャって何? どこがオブジェクト指向と似てるの? をRubyで説明するために書いたコードを読んでください。なんかめんどくさいな、と思ったらスルーしてくだされ。
テンプレートから函数を呼び出す
さて、今回作った自作の函数を呼び出すには以下のようにします。
<li><a href="{{ url_to('thread_index', {"board": thread.board_id, "timestamp": thread.timestamp}) }}">{{ thread.title }}</a></li>
{"board": thread.board_id, "timestamp": thread.timestamp}
はTwigの中で連想配列を作成するための記法です。
次回予告
そろそろ掲示板っぽくなるとイイナ。次回はまた半年後、とかにならなければイイナ。