kohanaのテスト12-9・・・WordPressのトップページを作る
新規投稿が出来るようになったので、いよいよトップ画面をそれらしくします。これでユーザー登録・ログイン・新規投稿・ブログの表示というWordPressの根幹部分を kohana に移植することが出来ました。スタイルシートとスクリプトもしっかり動いているので、見た目も動作も WordPress そのものです。
ロジックの部分はというと驚くほどシンプルです。これに対してビューのテンプレートの方は読むのが嫌になるくらい class や id が定義されていて驚きます。スクリプトは jQuery とか TynyMCE とか有名所を使っているので、比較的簡単に実装できると思います。要するにサイトを作る努力って、スタイルシートで飾り付けることなんですかね?
では今日の本題へ。
モデル
先ずはデータベースからブログを読み取るモデルを作ります。
・kohana/application/classes/model/test12/posts.php の追加部分
欲しいカラムのキーと値を配列で渡すと必要な SQL インスタンスを返してくれるように書きました。ブログのデータベースには著者のユーザー名が無く、ユーザー ID しか無いので、ID を渡すとユーザー名を返してくれるメソッドも追加しています。
public function selectblogs($array) { $select = DB::select('*') ->order_by('ID', 'DESC') ->from('wp332_posts'); if ($array) { $select->where_open(); foreach ($array as $key => $value) { $select->and_where($key, '=', $value); } $select->where_close(); } return $select->execute(); } public function id2name($id) { $select = DB::select('ID', 'user_login') ->where('ID', '=', $id) ->from('wp332_users') ->execute(); return $select->get('user_login', FALSE); }
ビュー
テスト9 の応用でページネーション付きのビューを書きます。
・kohana/application/classes/contents/toppage.php
記事のタイトルは HTML エスケープしますが、記事はエスケープしません。TyniMCE のセキュリティー対策に頼る格好ですが、本当に穴がないかというと、あるんじゃないかと考えています。
<?PHP foreach ($data as $d) { $blogid = $d['ID']; $post_author = $d['post_author']; $user_login = Model::factory('test12_posts')->id2name($post_author); $post_title = HTML::chars($d['post_title']); $post_content = $d['post_content']; $post_date = $d['post_date']; ?> <article id="post-25" class="post-25 post type-post status-publish format-standard hentry category-1"> <header class="entry-header"> <h1 class="entry-title"><a href="/kohana/test12?p=<?php echo $blogid ?>" title="パーマリンク" rel="bookmark"><?php echo $post_title ?></a></h1> <div class="entry-meta"> <span class="sep">投稿日:</span><a href="/kohana/test12?p=<?php echo $blogid ?>" title="" rel="bookmark"> <time class="entry-date" datetime="<?php echo $post_date ?>" pubdate><?php echo $post_date ?></time></a> <span class="by--author"> <span class="sep">作成者:</span> <span class="author vcard"> <a class="url fn n" href="/kohana/test12?author=<?php echo $post_author ?>" title="<?php echo $user_login ?> の投稿をすべて表示" rel="author"><?php echo $user_login ?></a></span></span> </div><!-- .entry-meta --> <div class="comments-link"> <a href="/kohana/test12?p=<?php echo $blogid ?>#respond" title="コメント"><span class="leave-reply">返信</span></a> </div> </header><!-- .entry-header --> <div class="entry-content"> <p><?php echo $post_content ?></p> </div><!-- .entry-content --> <footer class="entry-meta"> <span class="cat-links"> <span class="entry-utility-prep entry-utility-prep-cat-links">カテゴリー:</span> <a href="/kohana/test12?cat=1" title="未分類 の投稿をすべて表示" rel="category">未分類</a></span> <span class="sep"> | </span> <span class="comments-link"><a href="/kohana/test12?p=<?php echo $blogid ?>#respond" title="コメント"> <span class="leave-reply">コメントをどうぞ</span></a></span> </footer><!-- #entry-meta --> </article><!-- #post-25 --> <?php } ?> <?php if (!$single) { echo '<br />'; echo '<a href=' . $firstpage . '><<</a>'; echo '<a href=' . $beforepage . '> <</a>'; echo ' 前のページ 次のページ '; echo '<a href=' . $nextpage . '>> </a>'; echo '<a href=' . $lastpage . '>>></a>'; } ?>
・kohana/application/views/test12/toppage/recent_posts_2.php
サイドメニューの最近の投稿リスト用のビューです。
<aside id="recent-posts-2" class="widget widget_recent_entries"> <h3 class="widget-title">最近の投稿</h3> <ul> <?PHP foreach ($data as $d) { $blogid = $d['ID']; $post_title = HTML::chars($d['post_title']); ?> <li><a href="/kohana/test12?p=<?php echo $blogid ?>" title="<?php echo $post_title ?>"><?php echo $post_title ?></a></li> <?php } ?> </ul> </aside>
コンテンツ用のコントローラ
動的なコンテンツはメインのコントローラとは別に書いています。
・kohana/application/classes/contents/toppage.php
トップページの中核となるブログ表示部のコントローラです。ページネーションの制御、著者が指定された場合の制御、単一ページ指示の場合の制御を行います。テスト9のコードを改造して作ります。
メソッド recentpost2 はサイドメニューの最近の投稿を処理しています。ページネーションは不要なんですが、便利なので ZF のページネーター用のメソッドを利用しています。
驚くべきは次の一文です。
$paginator = Zend_Paginator::factory($select);
kohana が作った SQL のインスタンスを ZF が食ってくれます。こんなのどこにも書いてない訳ですが、やってみたら出来ちゃったみたいな。kohana の設計者が ZF を意識してコードを書いたとしか思えないですね。
<?php defined('SYSPATH') OR die('No direct access allowed.'); class Contents_Toppage { static function blogs($page, $author, $p) { $model = Model::factory('test12_posts'); if ($author) { $array = array('post_author' => $author, 'post_status' => 'publish'); } elseif ($p) { $array = array('ID' => $p, 'post_status' => 'publish'); } else { $array = array('post_status' => 'publish'); } $select = $model->selectblogs($array); $paginator = Zend_Paginator::factory($select); if ($p) { $view = View::factory('test12/toppage/blogs'); $view->data = $paginator; $view->single = TRUE; return $view->render(); } if (!$page) { $page = 1; } $paginator->setCurrentPageNumber($page); $pagecount = $paginator->count(); $op = ''; if ($author) { $op = '&author=' . $author; } $hp = 'test12'; $firstpage = $hp . '?page=1' . $op; $beforepage = $hp . '?page=' . ($page - 1) . $op; $nextpage = $hp . '?page=' . ($page + 1) . $op; $lastpage = $hp . '?page=' . $pagecount . $op; if ($page == 1) $beforepage = $hp . '?page=1' . $op; if ($page == $pagecount) $nextpage = $hp . '?page=' . $pagecount . $op; $view = View::factory('test12/toppage/blogs'); $view->data = $paginator; $view->single = FALSE; $view->firstpage = $firstpage; $view->beforepage = $beforepage; $view->nextpage = $nextpage; $view->lastpage = $lastpage; return $view->render(); } static function recentposts2() { $model = Model::factory('test12_posts'); $array = array('post_status' => 'publish'); $select = $model->selectblogs($array); $paginator = Zend_Paginator::factory($select); $view = View::factory('test12/toppage/recent_posts_2'); $view->data = $paginator; return $view->render(); } } ?>
コントローラ
メインのコントローラのお仕事はあまりありません。
・kohana/application/classes/controller/test12.php
ビューで共有するグローバル変数を定義できるようなので、使ってみました。各ページにいちいち変数を渡す必要が無くなるので、コードが短くなります。
<?php defined('SYSPATH') OR die('No direct access allowed.'); class Controller_Test12 extends Controller { public function action_index() { $loginuser = Auth_Wplogin::instance()->get_user(); View::bind_global('loginuser', $loginuser); isset($_GET['page']) ? $page = $_GET['page'] : $page = NULL; isset($_GET['author']) ? $author = $_GET['author'] : $author = NULL; isset($_GET['p']) ? $p = $_GET['p'] : $p = NULL; $view = view::factory('test12/toppage/test12'); $view->head01 = view::factory('test12/toppage/head01'); $view->header01 = view::factory('test12/toppage/header01'); $view->primary01 = view::factory('test12/toppage/primary01'); $view->primary01->nav_above = view::factory('test12/toppage/nav_above'); $view->primary01->article01 = Contents_Toppage::blogs($page, $author, $p); $view->primary01->nav_below = view::factory('test12/toppage/nav_below'); $view->secondary01 = view::factory('test12/toppage/secondary01'); $view->secondary01->search_2 = view::factory('test12/toppage/search_2'); $view->secondary01->recent_posts_2 = Contents_Toppage::recentposts2(); $view->secondary01->recent_comments_2 = view::factory('test12/toppage/recent_comments_2'); $view->secondary01->archives_2 = view::factory('test12/toppage/archives_2'); $view->secondary01->categories_2 = view::factory('test12/toppage/categories_2'); $view->secondary01->meta_2 = view::factory('test12/toppage/meta_2'); $view->footer01 = view::factory('test12/toppage/footer01'); $view->adminbar = view::factory('test12/toppage/adminbar'); $this->response->body($view); } } ?>
動作試験
冒頭にも書きましたが、基本機能限定とはいえ WordPress そのもの。素晴らしい出来栄えです。特に記事の投稿部分が素晴らしいと感じるのですが、ここは TynyMCE のおかげですね。アドミンバーやアドミンサイドメニューなんかも jQuery の威力だろうし。WordPress の人気って、多分歴史的なものなんでしょうね。同等機能のブログツールを kohana で作ったら、3人で一ヶ月あれば出来ちゃうと思います。