アリスはプラダの鞄を蒐集する

自分でも主旨がよくわからないけれどもアリプラシリーズの続き。アリス・イン・ワンダーランドとプラダを着た悪魔に似ているような似てないような…という関係は後付けで。

アリスに Prada をプレゼントすると貰うけど、Tiffany をプレゼントしても貰わない、ってのが次のコードです。Prada と Tiffany のクラスを一度 dynamic_cast してみてチェックする訳ですね。

// for アリスはプラダの鞄を蒐集する
#include <iostream>
#include <list>
using namespace std;

class Bag {
protected:
	bool used;
public:
	virtual bool getUsed() { return used; }  
	virtual void Used() { this->used = true; }  
};

class Prada : public Bag {};
class Tiffany : public Bag {};

class Alice
{
private:
	list<Bag*> bags;
public:
	Alice() {
	}
	void Present( Bag *bag ) 
	{
		if ( dynamic_cast<Prada*>(bag) != NULL ) 
		{
			bags.push_back( bag );
		}
	}
	int CountBags() 
	{
		return bags.size();
	}
};

int main(void)
{
	Alice alice;
	
	cout << &quot;alice has &quot; << alice.CountBags() << &quot; bags.&quot; << endl;
	alice.Present(new Prada());
	cout << &quot;alice has &quot; << alice.CountBags() << &quot; bags.&quot; << endl;
	alice.Present(new Tiffany()); // ★
	cout << &quot;alice has &quot; << alice.CountBags() << &quot; bags.&quot; << endl;
	alice.Present(new Prada());
	cout << &quot;alice has &quot; << alice.CountBags() << &quot; bags.&quot; << endl;

	return 0;
}

実行するとこんな感じ。

D:\work\blog\src\alice>a
alice has 0 bags.
alice has 1 bags.
alice has 1 bags. ★
alice has 2 bags.

3番目に Tiffany をプレゼントしていますが受け取って貰えません。贅沢者です。

一方で、鞄ならなんでも受け取るけど、見せるときは Prada だけ、というのがロリータです。

// for ロリータはプラダの鞄だけを見せる
#include <iostream>
#include <list>
using namespace std;

class Bag {
protected:
	bool used;
public:
	virtual bool getUsed() { return used; }  
	virtual void Used() { this->used = true; }  
};

class Prada : public Bag {};
class Tiffany : public Bag {};

class Lolita
{
private:
	list<Bag*> bags;
public:
	void Present( Bag *bag ) 
	{
		bags.push_back( bag );
	}
	list<Bag*> LookBags() 
	{
		list<Bag*> temp;
		for ( list<Bag*>::iterator it = bags.begin(); it != bags.end();  ++it ) {
			if ( dynamic_cast<Prada*>(*it) != NULL ) {
				temp.push_back(*it);
			}
		}
		return temp;
	}
};

int main(void)
{
	Lolita lolita;
	
	cout << &quot;lolita has &quot; << lolita.LookBags().size() << &quot; bags.&quot; << endl;
	lolita.Present(new Prada());
	cout << &quot;lolita has &quot; << lolita.LookBags().size() << &quot; bags.&quot; << endl;
	lolita.Present(new Tiffany());
	cout << &quot;lolita has &quot; << lolita.LookBags().size() << &quot; bags.&quot; << endl;
	lolita.Present(new Prada());
	cout << &quot;lolita has &quot; << lolita.LookBags().size() << &quot; bags.&quot; << endl;

	return 0;
}
D:\work\blog\src\alice>a
lolita has 0 bags.
lolita has 1 bags.
lolita has 1 bags.
lolita has 2 bags.

ロリータの場合は、Present された鞄は bags にため込むけど、いざ LookBags で見せてもらうと、Prada に一致するものしか見せません。

このクラスを判別する方法ですが、C# だと 変数名 as クラス名 でキャストをしてチェックができます。他にも GetType なりでチェックする方法もあります。
ここで面白いのは、クラス名がクラスの属性のひとつとなっているところで、Prada クラスか Tiffany クラスで作られたオブジェクトを判別するのに、オブジェクトの属性ではなく、クラスそのものの属性というのがミソですね。

例えば、JUnit では Test というメソッドをリフレクションで拾う訳ですが、C# だと [TestCase] という形で属性を与えます。その昔はこのような考え方がなかったので、クラスを判別するときは明示的にクラス名を protected 変数などで設定したのです。

class Prada {
protected:
  char *classname = "Prada";
public:
  ...
};

こんな感じで。そう、CakePHP で自前のクラス名を指定している(PHP4対策だそうです)を見て、そんなことを考えたりしました。UML で云えば、クラス変数オブジェクト変数という違いだったり。

カテゴリー: C++ | アリスはプラダの鞄を蒐集する はコメントを受け付けていません

CakePHP から SlimStat へアクセスする(準備)

アクセス解析では wordpress の SlimStat と Google Analytics を使っているのですが、SlimStat も Analytics もグラフ表示が flush で出来ているから iPad で確認ができないんですよね。ちょっと困る。なので、数値だけでも見たいなぁと思いつつ、まずは SlimStat から手をつけるか、と思っています。

SlimStat のテーブル構造は簡単で、6つのテーブルしかありません。しかも、自分で解析するときに必要なのは、wp_slim_stats ぐらいなものです。wp_slim_visits を見ると訪問者毎のアクセス数が分かるみたいなのですが、トラッキングコード自体は slimstat が使っているだけなので、実質 wp_slim_stats だけで解析が可能です。

テーブル構造はこんな形。

id, int(10) unsigned, NO, PRI, , auto_increment
ip, int(10) unsigned, YES, , 0, 
language, varchar(5), YES, , , 
country, varchar(2), YES, , , 
domain, varchar(255), YES, , , 
referer, varchar(255), YES, , , 
searchterms, varchar(255), YES, , , 
resource, varchar(255), YES, , , 
browser_id, smallint(5) unsigned, NO, , 0, 
screenres_id, smallint(5) unsigned, NO, , 0, 
plugins, varchar(255), YES, , , 
visit_id, int(10) unsigned, NO, , 0, 
dt, int(10) unsigned, YES, , 0, 

ひとまず、

select distinct searchterms from wp_slim_stats
order by searchterms desc
limit 1000;

なんていうクエリを叩くと、検索されてくる単語が割り出せます。
これを時刻(dt)で区切ってやれば、最近やってくるキーワードが割り出せますね。

他にもブログへのアクセス(resource)をチェックしていけば、直近よく読まれている記事 top 10 という表示も可能ですね。

という訳で、実装は後程。

カテゴリー: 雑談, CakePHP | CakePHP から SlimStat へアクセスする(準備) はコメントを受け付けていません

アリスは初物がお好き

今度は、プラダの鞄でも最初の製品だけを好む、という設定にしてみます。同じオブジェクトでも最初だけっていうのは製造番号が付いているとか、シルクスクリーンに番号が振ってあるとか、限定品っていう意味合いですね。これは、COM で言うところの参照カウンタと同じです。

// for アリスは初物がお好き
#include <iostream>
using namespace std;

class Bag {
protected:
	bool used;
	static int _count;
public:
	Bag() : used(false) {
		_count++;
		cout << &quot;constractor &quot; << _count << endl;
	}
	bool getUsed() { return used; }  
	void Used() { this->used = true; }  
	// 参照カウンタ
	int getCount() { return _count; }
};
// 参照カウンタを初期化
int Bag::_count = 0;

class Prada : public Bag {};
class Tiffany : public Bag {};

// 初物好きなアリス
class Alice
{
public:
	Alice() {}
	bool DoYouLike( Bag *bag ) 
	{
		if ( bag->getCount() != 1 || bag->getUsed() == true ) {
			return false;
		} else {
			return true;
		}
	}
	void Use( Bag *bag ) {
		bag->Used();
	}
};
// 悪友ロリータ
class Lolita 
{
public:
	bool DoYouLike( Bag *bag ) {
		return true;
	}
	void Use( Bag *bag ) {
		bag->Used();
	}
};

int main(void)
{
	Alice alice;
	Lolita lolita;
	{
		Prada bag;
		if ( alice.DoYouLike( &bag ) ) {
			cout << &quot;alice like first Prada.&quot; << endl;
		} else {
			cout << &quot;alice don't like other Prada.&quot; << endl;
		}
	}
	{
		Prada bag;
		// アリスは初物じゃなくちゃ嫌
		if ( alice.DoYouLike( &bag ) ) {
			cout << &quot;alice like first Prada.&quot; << endl;
		} else {
			cout << &quot;alice don't like other Prada.&quot; << endl;
		}
	}
	return 0;
}

同じように Prada bag として bag を作成しているけど、最初の bag と次の bag は違います。C++ の場合は参照カウンタをクラスの外で初期化しないと駄目なんですが(C++0x だとできたっけ?)、C# だとクラス内で初期化ができるので static で指定すればOK。

using System;

public class Bag {
	protected bool _used = false;
	protected static int _count = 0;
	public Bag() {
		_count++;
	}
	public void Use() {
		_used = true;
	}
	public bool Used {
		get { return _used; }
	}
	public int Count {
 		get { return _count; }
	}
}
public class Prada : Bag {}
public class Tiffany : Bag {}

// 初物好きなアリス
public class Alice {
	public bool DoYouLike( Bag bag ) {
		if ( bag.Count != 1 || bag.Used == true ) {
			return false;
		} else {
			return true;
		}
	}
}

public class Program {
	public static void Main(string []args ) {
		Alice alice = new Alice();
		
		Prada prada = new Prada();
		if ( alice.DoYouLike( prada ) ) {
			Console.WriteLine("alice likes first prada.");
		} else {
			Console.WriteLine("alice don't like other prada.");
		}
		// 2つ目を作る
		prada = new Prada();
		if ( alice.DoYouLike( prada ) ) {
			Console.WriteLine("alice likes first prada.");
		} else {
			Console.WriteLine("alice don't like other prada.");
		}
	}
}
カテゴリー: C++ | アリスは初物がお好き はコメントを受け付けていません

アリスは中古のプラダがお嫌い(2)

アリスはプラダの中古がお嫌い | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/1442

前回、こんなところで中古のプラダを渡すと激怒して例外を発生させましたが、今回は大人しく false を返すことにします。

#include <iostream>

class Bag {
protected:
	bool used;
public:
	Bag() : used(false) {}
	virtual bool getUsed() { return used; }
	void Used() { Bag::used = true; }
};
class Prada : public Bag {};
class Tiffany : public Bag {};

// 中古が嫌いなアリス
class Alice
{
public:
	bool DoYouLike( Bag *bag ) 
	{
		if ( bag->getUsed() == true ) {
			return false;
		} else {
			return true;
		}
	}
	void Use( Bag *bag ) {
		bag->Used();
	}
};
// 悪友ロリータ
class Lolita 
{
public:
	bool DoYouLike( Bag *bag ) {
		return true;
	}
	void Use( Bag *bag ) {
		bag->Used();
	}
};

using namespace std;
int main(void)
{
	Alice alice;
	Lolita lolita;
	{
		Prada bag;
		if ( alice.DoYouLike( &bag ) ) {
			cout << &quot;alice like New Prada.&quot; << endl;
		} else {
			cout << &quot;alice don't like Used Prada.&quot; << endl;
		}
	}
	{
		Prada bag;
		lolita.Use( &bag );
		// アリスは他人が使ったものが嫌い
		if ( alice.DoYouLike( &bag ) ) {
			cout << &quot;alice like New Prada.&quot; << endl;
		} else {
			cout << &quot;alice don't like Used Prada.&quot; << endl;
		}
	}
	{
		Prada bag;
		alice.Use( &bag );
		// 自分が使っても嫌い???
		if ( alice.DoYouLike( &bag ) ) {
			cout << &quot;alice like New Prada.&quot; << endl;
		} else {
			cout << &quot;alice don't like Used Prada.&quot; << endl;
		}
	}
	return 0;
}

ただし、これを実行するとですとね、

D:\work\blog\src\alice>a
alice like New Prada.
alice don't like Used Prada.
alice don't like Used Prada.

な風に自分が使ったプラダの鞄も嫌いになってしまうのです。なので、

  • 自分が使った鞄は中古でも OK(中古というよりも自分のものだし)
  • 他人が使った鞄は嫌い

という判別を付けたいとすると。

class Alice
{
public:
	Alice() {}
	bool DoYouLike( Bag *bag ) 
	{
		if ( bag->getMark() == this || bag->getUsed() == false ) {
			return true;
		} else {
			return false;
		}
	}
	void Use( Bag *bag ) {
		bag->setMark( this );
		bag->Used();
	}
};

のように、Use メソッド中であらかじめ自分の使っていた鞄のポインタを保存しておいて、DoYouLike メソッドではそれと比較します。自分のだったら OK って具合ですね。

このあたりのパターンは、自 Factory クラスで作ったオブジェクトは利用できるけど、他 Factiory クラスで作ったオブジェクトは弾くってな感じで使えます。独自 malloc なんかを作るとき、単純に free したりするとアウトになってしまうので、自分のかどうかを判別するわけです。このフラグは、オブジェクト自身に置いてもよいし、Factory クラス自身に置いても OK。どちらも、ポインタ分だけを消費しますが、作成されたオブジェクトのほうに置くほうが、自他の判別は早くなるのかなぁと。

マーキングを利用したパターンが以下です。

class Bag {
protected:
	bool used;
	void *_mark;
public:
	Bag() : used(false), _mark(NULL) {}
	virtual bool getUsed() { return used; }
	void Used() { Bag::used = true; }
	// マーキング
	void setMark( void *mark ) { this->_mark = mark; }
	void *getMark() { return _mark; }
};
class Prada : public Bag {};
class Tiffany : public Bag {};

// 中古が嫌いなアリス
class Alice
{
public:
	Alice() {}
	bool DoYouLike( Bag *bag ) 
	{
		if ( bag->getMark() != this && bag->getUsed() == true ) {
			return false;
		} else {
			return true;
		}
	}
	void Use( Bag *bag ) {
		bag->setMark( this );
		bag->Used();
	}
};

この場合は、Bag クラスを修正しないといけないので、ちょっと変な気もしますが、鞄に名前を付けておくとか、自分だけ分かるシールを貼っておくとかができますよね。マーキング自体は void * になるので、”alice” という文字列でもよいし、ユニークな数値でも構いません(32,64bit以内であれば)。この手のマーキングは、C# だと GUID として使われているのだろうなぁ、と。

カテゴリー: C++ | アリスは中古のプラダがお嫌い(2) はコメントを受け付けていません

アリスはプラダがお好き

プラダの鞄を直接あげるのが値渡し | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/1431
プラダの鞄をおねだりするのが参照 | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/1434
アリスはプラダの中古がお嫌い | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/1442

プラダの鞄シリーズを再開w

ってな訳で、ちょっと C++ で組んでみました。
Bag クラスを基底クラスにして、Prada と Tiffany クラスを作ります。そして、ルイスが尋ねるのです。 Do you like this ?

なので、Alice クラスに DoYouLike メソッドを追加して Bag を渡すとどうなるでしょうか?

#include <iostream>

class Bag {
protected:
	bool used ;
public:
	virtual void setUsed() { used = true; }
};
class Prada : public Bag {};
class Tiffany : public Bag {};

#if 0
// おバカなアリス
class Alice
{
public:
	bool DoYouLike( void *bug ) 
	{
		return true;
	}
};
#else
// 賢いアリス
class Alice
{
public:
	bool DoYouLike( Bag *bag ) 
	{
		Prada *prada = dynamic_cast<Prada*>(bag);
		if ( prada == NULL ) {
			return false;
		} else {
			return true;
		}
	}
};
#endif

using namespace std;
int main(void)
{

	Alice alice;
	{
		Prada bag;;
		if ( alice.DoYouLike( &bag ) ) {
			cout << &quot;alice like Prada.&quot; << endl;
		} else {
			cout << &quot;alice don't like Prada.&quot; << endl;
		}
	}
	{
		Tiffany bag;;
		if ( alice.DoYouLike( &bag ) ) {
			cout << &quot;alice like Tiffany.&quot; << endl;
		} else {
			cout << &quot;alice don't like Tiffany.&quot; << endl;
		}
	}
	return 0;
}

おバカなアリスのほうは、Bag が貰えたらそれで嬉しいので、true を返します。メソッドを見ると分かりますが、何も考えていませんね。

賢いアリスのほうは、dynmaic_cast を使ってアップキャストをします。Prada も Tiffany も Bag クラスを継承しているので、DoYouLike メソッドには Bag* で渡すのですが、これが Prada なのか Tiffany なのかを Alice 自身が判断します。

D:\work\blog\src\alice>a
alice like Prada.
alice don't like Tiffany.

そうすると、こんな風に、Prada の鞄だけを好みます。
この判別の仕方、ちょっとなんだかなぁと思うのは、

  • 基底クラスに virtual なメソッドをひとつ必要とする。
  • dynamic_cast でキャストを実行するのじゃなくて、元の型を判別したいなぁ。

ってところですね。virtual メソッドは、dynamic_cast の制限のひとつで(でいいのかな?)、ひとつだけ仮想関数がないと駄目なんです。
キャストを実行せずに、元の型をというのは、多分 Java や C# のような型をランタイムで持っている言語じゃないと難しいと思います。そもそも C++ は型自身はメモリ上に持っていませんからね。単なる構造体と変わらないのです(v_tblとか)。

ちなみに C# で書くとこんな感じ。

using System;

public class Bag {}
public class Prada : Bag {}
public class Tiffany : Bag {}

public class Alice {
	public bool DoYouLike( Bag bag ) {
		if ( bag as Prada != null ) {
			return true;
		} else {
			return false;
		}
	}
}

public class Program {
	public static void Main(string []args ) {
		Alice alice = new Alice();
		
		Prada prada = new Prada();
		if ( alice.DoYouLike( prada ) ) {
			Console.WriteLine("alice likes prada.");
		} else {
			Console.WriteLine("alice don's likes prada.");
		}

		Tiffany tiffaniy = new Tiffany();
		if ( alice.DoYouLike( tiffaniy ) ) {
			Console.WriteLine("alice likes tiffaniy.");
		} else {
			Console.WriteLine("alice don's likes tiffaniy.");
		}
	}
}

やっぱり as 演算子でキャストして試してみるのが良さそうです。

カテゴリー: C++ | アリスはプラダがお好き はコメントを受け付けていません

オレオレ MVC を作ってみる(2)

オレオレ MVC を作ってみる(1) | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/1861

前回のコードを少し変更して wordpress のカテゴリ一覧とカテゴリ内の記事を表示させます。

■最初の index.php

まずは、クラス名とメソッドを取り出す index.php から。

<html>
<head>
<meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot;/> 
</head>
<body>
<?php
$url = $_GET['url'];
$url = strtolower($url);

$vars =  preg_split('|/|',$url);
$params = array();
if ( count($vars) == 1 ) {
	$base = $url;
	$method = &quot;index&quot;;
} else if ( count($vars) == 2 ) {
	$base = $vars[0];
	$method = $vars[1];
} else {
	$base = $vars[0];
	$method = $vars[1];
	for($i=2; $i<count($vars); $i++ ) {
		$params[] = $vars[$i];
	}
}
$classfile = $base.&quot;_controller.php&quot;;
$classname = strtoupper(substr($base,0,1)).substr($base,1).'Controller';

echo &quot;classfile: $classfile<br/>&quot;;
echo &quot;classname: $classname<br/>&quot;;
echo &quot;method: $method<br/>&quot;;
echo &quot;param: &quot;;print_r($params); echo &quot;<br/>&quot;;

include('controllers/'.$classfile);
$c = new ReflectionClass($classname);
$obj = $c->newInstance();
$method = $c->getMethod($method);
$method->invokeArgs($obj,$params);
?>
</body>

GET で送られる URL から「/」でパースするところは同じで、メソッドのパラメータを受けれるようにします。これの難点は常に body タグ内に view のコードが入ってしまうことですよね。一旦他のレンダリングに任せて、その中で view のコードを呼ぶようにしないと、head タグなどが書き出せません。

■ビューを作る

<!-- views/categories/item.php -->
<h1>カテゴリ一内の記事</h1>
<table>
	<tr>
		<th>Post.id</th>
		<th>Post.post_title</th>
		<th>User.display_name</th>
		<th>Post.post_date</th>
		<th>Post.guid</th>
	</tr>
	<?php foreach($Categories as $item) : ?>
	<tr>
		<td><?php echo $item['Post']['id']; ?></td>
		<td><?php echo $item['Post']['post_title']; ?></td>
		<td><?php echo $item['User']['display_name']; ?></td>
		<td><?php echo $item['Post']['post_date']; ?></td>
		<td><?php echo $item['Post']['guid']; ?></td>
	</tr>		
	<?php endforeach; ?>
</table>

ビューの作りは CakePHP の *.ctp と同じ。このあたりは互換が保てて楽ちん。

■コントローラーを作る

<?php
include 'models/category.php';
class CategoriesController {
	var $name = 'CategoriesController';
	function index() {
		// view に渡す変数
		global $Categories;
		// モデルを作成して
		$category = new Category();
		// 検索
		$Categories = $category->find('all');
		include('views/categories/index.php');
	}
	function item($slug) {
		// view に渡す変数
		global $Categories;
		// モデルを作成して
		$category = new Category();
		// 検索
		$Categories = $category->findBySlug($slug,10);
		include('views/categories/item.php');
	}
}
?>

コントローラーは、モデルを new してからメソッドを呼ぶだけ。最後に view をインクルードして表示ってな具合です。この include 部分を変えて index.php との連携を取らないといけませんね。

■モデルの基底クラスを作る

モデルの基底クラスを作りました。

<?php
class Model {
	var $cn ;
	function open() {
//		$this->cn = new PDO(&quot;mysql:host=127.0.0.1;dbname=moonmile_cake&quot;,
//			&quot;moonmile_cake&quot;,&quot;cakephp&quot;);
		$this->cn = new PDO(&quot;mysql:host=127.0.0.1;dbname=wordpress&quot;,
			&quot;wordpress&quot;,&quot;wordpress&quot;);
	}
	function close() {
		$this->cn = null;
	}
	function query($sql) {
		$this->open();
		$results = $this->cn->query($sql);
		$this->close();
		return $this->toArray($results);
	}
	function toArray($stmt) {
		$items = array();
		foreach($stmt as $item) {
			foreach($item as $key => $value) {
				if (strpos($key,'.') == true ) {
					list($table,$column) = split('\.',$key);
					$tables[$table][$column] = $value;
				}
			}
			$items[] = $tables;
		}
		return $items ;
	}
}
?>

ひとまず、データベースの open と close は、ここから継承することにすると。後、クエリを実行した後で配列に直すのが前回はださかったので、toArray メソッドを作って自動化します。こうすることで、Term.term_id のような名前をクエリで付けておけば $item[‘Term’][‘term_id’]のように参照ができます。

■モデルを作る

この基底クラスを継承して Category モデルクラスを作ります。

<?php
require('lib/model.php');

class Category extends Model {
	var $name = 'Category';
	function find($type) {
		$sql = <<< HERE
select
	Term.term_id	`Category.term_id`,
	Term.name		`Category.name`,
	Term.slug		`Category.slug`
from
	wp_terms as Term
  	inner join wp_term_taxonomy as TermTaxonomy
  	on ( Term.term_id = TermTaxonomy.term_id )
WHERE
	TermTaxonomy.taxonomy = 'category'
	order by Term.term_id
;
HERE;
		$items = $this->query($sql);
		return $items ;
	}

	function findBySlug($slug,$max) {
		$sql = <<< HERE
select
	Post.ID				`Post.id`,
	Post.post_title		`Post.post_title`,
	User.display_name	`User.display_name`,
	Post.post_date		`Post.post_date`,
	Post.guid			`Post.guid`
from
	wp_terms as Term
  	inner join wp_term_taxonomy as TermTaxonomy
  	on ( Term.term_id = TermTaxonomy.term_id )
	inner join wp_term_relationships as TermRelationship
	on ( TermTaxonomy.term_taxonomy_id = TermRelationship.term_taxonomy_id ) 
	inner join wp_posts as Post
	on ( TermRelationship.object_id = Post.ID ) 
  inner join wp_users as User
  on ( Post.post_author = User.ID )
WHERE
	TermTaxonomy.taxonomy = 'category'
AND Post.post_status = 'publish'
AND Term.slug = '$slug'
	order by Post.post_date desc 
	limit $max ;
HERE;
		$items = $this->query($sql);
		return $items ;
	}
}
?>

完全にクエリを書くということで、非自動化 O/R マッピングッ!!! SQL を分かっている場合はこれでも十分かなぁと。下手に Model::$belongsTo 等で悩むよりは、と考えてしまいます。

余談ですが、CakePHP のアソシエーションは、LINQ to Entities のアソシエーションのようにグラフィカルかつダイアログ等で入力してから、コードに直してやれば良いかと思っています。LINQ to Entities の設定って XML ファイルに書き出されるので、同様に CakePHP のアソシエーションを XML に設定しておいて読み込ませるってこともできますね。

このオレオレ MVC の O/R マッピングは完全に SQL 文を書くしかない体制なので、この部分をコピー&ペーストして MySQL Workbench で実行してチェックってことになります。なので、inner join を使ってきっちり結合させます。

■実行してみる

これだけのコードで MVC が実現できます(非常にチープですが)。

▼カテゴリ一覧

▼カテゴリ内の記事

まぁ、これだとリストの表示しかできないので、お次は insert, update, delete ですね。いや、その前にレイアウト読み込みをさせないと。

カテゴリー: 開発, CakePHP | オレオレ MVC を作ってみる(2) はコメントを受け付けていません

LINQ to Entities の実行クエリを表示する(続)

ちょっと前回の続き。

LINQ to Entities の実行クエリを表示する | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/1983

マスターページに渡すのに ViewData を使っていたのだが、static なクラスなんだから、そのまま渡してしまってもいい訳です。ViewData を使うと、モデル→コントローラー→ビュー となってモデルで LINQ を定義しているのに、わざわざコントローラーで ViewData に指定しないという手間が発生する訳で。

まずは、LINQ to Entities のクエリを表示するクラスを作ってしまいます。

public static class DebugSQL
{
    private static List<string> sqlList = new List<string>();
    public static string SQL
    {
        get
        {
            string sql = &quot;&quot;;
            foreach (string s in sqlList)
            {
                sql += s + &quot;\n&quot;;
            }
            return sql;
        }
                
    }
    public static List<string> SQLList
    {
        get {
            return sqlList; 
        }
    }
    public static string ToTraceString(IQueryable query)
    {
        var objectQuery = query as ObjectQuery;
        if (objectQuery != null)
        {
            return objectQuery.ToTraceString();
        }
        else
        {
            return &quot;no sql;&quot;;
        }
    }
    public static void Add(IQueryable query)
    {
        var objectQuery = query as ObjectQuery;
        if (objectQuery != null)
        {
            sqlList.Add( objectQuery.ToTraceString());
        }
    }
    public static void Clear()
    {
        sqlList = new List<string>();
    }
}

スタティックなクラスにするので、モデルやコントローラーで LINQ を使うところで、こんな風に DebugSQL::Add メソッドを使って追加していきます。

List<CategoryModel> model = new List<CategoryModel>();
var items = from t in ent.wp_terms
            where t.wp_term_taxonomy.FirstOrDefault().taxonomy == &quot;category&quot;
            select t;
DebugSQL.Add(items);

今回は複数回の呼び出しにも対処しているので、LINQ を使っているところで Add しておけば OK。

そして、マスターページ(Site.Master)

<div id=&quot;debug&quot;>
    <h2>実行クエリ</h2>
    <% List<string> sqls = MvcWordpress.Models.DebugSQL.SQLList; %>
    <% foreach (var item in sqls) { %>
        <%: item%><br />
    <% }%>
    <% MvcWordpress.Models.DebugSQL.Clear(); %>
</div>

で、こんな風に SQLList プロパティを参照してループで廻して表示します。
こうしておくと、CakePHP のように実行クエリが表示できます。

このすさまじいクエリ(苦笑)を SQL Server Management Studio なんかにコピー&ペーストして使えば、まぁ、なんかが分かるかもってな具合で。

SELECT            TOP (10) object_id, ID, post_author, post_date, post_date_gmt, post_content, post_title, post_category, post_excerpt, post_status, comment_status, ping_status, post_password, 
                        post_name, to_ping, pinged, post_modified, post_modified_gmt, post_content_filtered, post_parent, guid, menu_order, post_type, post_mime_type, comment_count
FROM              (SELECT            Extent1.object_id, Extent3.ID, Extent3.post_author, Extent3.post_date, Extent3.post_date_gmt, Extent3.post_content, Extent3.post_title, Extent3.post_category, 
                                                 Extent3.post_excerpt, Extent3.post_status, Extent3.comment_status, Extent3.ping_status, Extent3.post_password, Extent3.post_name, Extent3.to_ping, Extent3.pinged, 
                                                 Extent3.post_modified, Extent3.post_modified_gmt, Extent3.post_content_filtered, Extent3.post_parent, Extent3.guid, Extent3.menu_order, Extent3.post_type, 
                                                 Extent3.post_mime_type, Extent3.comment_count
                         FROM              wp_term_relationships AS Extent1 INNER JOIN
                                                 wp_term_taxonomy AS Extent2 ON Extent1.term_taxonomy_id = Extent2.term_taxonomy_id LEFT OUTER JOIN
                                                 wp_posts AS Extent3 ON Extent1.object_id = Extent3.ID
                         WHERE             ('category' = Extent2.taxonomy) AND (Extent2.term_id = @p__linq__0)) AS Project1
ORDER BY       post_date DESC

ええ、こちらのクエリの解析は後程。

カテゴリー: ASP.NET | LINQ to Entities の実行クエリを表示する(続) はコメントを受け付けていません

LINQ to Entities の実行クエリを表示する

LINQ to SQL の場合は Log プロパティを使う

LINQ to SQLのログ出力をVisual Studioの[出力]ウィンドウに出力するには?[3.5、C#、VB] - @IT
http://www.atmarkit.co.jp/fdotnet/dotnettips/801linqoutput/linqoutput.html

ってことで良いのですが、LINQ to Entities の場合は、ObjectQuery クラスにキャストして ToTraceString を使うってのが常套手段らしいです。

[C#]LINQ to Entitiesで発行されているSQL文を見たい
http://blogs.wankuma.com/kazuki/archive/2009/06/01/173926.aspx

上で示されているようなメソッドを作ればよいので、ASP.NET MVC で CakePHP 風な実行クエリを表示するようにしてみます。クエリを表示させると SQL Server Management Studio などで実行できるので確認が楽なんですよね。後、変なクエリをしていないかを確認できます。

using System.Data.Objects;

public static class DebugSQL
{
    private static string _sql;
    public static string SQL
    {
        get
        {
            return _sql;
        }
    }
    public static void ToTraceString<T>(IQueryable<T> query)
    {
        var objectQuery = query as ObjectQuery;
        if (objectQuery != null)
        {
            _sql = objectQuery.ToTraceString();
        }
        else
        {
            _sql = &quot;no sql;&quot;;
        }
    }
}

こんな形で、static class を定義しておきます。これだと1回のクエリしか保存できないので、複数回のクエリも保存したいですね。これは後程修正します。

そしてコントローラー中で、

            var items = from t in ent.wp_terms
                        where t.wp_term_taxonomy.FirstOrDefault().taxonomy == "category"
                        select t;
            DebugSQL.ToTraceString(items);

のように LINQ を実行した後に、DebugSQL.ToTraceString で SQL を保存しておきます。

これを画面に表示する時には、CakePHP のようにマスターページを使いたいので、Site.Master を変更します。

        <div id=&quot;main&quot;>
            <asp:ContentPlaceHolder ID=&quot;MainContent&quot; runat=&quot;server&quot; />
            <div id=&quot;debug&quot;>
                <h2>実行クエリ</h2>
                <%: ViewData[&quot;SQL&quot;] %>
            </div>
            <div id=&quot;footer&quot;>
            </div>
        </div>

こんな風に ViewData を使えばいいです。

これを設定するために、コントローラーで ViewData を指定します。

public ActionResult Index()
{
    Models.CategoryModel cate = new Models.CategoryModel();
    var model = cate.GetCategories();
    ViewData["SQL"] = Models.DebugSQL.SQL;
    return View(model);
}

CakePHP ではレイアウトのページで実行SQLを表示しているので、この手のデバッグ情報はコントローラーやモデルには書きたくないところですよね。。。ちょっと工夫したいところです。

まぁ、それでも一応、実行するとうまくクエリが取れます。

このクエリを SQL Serve Management Studio で実行するとこんな感じ。

実は、Visual Studio 2010 上でもクエリが実行できて、サーバーエクスプローラーから「新しいクエリ」でペインを開けます。

余分なウィンドウを消して、クエリを実行すると(余計なことに)クエリ文を整形してくれます。

元のクエリ文が残らないのでちょっと不安なのですが、うまくサブクエリになっている(笑)ことがわかります。

出力された結果が微妙なサブクエリになっているところが不安を醸し出します。アソシエーション経由でクエリを掛けるとこんな感じになるんですねぇ。わざわざ TOP を使ってサブクエリになっているのは FirstOrDefault メソッドを使っているからなので、このあたりは EDM の アソシエーションを 1対1 にすれば直るのかもしれません。

カテゴリー: 開発, ASP.NET | LINQ to Entities の実行クエリを表示する はコメントを受け付けていません

ASP.NET MVC から wordpress のデータを扱う(4)

ASP.NET MVC では LINQ to Entities を使えるので、あらかじめリレーションをしておくと楽…なんですが、逆にリレーションが設定されていない場合は大変ってのがありますね。CakePHP の場合は Model::$belongsTo 等で後からリレーション/アソシエーションを設定するのですが、LINQ to Entities の場合はこれが大変、というか、これはどう設定すればいんでしょう???な状態なのですが、ええ、実は意外と簡単でした。

デザイナの背景をクリックして「追加」→「アソシエーション」ですね。

そして、アソシエーションのプロパティで「参照に関する制約」を忘れずに設定

という訳です。詳しくは後ほどブログに書きますが、ひとまず(個人的に)速報まで。

さて、お次はカテゴリ内の記事の一覧を作ります。関連するテーブルは以下の4つ。

  • terms: カテゴリ名などの名称
  • term_taxonomy: terms の分類(category, link_category など)
  • term_relationships: カテゴリとの親子関係
  • posts: 記事自体

CakePHP から wordpress のデータを扱う(3) | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/1922

と同じように書いていきます。

■ビューを作る

目標となるビューはこんな感じです。

<%@ Page Title=&quot;&quot; Language=&quot;C#&quot; MasterPageFile=&quot;~/Views/Shared/Site.Master&quot; Inherits=&quot;System.Web.Mvc.ViewPage<IEnumerable<MvcWordpress.Models.CategoryModel>>&quot; %>

<asp:Content ID=&quot;Content1&quot; ContentPlaceHolderID=&quot;TitleContent&quot; runat=&quot;server&quot;>
	Index
</asp:Content>

<asp:Content ID=&quot;Content2&quot; ContentPlaceHolderID=&quot;MainContent&quot; runat=&quot;server&quot;>

    <h2>カテゴリ一覧</h2>
    <table>
        <tr>
            <th>term_id</th>
            <th>term_taxonomy_id</th>
            <th>name</th>
            <th>slug</th>
        </tr>

    <% foreach (var item in Model) { %>
        <tr>
            <td><%: item.Term.term_id %></td>
            <td><%: item.TermTaxonomy.term_taxonomy_id %></td>
            <td><%: item.Term.name %></td>
            <td><%: item.Term.slug %></td>
        </tr>
    <% } %>
    </table>
    <h2>カテゴリ一覧(リンク版)</h2>
    <ul>
    <% foreach (var item in Model) { %>
        <li><a href=&quot;/Categories/Item/<%: item.Term.slug %>&quot;><%: item.Term.name %></a></li>
    <% } %>
    </ul>
</asp:Content>

CakePHP と同じように CategoryModel というモデルクラスを独自に作ります。理由は同じで、wp_terms も wp_term_taxonomy も名前として適切ではないからです。そうそう、後から書きますが、実は今回の場合はデータベースに View を作ったほうがよいです。と言うのも、wp_term の扱いがメタ情報データ的に扱われているので、wp_posts や wp_comments のような通常のテーブルと扱いが違うためです。このあたりの理由は、時間があれば別途。

カテゴリのそれぞれの参照は、「item.Term.term_id」に、元テーブル名.カラム名 のようにアクセスできる形にしておきます。カテゴリのための別名を作っても良いのですが、プログラミングするときに覚えることが増えてしまう(再マッピングのために)ので、この程度の複雑度ならばこのまま使います。カラム名がちょっと不明な感じ(T200idとか)の場合であれば、別名を定義しますが。

■コントローラーを作る

コントローラーはこんな感じにシンプルになります。

public class CategoriesController : Controller
{
    //
    // GET: /Categories/

    public ActionResult Index()
    {
        Models.CategoryModel cate = new Models.CategoryModel();
        var model = cate.GetCategories();
        return View(model);
    }
}

最初作っていたときは、ここに LINQ が入っていたのですが、やめました。理由としては、

  • データベースで外部キー/リレーションを使っているのに、有効活用できていない。
  • リレーションの複雑さをコントローラーに持ち込むのは適切ではない。モデルとしてデータベースに近いところに置くべきだろう。

としたためです。ちょっとした試行錯誤の結果ですが、簡単なクエリであればコントローラーにおいてもよいのですが、複雑になるとちょっとという感じがしますね。このあたり、ASP.NET MVC の場合は、コントローラーとモデルの役割分担が難しいという感じがします。逆に言えば、CakePHP のほうは、モデルでやるのが適切という強制力が働いています。

■モデルを作る

モデルでは、このように LINQ to Entities のクラスを活用しています。

public class CategoryModel
{
    public wp_terms Term { get; set; }
    public wp_term_taxonomy TermTaxonomy { get; set; }

    /// <summary>
    /// カテゴリ一覧を取得
    /// </summary>
    /// <returns></returns>
    public List<CategoryModel> GetCategories()
    {
        wordpressEntities ent = new wordpressEntities();
        List<CategoryModel> model = new List<CategoryModel>();
        var items = from t in ent.wp_terms
                    where t.wp_term_taxonomy.FirstOrDefault().taxonomy == &quot;category&quot;
                    select t;
        foreach ( var item in items )
        {
            model.Add(new CategoryModel
            {
                Term = item,
                TermTaxonomy = item.wp_term_taxonomy.First()
            });
        }
        return model; 
    }
}

カテゴリ一覧を表示する時に必要なテーブルは2つ(wp_termとwp_term_taxonomy)なので、wp_term を中心にして検索しています。
カテゴリだけを表示するための LINQ に工夫があって、アソシエーションを利用しています。

こんな風に既に EDM(Entity Data Model)のほうには外部キーが設定してあるので、相互にテーブルが参照できます。具体的には、t.wp_term_taxonomy.taxonomy として、wp_term_taxnomy テーブルのカラムを指定できる訳ですね。ここでは、wp_term(1)-(*)wp_term_taxnomy という関係なので(私の外部キーの設定が間違っているのでこんな風になっています。本来は 1対1 です)、FirstOrDefault メソッドを使ってコレクションの最初を拾ってきていますが、アソシエーションを設定しておくとデータベースの外部キーを LINQ で再度 join しなくて済みます。
と言いますか、データベースに指定してあるのに、再び LINQ で join するのは O/R マッピング的に本末転倒ですよね。関係というロジックが2箇所に分散されてしまいます。

これを実行した結果が次です。

綺麗に表示されていますが、実際の作成手順は CakePHP と逆になりますね。ASP.NET MVC だとどうしてもコンパイルエラーに引かれてしまうのと、インテリセンスを使ってのコーディングが普通なので、次の順序で作ります(実際作ったし)。

  1. wordpress のデータベースに外部キーを設定。
  2. データベースから EDM へ取り込む(モデルの作成)
  3. EDM を使って コントローラーを仮に作成。LINQ 版を作る。
  4. ビューを作成して、LINQ のテスト。
  5. EDM を使って CategoryModel を作成。
  6. コントローラーの LINQ を CategoryModel に移してリファクタリング

な手順です。いきなり、CategoryModel を作ろうとすると失敗するので、まずはコントローラーに書いて実験するところから始めました。そして、方針が固まったら CategoryModel に移してという流れですね。

お次は、カテゴリ内の一覧を ASP.NET MVC で作ります。

カテゴリー: 開発, ASP.NET, Wordpress | ASP.NET MVC から wordpress のデータを扱う(4) はコメントを受け付けていません

美女Linux iGoogle ブログパーツを公開

さっくりと、開発バージョンを公開しておきます。本家 bijo-linux.com への公開はもうちょっとお待ちを

美女Linux 0.9 – 自分のホームページに追加
http://www.google.com/ig/directory?url=bijo-linux.com/bp/js/bijo-0.9.xml

http://www.google.com/ig/directory?url=bijo-linux.com/bp/js/bijo-off-0.9.xml

http://www.google.com/ig/directory?url=bijo-linux.com/bp/js/bijo-feb-0.9.xml

美女Linux v0.9
http://www.google.com/ig/adde?moduleurl=http://bijo-linux.com/bp/js/bijo-0.9.xml&source=imag
美女Linux v0.9 オフショット版
http://www.google.com/ig/adde?moduleurl=http://bijo-linux.com/bp/js/bijo-off-0.9.xml&source=imag
美女Linux v0.9 Feb.版
http://www.google.com/ig/adde?moduleurl=http://bijo-linux.com/bp/js/bijo-feb-0.9.xml&source=imag

以前、作ったときの手順を忘れていて…

美女Linux 公開 | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/1642

なんだ、xml を作るだけだったか、っていうオチでした。
プレビューのページなどを付けておかないと。

カテゴリー: 開発, ブログパーツ | 美女Linux iGoogle ブログパーツを公開 はコメントを受け付けていません