dari88's diary

これから趣味にするプログラミング/PHP/javascript/kohana/CMS/web design/

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 . '>&lt;&lt;</a>';
    echo '<a href=' . $beforepage . '>&nbsp;&lt;</a>';
    echo ' 前のページ     次のページ ';
    echo '<a href=' . $nextpage . '>&gt;&nbsp;</a>';
    echo '<a href=' . $lastpage . '>&gt;&gt;</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人で一ヶ月あれば出来ちゃうと思います。