2007年11月21日

CodeIgniter:一度は使ってみたかったHTMLテーブルクラス

前回は日本語を含むURIでエラーが出る場合の(我流の)対処法を書きましたが、今回はそもそもの元になった五十音パッド(これもまた正確な?というか標準的な名前がわからないので勝手に命名していますが…)を作成していきたいとおもいます。

ただ作ってみると結構複雑な問題も絡んできてBlog的には長くなりそうな悪寒なので、今回はそのGUI?っつーか表面的な部分までの話を書きます。で、どんなものかといいますと…

gojuonpad.png

こんな感じで平仮名(の一部。「ん」とか「を」とかは省く)をただ並べた表です。各文字はアンカーが設定してあってクリックした文字で始まる○市町村が一覧表示される(予定)という仕様です。

表の上に書いてある|新市町村|旧市町村|はタブとおもってください(^^;。まだcssを書いてないのでなんだかわかりづらいですが、これで新/旧を選択するようにします。

で、今回はこの表をCodeIgniterのHTMLテーブルクラスで書いてみます。ちなみに以前にもHTMLテーブルクラスを試しに使ってみましたと書いたことがありました。その時はメインのテーブルをクラスを使って出力しようとして、結局却下されてベタ打ちになりましたが、今回はなんとかクラスで出力しています。ということでまずControllerでクラスをロードします。
$this->load->library('table');

で、新たにViewファイルgojuon.phpを作成して、メインのViewからロードしておきます。
<?$this->load->view('gojuon');?>

これでさしあたっての準備はできたので、gojuon.phpの中身を書いてみます。
$kana = array('あ','い','う','え','お',
'か','き','く','け','こ',
'さ','し','す','せ','そ',
'た','ち','つ','て','と',
'な','に','ぬ','ね','の',
'は','ひ','ふ','へ','ほ',
'ま','み','む','め','も',
'や','ゆ','よ', '', 'わ');
$gojuon = $this->table->make_columns($kana, 5);
echo $this->table->generate($gojuon);

無茶苦茶シンプルですね。単に「あ〜わ」までの一次元配列を作って、それをmake_columnsに渡してるだけです。まあこの状態だとアンカーもタブもないしテーブルテンプレートも適用してないので本当にただの五十音表なわけですが…(こんなん今時小学生でも見向きもすまい)

で、アンカーを付け足します。
foreach($kana as $chr)
{
if ($chr <> '')
{
$withAnchor[] = anchor('gappei/kanaSearch/'.urlencode($chr), $chr);
} else {
$withAnchor[] = '';
}
}

URLエンコードした文字をkanaSearch()に渡すリンクです。「やゆよ」と「わ」の間に空白セルを作りたかったので、空白の場合はリンクなしにします。もちろんmake_columnsには今度はアンカー付きの$withAnchor配列を渡すようにします。

で、一応kanaSearch()もControllerに記述しておきます。といってもechoだけですが…。
function kanaSearch($cap)
{
echo $cap;
}

あえてindex()にもリダイレクトさせないでおきます。実は前回はこのあたりまで書いてから、散々五十音パッドをクリックして、エラーが出たとか出ないとかあ〜だこ〜だやっていたわけです。成功すれば「ふ」とか「む」とか平仮名一文字が表示されます(なんだか虚しい)

次に↑の画像をよく見ると「て、ね、へ、め」の部分にアンカーが設定されていないのがわかるとおもいます(リンク色と地の文字色が似ててちょっと見難い)が、これは別にバグとかではなくて正常な結果です。

何をやっているかというと、状態としては新市町村のタブをクリックした時点で、次の動作としては五十音のどれか一文字をクリックすることを期待していますが、その際に不要な文字はアンカーを付けない状態にしたかったわけです。

不要な文字とは要するに新市町村のふりがなの最初の一文字に現れない文字です。いわゆるディム化というか、仮にクリックされたとしても検索結果は一件もない文字をあらかじめ選択できない状態で表示しておくということです。

で、これを実現するにあたって旧市町村テーブル(データベース)に読み(ふりがな)フィールドを追加しなければなりません。新市町村テーブルは元々yomiフィールドを作ってありましたが、旧市町村テーブルに新たにoyomiというフィールドを追加してふりがなを入力しました。
|  1  | 函館市 | はこだてし |
| 2 | 戸井町 | といちょう |
| 3 | 恵山町 | えさんちょう |
| 4 |南茅部町| みなみかやべちょう |
| 5 |椴法華村| とどほっけむら |


| 2021|具志頭村| ぐしかみそん |

さすがに2000件以上のふりがなを入力するとかなり辛い作業でした(嘘)ンナワケナイッショ

それはさておき、要するに欲しいのは[新/旧]市町村の読みの最初の一文字の一覧なわけです。なのでまずControllerにデータを取ってくるメソッドを追加します。
function _getCap($field) { }

んで一応MVC(かなり崩壊してきてるけど…)ということで、実際データベースから取ってくるコードはModelの方に書きます。
function getCap($field)
{
$this->db->select('cap');
$this->db->from($field);
return $this->db->get();
}

ここで疑問におもわれるとおもいますが、上記のActiveRecordの記述では…
SELECT cap FROM ($field:実際はテーブル名);

というSQL文しか発行されません。capってなんぞ?なわけですが、実はデータベース上で予めビューを作成することにしました(ってかしています)。
CREATE VIEW ncap AS
SELECT DISTINCT SUBSTRING(newcities.yomi FROM 1 FOR 1) AS cap
FROM newcities
ORDER BY newcities.yomi;

つまり新市町村テーブルの読み(ふりがな)フィールドの一文字目を重複なしで順番に並べたテーブルということです。ここでSUBSTRINGというのはPHPの関数でいえばsubstrとほとんど同じものです。ただしその後のFROM xx FOR yyみたいな部分の書式?はsubstrと若干異なっていて、FROM 1で一文字目という意味になります(substrは0から)。

上はnewcitiesの方から抽出?されるビューですが、旧市町村(olocities)の方もほぼ同じようにしてocapというビューを予め作っておきます。

で、先ほどのModelに作ったgetCap()メソッドで結果を取得しますが、Controller側ではさらにもう一工夫しなければなりません。
function _getCap($field)
{
foreach($this->Gdb->getCap($field)->result_array() as $row)
{
$cap = mb_convert_kana($row['cap'], 'h', 'UTF-8');
$cap = mb_substr($cap, 0, 1, 'UTF-8');
$kana[] = mb_convert_kana($cap, 'H', 'UTF-8');
}

$kana = array_unique($kana);

return $kana;
}

ただ結果を配列に入れて返すだけなら、$this->Gdb->getCap($field)->result_array()だけでいいのですが、よくよく考えてみると市町村の読みの最初の一文字には濁点や半濁点のつくものがある可能性があります。しかし今作成中の五十音パッドには濁点・半濁点はリストする予定はなく、例えば「た」で検索された場合に「だ」で始まるものも出力しようと考えています。

そこで少々トリッキーかもしれませんがデータベースからの取得結果(これは全部平仮名です)を半角カタカナに変換します。半角カタカナというのは濁点や半濁点が別の文字になっているわけです。

だ→ダ = タ + ゙
ぴ→ピ = ヒ + ゚

そして半角カタカナに変換後最初の一文字目を取得します。こうすることで濁点・半濁点を含む地名(の読み)も清音の仮名一文字にまとめることができます。

最後にそれを再び平仮名に変換してダブりを削除して返します。ダブりというのは、例えば地名に「だいまる」と「たかやま」があった場合に上記の変換をすると「た」が2つ出力されてしまうため、array_uniqueで重複を削除しています。

んでindex()では上記メソッドを呼んで$data['cap']などに格納してViewに送ります。
$data['seltab'] = (get_cookie('kanatab') <> '') ? get_cookie('kanatab') : 'ncap';
$data['cap'] = $this->_getCap($data['seltab']);

クッキーは五十音パッドのタブ(つまり新市町村か旧市町村か?)の選択状態を取得しています。

で、ここまでで[新/旧]市町村に存在する地名の読みの一文字目が濁点・半濁点を吸収した形で一覧として取得できたわけですが、今度はこれをViewに反映させなければなりません。
$dim_kana = array_diff($kana, $cap);
foreach($kana as $chr)
{
if (in_array($chr, $dim_kana) === FALSE)
{
if ($chr <> '')
{
$withAnchor[] = anchor('gappei/kanaSearch/'.urlencode($chr), $chr);
} else {
$withAnchor[] = '';
}
} else {
$withAnchor[] = $chr;
}
}

↑の方で五十音にアンカーを付け足している部分のコードを少々変更しています。まず$cap(データベースから取得した一覧)と$kana(五十音表)の差分を取ります。この配列($dim_kana)は[新/旧]市町村の地名の一文字目に使われていない文字の配列になります。

そしてforeachの中でin_arrayでその配列に該当するかどうかを調べて、該当する場合はアンカーを付けないようにします。これでようやく↑の画像で示したように該当地名のない五十音にはリンクがつかない形で五十音パッドを出力することができます。(ふぅ、結構メンドイ)

で!ここまで書いてハタと気がつきましたが、ってか今まで気がつかなかったのかよ!といったことなんですが、なんと「らりるれろ」の行がない!(--;(真面目にやってます。本当に気づいてなかった…)

ということで急遽(爆)$kanaに付け足します。
$kana = array('あ','い','う','え','お',
'か','き','く','け','こ',
'さ','し','す','せ','そ',
'た','ち','つ','て','と',
'な','に','ぬ','ね','の',
'は','ひ','ふ','へ','ほ',
'ま','み','む','め','も',
'や','ゆ','よ', '', 'わ'
'ら','り','る','れ','ろ');

こんな感じで…(って何がこんな感じだ)オオボケです。それでもちゃんとロジックは働いてるようで…

gojuonpad2.png

どうやら旧市町村で「ら」と「れ」で始まる地名はないようですね、ふむふむ(何がふむふむなのか!・・・と)

んで突然話はHTMLテーブルクラスに戻りますが、上記のコードだけでは↑の画像のようにはなりません。もちろんタブもまだ書いてません。ってことでまずはテーブルテンプレート。
$tmpl = array('table_open'=>'<table border="1" cellpadding="2" cellspacing="0" width="154">',
'cell_start'=>'<td align="center">',
'cell_alt_start'=>'<td align="center">');
$this->table->set_template($tmpl);
$this->table->set_empty(" ");

単にボーダーを指定したかっただけです。枠なしじゃなんか“パッド”っていう感じがしなかったもので。そんでcellも中揃えにしたかったので'cell_start'=>'<td align="center">って書いてみたら、なんと奇数行しか中揃えにならない。なので止む無くaltの方も指定しました。

set_emptyは空白セルをどうするか?って話らしいです。まあ1個しかありませんが…。で、これでgenerateすれば出力されるんですが、この辺の書き方っつーか書く場所?が結構自分の中でも曖昧になってきていて…。Viewファイルには書いているものの特にクラスを作ったわけでもないし、でも一応出力系のコードは外に出すか?とかなんとかかなりあやふやです(^^;。
<div>|
<?=anchor('gappei/changeKanaTab/ncap', '新市町村');?> |
<?=anchor('gappei/changeKanaTab/ocap', '旧市町村');?>
|</div>
<?=$this->table->generate($gojuon);?>

なんとなく外(<?php ?>の外という意味)に出してみました。まあ気休めにもなってませんが(ってかすぐに<?=とか書いてるし…)。一応echoはしなかったぞ!ということで…。

んでタブ部分はchangeKanaTabをパラメータ付けて呼びます。パラメータは↑で作ったデータベースのビューの名前そのものです。呼ばれた側の中身は…。
function changeKanaTab($field)
{
set_cookie('kanatab', $field, Gappei::COOKIE_LIFE);
redirect('gappei/index/');
}

のようにまたまたクッキーを設定しているだけです。

ちなみにクッキーに保存するかセッションデータに保存するかの分類?は、

検索条件・ソート条件などそのセッション中だけに限定したいモノ
→セッション
表示件数・表示モードなどある程度の期間保っていたいモノ
→クッキー

みたいな感じで適当に(テキトーに)分けています。なので五十音パッドのタブはクッキーです。(なんでだ???ま、いっか)次回はやっと検索部分の実装を書く予定です。
ラベル:codeigniter
posted by ciallost at 20:27| Comment(0) | TrackBack(0) | 日記 | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。

この記事へのトラックバック