2008年02月25日のニュースとネタをお届けします
MovableTypeには画像などのファイルをアップロードする機能がついています。MovableTypeではこのアップロードしたファイルのことをアセットと呼び、MTAssetという関数群でこれらをブログ上に表示させることができます。しかしMTAsset関数はまだまだ数が少なく、十分に情報を引き出せるとはいえません(そもそもMovableTypeのテンプレート上では大掛かりなカスタマイズ/処理は行えない)。また画像のアップロードと同時に生成されるサムネイルとその表示用アルバムページも1回生成したらそれっきり。再構築ができないため『次の画像へのリンク』といったページャーリンクを作れないという、致命的な欠陥も抱えています。そこで今回僕はPHPを使ってMovableTypeのデータベースに直接アクセス、データを引っ張ってきて処理するというプログラム(関数)を作成することにしました。と、ここまでが長い前フリ。
以下手っ取り早く完成品の関数を。なお当方の制作・動作確認済み環境は『PHP5.23 x MySQL5.1.20-beta x MovableType4.1』です。PHPはおそらく4でも問題なく動くはずですし、MySQLのバージョンもおそらく問いません(SQLiteでも少しいじれば大丈夫)。ただMovableTypeのバージョンは4以降でないとダメな気がしますので、あらかじめご了承ください。また文字コードはUTF-8かShift_JISを想定しています。
今回のプログラムのキモとなるGetMTAssetImageList関数は指定したID(MTAssetID)の情報をMovableTypeのデータベースから取得するための関数です。前後あわせて3件の画像のデータを取得するリストモード、指定件数の画像を一覧取得するインデックスモード、画像単体の情報を取得するIDモードを使えます。
function GetMTAssetImageList($Mode,$ID="",$Amount=10,$Offset=0,$Is_Thumbnail=0){
global $db;
$ID = intval($ID);
$Q_Select = "select `asset_id`,`asset_created_on`,`asset_description`,`asset_file_path`,`asset_label`,`asset_meta` from `mt_asset`";
// ここで取得を設定できる項目はasset_id, asset_blog_id, asset_class, asset_created_by, asset_created_on, asset_description, asset_file_ext, asset_file_name, asset_file_path, asset_label, asset_meta, asset_mime_type, asset_modified_by, asset_modified_on, asset_parent, asset_url です
$Q_Image = "`asset_class` = 'image'";
if($Is_Thumbnail){
$Q_Thumbnail = "`asset_parent` >= {$ID}";
$Q_Thumbnail2 = "`asset_parent` < {$ID}";
$Q_Thumbnail3 = "`asset_parent` IS NOT NULL";
}else{
$Q_Thumbnail = "`asset_id` >= {$ID} and `asset_parent` IS NULL";
$Q_Thumbnail2 = "`asset_id` < {$ID} and `asset_parent` IS NULL";
$Q_Thumbnail3 = "`asset_parent` IS NULL";
}
$Return = array();
switch($Mode){
case "List" :
$Query = "{$Q_Select} where {$Q_Image} and {$Q_Thumbnail} order by `asset_id` limit 2";
list($Return['This'],$Return['Next']) = DBDataFetch($Query);
$Query = "{$Q_Select} where {$Q_Image} and {$Q_Thumbnail2} order by `asset_id` DESC limit 1";
list($Return['Back']) = DBDataFetch($Query);
break;
case "Index" :
$Amount = intval($Amount);
$Offset = intval($Offset);
$Query = "{$Q_Select} where {$Q_Image} and {$Q_Thumbnail3} order by `asset_id` DESC limit {$Amount} offset {$Offset}";
$Return = DBDataFetch($Query);
break;
case "ID" :
$Query = "{$Q_Select} where {$Q_Image} and {$Q_Thumbnail} limit 1";
$Return = DBDataFetch($Query);
break;
}
foreach($Return as $key => $val){
if(!$val){
unset($Return[$key]);
continue;
}
if(isset($val['asset_meta'])){
preg_match_all("/([0-9]+)/",$val['asset_meta'],$temp);
if($temp){
unset($Return[$key]['asset_meta']);
list($Return[$key]['asset_meta']['height'],$Return[$key]['asset_meta']['width']) = $temp[1];
}
}
}
return $Return;
}
GetMTAssetTagList関数は指定したID(MTAssetID)の画像の持つタグ情報を取得するための関数です(画像とタグは別々のデータベースに記録されています)。IDの配列を引数に持たせて動かします。
function GetMTAssetTagList($AssetIDArray){
global $db;
$AssetTagIDArray = array();
$Return = array();
foreach($AssetIDArray as $AssetID){
if(!$AssetID){
continue;
}
$AssetID = intval($AssetID);
$Query = "select `objecttag_tag_id` from `mt_objecttag` where `objecttag_object_datasource` = 'asset' and `objecttag_object_id` = {$AssetID}";
$AssetTagIDArray[$AssetID] = DBDataFetch($Query);
}
foreach($AssetTagIDArray as $AssetID => $TagIDArray){
if(!is_array($TagIDArray)){
continue;
}
$Return[$AssetID] = array();
foreach($TagIDArray as $TagID){
if(!$TagID){
continue;
}
$TagID = intval($TagID);
$Query = "select `tag_name` from `mt_tag` where `tag_id` = {$TagID} limit 1";
list($Return[$AssetID][]) = DBDataFetch($Query);
}
}
return $Return;
}
GetMTAssetID関数は画像のファイル名、あるいはタグから画像を検索、該当するIDを(MTAssetID)取得する関数です。
function GetMTAssetID($From,$Source){
global $db;
$Return = array();
$Source = mysql_escape_string($Source);
switch($From){
case "FileName" :
$Query = "select `asset_id` from `mt_asset` where `asset_file_name` LIKE '{$Source}.%' AND `asset_class` = 'image' limit 1";
$temp = DBDataFetch($Query);
$Return = $temp[0]['asset_id'];
break;
case "TagLabel" :
mysql_set_charset("UTF8");
// PHP5以降かつMySQL5以降でないとmysql_set_charset関数は使えませんので、mysql_query("set names UTF8",$db);としておいたほうが確実です
$Query = "select `tag_id`,`tag_n8d_id` from `mt_tag` where `tag_name` = '{$Source}' limit 1";
print_r(mysql_error($db));
$MySQL = mysql_query($Query,$db);
while($temp[] = mysql_fetch_assoc($MySQL)){null;}
print_r(mysql_info($db));
if($temp){
if(!$temp[0]){
break;
}
if($temp[0]['tag_n8d_id'] == 0){
$temp[0]['tag_id'] = intval($temp[0]['tag_id']);
$Query = "select `tag_id` from `mt_tag` where `tag_n8d_id` = {$temp[0]['tag_id']} limit 1";
$temp2 = DBDataFetch($Query);
if($temp2[0]['tag_id']){
$TagID = $temp2[0]['tag_id'];
}else{
$TagID = $temp[0]['tag_id'];
}
}else{
$TagID = $temp[0]['tag_id'];
}
$Query = "select `objecttag_object_id` from `mt_objecttag` where `objecttag_object_datasource` = 'asset' and `objecttag_tag_id` = {$TagID}";
$temp = DBDataFetch($Query);
foreach($temp as $val){
$Return[] = $val['objecttag_object_id'];
}
}
break;
}
return $Return;
}
残り2つの関数はこれら3つの関数で用いる汎用的な関数です。
/*
MT_DB_HOST,MT_DB_USER,MT_DB_PASSはあらかじめdefineしておいてください
*/
function ConnectMTDB(){
global $db;
if($db = mysql_connect(MT_DB_HOST,MT_DB_USER,MT_DB_PASS)){
if(mysql_select_db(MT_DB_TITLE,$db)){
mysql_query("SET SESSION character_set_results = 'UTF8'");
// 環境によってはこの文字コードの指定は不要です
return TRUE;
}
}
return FALSE; // mysql_error()
}
function DBDataFetch($Query){
global $db;
$Result = array();
$MySQL = mysql_query($Query,$db);
while($Result[] = mysql_fetch_assoc($MySQL)){null;}
return $Result;
}
それではこれらの関数の使用例をご紹介します。
$AssetIDArray = GetMTAssetID("TagLabel","初音ミク");
// ちなみにデータベース内の情報はデフォルトではすべてUTF-8で保存されているので、この“初音ミク”もUTF-8でエンコードされていないと情報が正しくヒットしません。必要であればmb関数でエンコードを。
foreach($AssetIDArray as $AssetID){
if(!$AssetID){
continue;
}
print_r(GetMTAssetImageList($Mode="ID",$AssetID,10,0,0));
}
print_r(GetMTAssetTagList($AssetIDArray));
この例では『初音ミク』タグを登録している画像の情報をすべて取得します。GetMTAssetTagList関数で取得できた返り値はAssetIDをキーとして配列状のタグを要素とした多次元配列となっています(このなかには当然すべての2階層めの配列に『初音ミク』が含まれます)。MovableTypeで行える通常のタグ検索ではなぜか記事のデータしか取得できないので、これとあわせてはじめて真のタグ検索が可能になります(つうか何故SixApartがこれをサポートしないのかが謎です。もしかして僕が気づいてないだけでしょうか)。時間が出来たらTRASH-NEWSで取り入れてみます。
更に実践的な使用例をもうひとつ。
$ID = GetMTAssetID("FileName","20080124-Akiba-in-Snow");
$Return = GetMTAssetImageList($Mode="List",$ID,10,0,0);
print_r($Return);
foreach($Return as $key => $val){
if(!(is_array($val))){
print_r(GetMTAssetTagList(array($Return['asset_id'])));
break;
}else{
print_r(GetMTAssetTagList(array($val['asset_id'])));
}
}
この例ではファイルの名前が『20080124-Akiba-in-Snow』 + 『.(拡張子)』にあてはまる画像の情報を引き出します。このとき$Modeで"List"と指定していますので、『20080124-Akiba-in-Snow』自体の画像情報と、その前後の画像の情報もあわせて引き出します。これにより『前後の画像へのリンク』が可能となり、このようなページを作ることが可能となります。
実際の利用シーンとしては.htaccessとRewriteEngineを併用して上記の関数を呼び出す感じですね。
if($_GET['FileName'] && isset($_GET['FileName'])){
if($ID = GetMTAssetID("FileName",$_GET['FileName'])){
if($ImageListarray = GetMTAssetImageList($Mode="List",$ID,10,0,0)){
$ImageID = $ImageListarray['This']['asset_id'];
$ImageTagListarray = GetMTAssetTagList(array($ImageID));
}else{
die();
}
}else{
die();
}
}else{
header("Location:http://www.trash-news.net/pict/");
}
↓の.htaccessは画像のアップロードディレクトリと同じところにおけばよいかと思います。
RewriteEngine on
RewriteRule ^([0-9a-zA-Z_\-]+)\.html$ [↑のプログラム]?FileName=$1 [L]
![]()
これでhttp://www.trash-news.net/pict/20080124-Akiba-in-Snow.htmlへのアクセスで20080124-Akiba-in-Snow.jpgの情報ページを呼び出すことができます。
実はわざわざデータベースを直接叩かなくてもcsv形式で画像のデータを保存するテンプレートを作ればそれで良いのですが、なんだか負けたような気がするので頑張ってみました。どなたかのお役に立てれば幸いです。全文自作ですので、何かご不明な箇所やツッコミがあればコメント欄に容赦なくお願いします。
[追記] なお既知の問題としては、同じファイル名で拡張子の違う画像(MIKU.jpgとMIKU.png)が複数あると正しいリンクを生成できないという点、ファイル名に『.(ドット)』のついた画像の情報を引き出せない点があります。前者はうっかりやってしまいそうですが、後者に関してはたぶんMovableType側でそうならないようチェックしてくれていると思いますので心配は必要なさそうです。
[追記] GetMTAssetID関数内のクエリ文に"AND `asset_class` = 'image'"を追記しました。この指定がないと画像以外のファイルのIDを抽出しかねないため必須です。
[ 関連キーワード : (キーワードは登録されていません) ]
2008年02月26日 0時更新
| 日 | 月 | 火 | 水 | 木 | 金 | 土 |
|---|---|---|---|---|---|---|
| « 01月 | - | 03月 » | ||||
| 1 | 2 | |||||
| 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 17 | 18 | 19 | 20 | 21 | 22 | 23 |
| 24 | 25 | 26 | 27 | 28 | 29 | |
