LoginSignup
3
4

More than 5 years have passed since last update.

インスパイヤされて掲示板を作りたくなった(5)

Last updated at Posted at 2016-09-17

なあ… そろそろ掲示板しようや…

過去ログ

スレ 内容
インスパイヤされて掲示板を作りたくなった(1) PHPとComposer、あとべんりなライブラリ
インスパイヤされて掲示板を作りたくなった(2) SQLite3のスキーマ設計とTwigテンプレート
インスパイヤされて掲示板を作りたくなった(3) ルーティングとモデルの説明
インスパイヤされて掲示板を作りたくなった(4) ドキュメント生成とモデル、SQL
インスパイヤされて掲示板を作りたくなった(番外篇) PsySHでアプリケーションと対話する
インスパイヤされて掲示板を作りたくなった(番外篇2) Composer 依存関係の更新(メンテナンス)
インスパイヤされて掲示板を作りたくなった(5) ← イマココ

おさらひ

  1. この連載のソースコードは https://github.com/zonuexe/inspire-bbs/ にあります
  2. この連載に含まれるソースコードはWTFPLなので、煮るなり焼くなり好きにしてください。
    • 許可とか条件とか表示とか要らないし
    • なんだったら著作権表示を外して自分のものにしておk、の意
  3. よくわかんないこと、気になったこと、感想などあればコメントしてくださいね ヾ(〃><)ノ゙
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

「内部名」はソースコード上でページを一意に定めるためのもので、テンプレートのファイル名や名前付きルーティング(後述)に利用します。

トップページ(板一覧)の作成

改めて、以下のようなルーティングを設定します。

index.php
// この配列に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)に書きましたので、再確認してください。

top_index.tpl.html
{% 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']を継ぎ足していきます。

index.php
$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テンプレートを組んでいきます。

board_index.tpl.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)の次の行に書くのが大事です。

index.php
// ルーターを起動する
$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で説明するために書いたコードを読んでください。なんかめんどくさいな、と思ったらスルーしてくだされ。

テンプレートから函数を呼び出す

さて、今回作った自作の函数を呼び出すには以下のようにします。

board_index.tpl.html
    <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の中で連想配列を作成するための記法です。

次回予告

そろそろ掲示板っぽくなるとイイナ。次回はまた半年後、とかにならなければイイナ。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4