CodeIgniterの学習 8 - ビューを外枠(器)と中身(具)のテンプレートの2つに分ける

(08/09/27 記述を訂正しました。
誤:$tpl["main_content"] = $this -> load -> view( 'tasklist_view', $data );
正:$tpl["main_content"] = $this -> load -> view( 'tasklist_view', $data ,true);
でした。これでテンプレートがネストできます。)



CodeIgniterでも他のCMSなどと同様に、ビューのテンプレートを

  • 外枠(ヘッダとかフッタとか、メインコンテンツの外側のみのテンプレート)と、
  • 中身(メイン機能部分とかメニュー部分とかのテンプレート、ここは1個〜n個)

に分けて構築したくなった。


外枠は毎回同じビューを使い回して、中身だけ各コントローラー専用としたい。

ちょっと調べてみたら、
Displaying Multiple Views - http://codeigniter.com/wiki/Displaying_Multiple_Views/
というものが有ったのだが、その中に

NOTE: This article was written before version 1.6. Version 1.6 of CI allows multiple views to be loaded in same controller function.

(意訳)
注:これはバージョン1.6以前を対象とした記事なりよ。1.6からは多重ビューは、既存のコントローラで読み込まれるにょ。

と書いていた。デフォルトで使えるようになってるらしい。

system/libraries/Loader.php を覗いてみると確かにそうなっていそうだったので、試してみた。
(ついでに少しだけ昨日までのソースを整理した。)

(ちなみに今日やる予定だったログイン認証は選択肢が多くて調査が結構面倒なので、先に簡単な方から片づけることにする。←逃げてる)


作業メモ

1)ビューを分割 (外枠用)

外枠用のビューを application/views/base_view.php として新設し、
この中を以下のようにした。

外枠用の共通の変数として、

  • $page_title …(タイトル用、無ければ空)
  • $xajax_js …(xajaxのjavascript用、無ければ空)
  • $main_content … (メインコンテンツすなわち具 用)

を設置した。

複数のブロックに分けたければ、このテンプレートに $sub_content なり $menu_content なりを作っていけばいい。

cssを分けていないのは説明の都合上こっちの方が楽だからだが、
cssも $css とかを作れば、動的にcssの使い分けもできるだろう。


外枠用のビュー: application/views/base_view.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title><?php if(isset($page_title)){echo $page_title ;}?></title>
<?php if(isset($xajax_js)){echo $xajax_js ;}?>
<style type="text/css">
body {
 background-color: #fff; margin: 40px;
 font-family: Lucida Grande, Verdana, Sans-serif;
 font-size: 14px; line-height:26px; color: #4F5155;}
a {
 color: #003399; background-color: transparent;
 font-weight: normal; text-decoration:underline;}
h1 {
 color: #444; background-color: transparent;
 border-bottom: 1px solid #D0D0D0;
 font-size: 16px; font-weight: bold;
 margin: 24px 0 2px 0; padding: 5px 0 6px 0;}
h2 {
 color: #666; background-color: transparent;
 border-bottom: 1px solid #ccc ; width:50%;
 font-size: 14px; font-weight: bold; margin-top:20px;}
#txt_red{
 color:#dc143c; font-weight: bold;}
#msg_red{
 color:#dc143c; font-weight: bold;
 background: #ffbbcc;
 border:1px solid #ffaacc; width:60%;
 padding:10px; font-size:1.4em;}
#SomeElementId{
 color:#ff0000; font-weight: bold;
 background: #b8e08f;
 border:1px solid #bfdb58; width:60%;
 padding:10px; font-size:1.4em;}
</style>
</head>
<body>
<!-- main_content //-->
<?=$main_content?>
<!--// main_content -->
</body>
</html>


2)ビューを分割 (中身用)
中身用のビューは、昨日までの実験ソースの application/views/tasklist_view.php から、
外枠部分を削りとったもの(+ちょこっと変更)。

中身用のビュー: application/views/tasklist_view.php

<h1>Tasklistへようこそ!</h1>
<h2>↓セッションの値確認↓</h2>
<p id="msg_red"><?= $info ?></p>
<ul>
<li><a href="/tasklist/sesstest1">/tasklist/sesstest1 に行ってセッションを格納してみる</a></li>
<li><a href="/tasklist/sesstest2">/tasklist/sesstest2 でセッションを破棄して/tasklistにリダイレクト</a></li>
</ul>

<h2>↓XAJAXのテスト↓</h2>
<div id="SomeElementId">下のボタンを押してちょ、中身が書き変わるよ</div>
<p/>
<!-- php側の_AjaxTest()は、javascript側では xajax_ をつけて xajax__AjaxTest(引数) で呼べる。
 外部から直接呼ばれないようにファンクション名を
 _AjaxTestとしてアンダーバーをつけているので_が2つある -->
<input type="button" value="押してみる" onclick="xajax__AjaxTest(2);">

<h2>↓キャプチャのテスト↓</h2>
<form method="post" action="/tasklist/captest">
<?=$cap_img?>
<p>上に見える文字を素早く入力してください</p>
<p id="txt_red"><?=$cap_msg?></p>
<input type="text" name="captcha" value="" />
<input type="submit" name="submit" value="★キャプチャのテスト★">
</form>


3)コントローラー側の修正

コントローラー application/controllers/tasklist.php 側では、
ビューにデータをセットするところを、以下のようにした。

ソース内の
$tpl["page_title"] が、外枠の $page_title
$tpl["xajax_js"] が、外枠の $xajax_js
$tpl["main_content"] が、外枠の $main_contentで、同時に中身(具)の tasklist_view となっている。

これで多段ビューが可能になる。

<?php 
//上略

// 大枠のテンプレート側の変数(ヘッダ部分のタイトルと、xajax用javascriptタグ)
$tpl["page_title"] = "タスクリストにようこそ";
$tpl["xajax_js"] = $this -> xajax -> getJavascript( base_url() ); // xajax用javascript生成
 
// タスクリスト(実験ソース)のテンプレートにデータをセット
$tpl["main_content"] = $this -> load -> view( 'tasklist_view', $data ,true); 
// 大枠のテンプレートに、タスクリストのビューをはめ込む
$this -> load -> view( 'base_view', $tpl );

//下略
 ?>


全ソース

昨日までのソースを改良した全ソースを貼っておく。

ビューの生成部分は _SetTpl() にまとめた。
また、ファンクション名とかを多少分かりやすいものに変更している。

コントローラー側: application/controllers/tasklist.php

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
// タスクリストというかいろいろ実験
class Tasklist extends Controller {
// コンストラクタ
function Tasklist()
{
  parent :: Controller(); 
  // urlヘルパ
  $this -> load -> helper( 'url' ); 
  // xajaxを有効に
  $this -> load -> library( 'xajax' ); 
  // _AjaxTestをajax(xajax)のファンクション化する。
  // javascript側では xajax__AjaxTest で呼び出せる
  $this -> xajax -> registerFunction( array( '_AjaxTest', &$this, '_AjaxTest' ) );
  $this -> xajax -> processRequest();
} 
// デフォルトインデックス
function index()
{ 
  // セッションの値確認テスト用
  $data["info"] = ( $this -> db_session -> userdata( 'info' ) ) ?
  $this -> db_session -> userdata( 'info' ) : "中に誰もいませんよ"; 
  // キャプチャタグを生成する
  $data["cap_img"] = $this -> _MakeCaptcha();
  $data["cap_msg"] = ""; 
  // ビューの生成
  $this -> _SetTpl( $data );
} 
// ビューの生成 一カ所にまとめた
function _SetTpl( $data )
{ 
  // 大枠のテンプレート側の変数(ヘッダ部分のタイトルと、xajax用javascriptタグ)
  $tpl["page_title"] = "タスクリストにようこそ";
  $tpl["xajax_js"] = $this -> xajax -> getJavascript( base_url() ); // xajax用javascript生成
   
  // タスクリスト(実験ソース)のテンプレートにデータをセット
  $tpl["main_content"] = $this -> load -> view( 'tasklist_view', $data ,true); 
  // 大枠のテンプレートに、タスクリストのビューをはめ込む
  $this -> load -> view( 'base_view', $tpl );
} 
// セッション格納テスト画面
function SessTest1()
{ 
  // セッションに値を格納
  $this -> db_session -> set_userdata( 'info', 'なんか入ってきた:' . date( "H:i:s", time() ) ); 
  // セッションから取り出し
  $data["info"] = $this -> db_session -> userdata( 'info' ); 
  // キャプチャタグを生成する
  $data["cap_img"] = $this -> _MakeCaptcha();
  $data["cap_msg"] = ""; 
  // ビューの生成
  $this -> _SetTpl( $data );
} 
// セッション破棄テスト→リダイレクト
function SessTest2()
{ 
  // セッションの破棄
  $this -> db_session -> sess_destroy();
  redirect( '/tasklist' );
} 
// xajaxのレスポンス用
function _AjaxTest( $number )
{ 
  // xajax用(ボタン押下時のイベント)
  $objResponse = new xajaxResponse(); 
  // SomeElementId とついたID内をinnerHTML書き換えで、入れ替える。
  $objResponse -> Assign( "SomeElementId", "innerHTML", "足し算結果:" . ( $number + 3 ) );
  return $objResponse;
} 
// キャプチャの生成用
function _MakeCaptcha()
{ 
  // キャプチャの生成
  // プラグインの読み込み
  $this -> load -> plugin( 'captcha' ); 
  // キャプチャ生成用引数のセット
  $vals = array( 'img_path' => './img/captcha/', // 画像の保存パス
      'img_url' => '/img/captcha/', // 画像のURL
      'font_path' => BASEPATH . 'fonts/sazanami-gothic.ttf', // 使用するフォント
      'img_width' => '250', // 幅
      'img_height' => 60, // 高さ
      ); 
  // キャプチャを生成
  $cap = create_captcha( $vals ); 
  // キャプチャ確認用にレコードを挿入
  if ( $cap ) {
      $data = array( 'captcha_id' => '',
          'captcha_time' => $cap['time'],
          'ip_address' => $this -> input -> ip_address(),
          'word' => $cap['word'] , 
          );
      $query = $this -> db -> insert_string( 'captcha', $data );
      $this -> db -> query( $query );
  } else {
      return "captchaが生成できないよ" ;
  } 
  return $cap['image'] ;
} 
// キャプチャの整合性チェック
function _CheckCaptcha()
{ 
  // 古いキャプチャ確認用データをまず削除(ここでは2時間)
  $expiration = time()-7200 ;
  $sql = " DELETE FROM captcha WHERE captcha_time < ? ";
  $binds = array( $expiration );
  $query = $this -> db -> query( $sql, $binds ); 
  // 画面から入力された値が正しいかチェック
  $sql = "SELECT COUNT(*) AS count FROM captcha WHERE word = ? AND ip_address = ? AND captcha_time > ?";
  $binds = array( $_POST['captcha'], $this -> input -> ip_address(), $expiration );
  $query = $this -> db -> query( $sql, $binds );
  $row = $query -> row();

  if ( $row -> count == 0 ) {
      return false;
  } 
  return true;
} 
// キャプチャの確認画面
function CapTest()
{ 
  // セッションの値確認テスト
  $data["info"] = ( $this -> db_session -> userdata( 'info' ) ) ?
  $this -> db_session -> userdata( 'info' ) : "中に誰もいませんよ"; 
  // キャプチャタグの整合性チェックと再生成
  $data["cap_img"] = $this -> _MakeCaptcha();
  $data["cap_msg"] = $this -> _CheckCaptcha() ? "正解!だけどもう一回" : "残念なのでもう一回"; 
  // ビューの生成
  $this -> _SetTpl( $data );
} 
} //Endofclass
/**
* End of file tasklist.php
*/
/**
* Location: ./application/controllers/tasklist.php
*/
 ?>


ビュー側(外枠): application/views/base_view.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title><?php if(isset($page_title)){echo $page_title ;}?></title>
<?php if(isset($xajax_js)){echo $xajax_js ;}?>
<style type="text/css">
body {
 background-color: #fff; margin: 40px;
 font-family: Lucida Grande, Verdana, Sans-serif;
 font-size: 14px; line-height:26px; color: #4F5155;}
a {
 color: #003399; background-color: transparent;
 font-weight: normal; text-decoration:underline;}
h1 {
 color: #444; background-color: transparent;
 border-bottom: 1px solid #D0D0D0;
 font-size: 16px; font-weight: bold;
 margin: 24px 0 2px 0; padding: 5px 0 6px 0;}
h2 {
 color: #666; background-color: transparent;
 border-bottom: 1px solid #ccc ; width:50%;
 font-size: 14px; font-weight: bold; margin-top:20px;}
#txt_red{
 color:#dc143c; font-weight: bold;}
#msg_red{
 color:#dc143c; font-weight: bold;
 background: #ffbbcc;
 border:1px solid #ffaacc; width:60%;
 padding:10px; font-size:1.4em;}
#SomeElementId{
 color:#ff0000; font-weight: bold;
 background: #b8e08f;
 border:1px solid #bfdb58; width:60%;
 padding:10px; font-size:1.4em;}
</style>
</head>
<body>
<!-- main_content //-->
<?=$main_content?>
<!--// main_content -->
</body>
</html>


ビュー側(中身): application/views/tasklist_view.php

<h1>Tasklistへようこそ!</h1>
<h2>↓セッションの値確認↓</h2>
<p id="msg_red"><?= $info ?></p>
<ul>
<li><a href="/tasklist/sesstest1">/tasklist/sesstest1 に行ってセッションを格納してみる</a></li>
<li><a href="/tasklist/sesstest2">/tasklist/sesstest2 でセッションを破棄して/tasklistにリダイレクト</a></li>
</ul>

<h2>↓XAJAXのテスト↓</h2>
<div id="SomeElementId">下のボタンを押してちょ、中身が書き変わるよ</div>
<p/>
<!-- php側の_AjaxTest()は、javascript側では xajax_ をつけて xajax__AjaxTest(引数) で呼べる。
 外部から直接呼ばれないようにファンクション名を
 _AjaxTestとしてアンダーバーをつけているので_が2つある -->
<input type="button" value="押してみる" onclick="xajax__AjaxTest(2);">

<h2>↓キャプチャのテスト↓</h2>
<form method="post" action="/tasklist/captest">
<?=$cap_img?>
<p>上に見える文字を素早く入力してください</p>
<p id="txt_red"><?=$cap_msg?></p>
<input type="text" name="captcha" value="" />
<input type="submit" name="submit" value="★キャプチャのテスト★">
</form>


動かしてみる

(機能と見た目は昨日と同じなので、写真は一枚のみ)


うむ、問題なく動作する。



これで外枠は固定で中身だけ作れば良くなった。すばらしい。

Free CSS Templates - http://www.freecsstemplates.org/ とか、
Open Web Design - http://www.openwebdesign.org/ とかから
美しいテンプレートをダウンロードして適用したりしやすくなった。(ライセンスには注意)



明日は綺麗なテンプレートをはめ込んで、見た目からやる気をだすか。