Movable Type4ダイナミックパブリッシング対応プラグインの作成方法をまとめてみた。
公開日 : 2007-10-02 18:47:01
MT界隈でダイナミックパブリッシングに対応したプラグインが少ない! っていうのは、確かに面倒ってのもあるけどやっぱり情報やサンプルが少ないのが影響してるんじゃないかなぁ。僕自身も今まで放置していたのだけど、ちょっとソース覗きながらやってみたのでまとめておきます(間違いがあるかもしれませんがお気づきの点があればツッコミください。あと想定読者はPerlでプラグイン書いてる人、なのであんまり詳細の説明は書きませんのでそのあたりご了承くださいませ)。
追記: ってか、あるじゃん orz.
- Movable Type 3.3 マニュアル - ダイナミック・パブリッシング
- Six Apart ProNet Articles - Dynamic Publishing: PHP Architecture Overview
何せ今回これ (SQLでのLIKE検索) 作るためにはじめて書いたので色々試行錯誤でした。
さぁ、これで一ヶ月後くらいにはダイナミックパブリッシング対応が劇的に進む...かな?
サンプルは、こんなタグから...
<mt:blognamefromdb><br />
<mt:helloworld>
<ol>
<mt:phppluginloop>
<li><mt:foo addos="1">
<mtiffoo var="Mac">
<-Mac!
</mtiffoo>
</li>
</mt:phppluginloop>
</ol>
テンプレートを「ダイナミックパブリッシング」に指定して、foo.html?blog_id=1 とか引数を付けてアクセスすると以下のように出力される例です。MT4である前提で書きます。
/mt/plugins/plugin_name/php/以下にPHPで書いたプラグインを置きます。基本的に1つのblock,functionやmodifierにつき1つの拡張子phpファイルになります。
function.mthelloworld.php のように、種類.名前.php というファイル名を付けます。このタグは <mt:helloworld> のように呼び出すことができます。
1.Functionタグ(お決まりのHello World!から行ってみよう)
<?php
function smarty_function_mthelloworld ($args, &$ctx) {
return 'Hello World!';
}
?>
Hello World!と出力する、ただそれだけです。
ちょっと鬱陶しいなって思うのは、perl側ではこのタグが登録されたことを認識してくれないので、テンプレートの保存時に「テンプレートでエラーが見つかりました。 <mt:helloworld> は存在しません(1行目)」のようなエラーが出てしまうことです。これはもちろんPerl版プラグインをPHP対応させよう、という場合等既にPerl側でタグが登録されている場合は表示されません。まぁ、今回はPHP版だけなので最後の仕上げでエラーが出ないようにダミーのタグだけ登録してやることにしましょう。
2.Blockタグ(MT3で言うところのコンテナ・タグ)
ファイル名は block.mtphppluginloop.php になります。いきなりちょっと長くなりましたね。でもこれ先にやっとかないと条件分岐とかに進めないので。
<?php
function smarty_block_mtphppluginloop($args, $content, &$ctx, &$repeat) {
$localvars = array( 'foo', 'vars', '_counter');
if (!isset($content)) {
//初期化
$ctx->localize($localvars);
$counter = 0;
} else {
$counter = $ctx->stash('_counter');
}
$vars = $ctx->stash('vars');
if (!isset($vars)) {
//配列のセット
$vars = array('Mac','Win','Linux');
$ctx->stash('vars', $vars);
} else {
$counter = $ctx->stash('_counter');
}
if ($counter < count($vars)) {
//ループ処理
$foo = $vars[$counter];
$ctx->stash('foo', $foo);
$ctx->stash('_counter', $counter + 1);
$repeat = true;
} else {
//処理の終了
$ctx->restore($localvars);
$repeat = false;
}
return $content;
}
?>
この例では、('Mac','Win','Linux')ってな配列を作って3回だけループするタグを作ろうとしています。まぁ実際はSQLでDBから値取って来てループさせるようなものになるのでしょう。 $localvarsに入れている 'foo', 'vars', '_counter' がそれぞれ取り出す値, 配列, カウンタになります。
3.Functionタグ(<mt:phppluginloop>〜</mt:phppluginloop>ループの中で使えるタグ)
ファイル名 function.mtfoo.php, タグは<mt:foo>。これは至ってシンプルですね。ブロックタグで定義した $ctx->stash('foo', $foo); を出力するだけです。
<?php
function smarty_function_mtfoo ($args, &$ctx) {
return $ctx->stash('foo');
}
?>
4.Modifier(タグ属性)
ついでに頭に「OS:」ってのを追加するタグ属性を作りましょう。ファイル名はmodifier.addos.php, タグ属性は<mt:foo addos="1">ってな具合に指定します。これも説明するまでもないですね。これで「OS:Mac」のように頭に「OS:」が付きます(テンプレート側で書けるし意味ねぇですけどね。実際は置換したりフォーマット化したりそんな処理になるでしょう)。
<?php
function smarty_modifier_addos($text) {
return "OS:$text";
}
?>
5.Blockタグ(MT3で言うところの条件タグ)
ファイル名は block.mtiffoo.php, つまりこんな感じで↓mtif〜で分岐できる奴です。
<mtiffoo var="Mac">
<-Mac!
</mtiffoo>
せっかくなので (何が「せっかく」なのか分からんけどね) 属性値 varを指定してfoo=varかどうかで分岐するようにしてみます。
<?php
function smarty_block_mtiffoo ($args, $content, &$ctx, &$repeat) {
$var = $args['var'];
$foo = $ctx->stash('foo');
if ($var == $foo) {
return $ctx->_hdlr_if($args, $content, $ctx, $repeat, 1);
} else {
return $ctx->_hdlr_if($args, $content, $ctx, $repeat, 0);
}
}
?>
6.DBから値を引っ張る、URLのパラメタを取得する
BlockタグでもFunctionタグでも良いですが、DBから値を取得する例&URLのパラメタを取得する例です(この例はFunctionタグ)。引数によって処理を変えればページ分割とか簡単に実装できますよね。
内容はブログ名を表示するだけ。別に標準のMTタグで出来るわけですが、サンプルですからね。ファイル名 function.mtblognamefromdb.php, タグは <mt:blognamefromdb>。 foo.html?blog_id=1 のようにパラメタ付けることでブログを指定できるようにしてみます。
<?php
function smarty_function_mtblognamefromdb ($args, &$ctx) {
if (isset($_SERVER['REDIRECT_QUERY_STRING'])) {
//foo.html?blog_id=1
$_SERVER['QUERY_STRING'] = getenv('REDIRECT_QUERY_STRING');
}
$param = getenv('QUERY_STRING');
parse_str($param);
if ($blog_id) {
if (preg_match("/^[0-9]+$/", $blog_id)) {
$sql = "SELECT blog_name "
. "FROM mt_blog "
. "WHERE blog_id=$blog_id ";
$results = $ctx->mt->db->get_results( $sql , ARRAY_N );
foreach ($results as $row) {
return $row[0];
}
}
}
}
?>
実はパラメタ取得のところは一瞬ハマってしまったのですが、MTのダイナミックパブリッシングは mod_rewrite 又は 404,403ページとしてphpへアクセスさせて動作しているので mod_rewrite の場合 $blog_id = $_GET['blog_id']; のようにしても値を取得できません。
なので REDIRECT_QUERY_STRING を取得して parse_str で分解、という手順になります(この辺はサーバー環境によってちょっとハマることがあるかもしれませんね)。
DBへのアクセスですが、Perlと比較すると抽象化されていない...というか要はSQLです(抽象化というより、php/extlibにはezsqlとsmartyフォルダがありますね。ezsqlというライブラリを使ってるということでしょうか)。
普段Perlのプラグイン書き慣れていると、mt_ 付けるのとか entry_ とか付けるの忘れそうになります(ここは省略可にして欲しいなぁ)。SQLに値を突っ込むので、浄化は忘れないようにしましょうね(この例では数字かどうかチェックしてますが、通常は「'」とかいわゆるSQLインジェクション系の対策が必要* かと思います)。SQLに突っ込む直前ですよ。取得した時じゃないよ。Web2.0言うな! サニタイズ言うな! (笑)。
*ezsqlの仕様を知らないのですけど、まぁ必要でしょう。
7.テンプレート編集画面でエラーが出ないようにPerlプラグインを用意
手順が通常と逆なので(今回はPerlプラグインをダイナミックにも対応させるってわけじゃないので)、Perl版はダミープラグインみたいになりますが、CMSにタグを登録してエラーが出ないようにしましょう。
ファイル名は仮にPluginsPHP.plとします。これは想定読者の方には説明するまでもないでしょう。
package MT::Plugin::PluginsPHP;
use strict;
use MT;
use MT::Plugin;
our $VERSION = 0.1;
use base qw( MT::Plugin );
@MT::Plugin::PluginsPHP::ISA = qw(MT::Plugin);
my $plugin = new MT::Plugin::PluginsPHP({
id => 'PluginsPHP',
key => 'pluginsphp',
description => 'Sample PHP Plugins',
name => 'PluginsPHP',
author_name => 'Junnama Noda',
author_link => '/online/',
'version' => $VERSION,
});
MT->add_plugin($plugin);
sub init_registry {
my $plugin = shift;
$plugin->registry({
tags => {
block => {
'phppluginloop' => ¥&_foo,
'iffoo?' => ¥&_foo,
},
function => {
'helloworld' => ¥&_foo,
'foo' => ¥&_foo,
'blognamefromdb' => ¥&_foo,
},
modifier => {
'var' => ¥&_foo,
},
}
});
}
sub _foo {
}
1;
ふぅ...何とかまとまったかな。