院生エンジニアのにっき

2007-06-02 新ブログシステムへ移行

ブログシステム移行

CakePHPで作ったブログシステムへの移行を進めています。

こちらのサイトも残しておきますが、今後は面倒ですが

http://life-hack.jp/labo/charly

までお越しください。

CakePHP関連で検索して下さる方が多いので、CakePHP関連のカテゴリも記載しておきます。

http://life-hack.jp/labo/charly/category/3

今後ともよろしくお願いします。

2007-05-21 Cake1.2 Behavior

[]CakePHP1.2のBehaviorを使う

CakePHPの1.2系から導入された(1.1系でもディレクトリだけは存在したw)「Behavior」、これでController→Component、View→Helper、Model→Behaviorという住み分けが完成したそうです。

そこでこのBehavior、何が出来るかというと他のComponentやHelperと同じようにMVC各クラスに共通機能を追加するためのモジュールとなっています。

CakePHP1.2ではデフォルトで/libs/model/behaviorsにacl.php、translate.php、tree.phpが入っていて、Modelクラスに追加できるようになっています。

ユーザが作成したBehaviorクラスは/app/model/behaviorに置くのですが、modelクラスでのBehaviorの利用法が他のControllerクラスやViewクラスと異なっていたため若干はまりました。。

ます、ComponentとHelperの使い方は以下の感じです

<?php
class HogeController extends AppControlle{
  var name = 'Hage';
  var $components = array('Fuge');  //FugeComponentを利用
  var $helpers = array('Hoge');  //HogeHelperを利用
・・・
}
?>

となっています。なので慣れた人ならModelクラスでBehaviorを使うためにはこう書こうとするんじゃないでしょうか

<?php
class HogeModel extends AppModel{
  var $behaviors = array('Fuge');  //FugeBehaviorを利用・・・したい
・・・
}
?>

しかしFugeBehaviorがないと怒られてしまいます。。

  • Controller→Components
  • View→Helpers
  • Model→Behaviors

と思うじゃないですか。

しかし正解は

<?php
class HogeModel extends AppModel{
  var $actsAs = array('Fuge');  //FugeBehaviorを利用
・・・
}
?>

$actsAsなんですって。しかも自分の場合は$actsAsという存在をhttp://bakery.cakephp.org/articles/view/soft-delete-behaviorやCakeのコア部で発見していながら$actAsって書いてしまっていてさまよう始末・・・・・

まぁBehaviorは「ふるまい」を定義するので「Act」はまぁまぁ正しいんでしょうが、「Act」が複数形になって「actsAs」って書かないといけないとはやられました。。

2007-05-20 ScalixをCentOSにインストール(成功)

[][][]ScalixをCentOS4.4にインストール(導入前解説)

大学院生になってさらに複数のメールアドレスを管理しないといけないようになり、しかも大学院でもらったメールアドレスにはスパムメールがてんこもり・・・(一般に公開されているので)ということで新しくメールクライアントを導入しようかと考えました。

メールクライアントといえばGmailYahoo!などのWebmail系、Outlook ExpressやEdMax(私も使っています)などのインストール型がありますが、欲しい要件としては

  • Webベース(バイト先、自宅、大学院等様々な場所でPCを扱うため一元で管理したい)
  • 複数のメールアドレスを管理できる
  • 無料(有料でもいいのがあったらいいのですが、有料→完璧にすばらしいもの→未だ発見できず なので無料なら自分で色々いじる意欲もでますんで)
  • スパムメールを排除してくれる
  • 自分で細部までカスタマイズ可能

くらいかと思いますが、WebベースのGmailYahoo!ではカスタマイズがしにくいことやスパムフィルタリングが微妙そうという点、Gmail個人情報を覗き見されている感がある点などからパス(あと、外部のアドレスを管理できますが微妙に使いづらいので)。Outlook ExpressやEdMaxなどのインストール型は様々な環境でメールを受信するので一元管理をしたいな・・・と。

そんなわけでたまたま発見したScalixに白羽の矢が立ちました。

しかしそれは同時に困難な道のりの始まりでした。。


まず、Scalixがどんなものかを簡単に説明しておきます。

  • AjaxベースのWebメールシステム(Scalix Web Access)
  • 拡張性が高い(高そう)
  • 実はすごい歴史がありそう
    • 前身となるのはヒューレットパッカード社のHP OpenMailだそうです。HP OpenMailは10万ものユーザが利用していたとScalixのHPに書いており、安心感があります。
  • 携帯電話にも対応+セキュリティも充実
    • 歴史があり、生き残ってきた=様々な攻撃に耐えてきたと解釈できるので様々なノウハウが蓄積されていてセキュリティもあると考えられます。それに、まだ使ってませんが携帯電話にも対応しているので電車の中からちょろっとメールチェックもできるかと思います。
  • オープンソース(無料)である
    • まぁここはいいような悪いような微妙なラインですが、オープンソースとして公開することによってすごいユーザーによる改良があるかもということでしょうか。しかし残念ながら現在の自分の技術力ではこのScalixほど巨大なソフトウェアの改良をする技術はないと感じています(利用言語の違いもさることながら)。Scalix社が25ユーザー以上(プレミアムユーザーに対して:プレミアムユーザーとは管理者のことで、一般ユーザ(スタンダードユーザー)は無料で無制限に作れます)に対して課金しているのにオープンソースなのは、サポート的な意味合いが強いのでしょう。いかにもオープンソースでのビジネスモデルって感じです。

ちなみに、CentOSインストールをした(現在日本語化までしておきました)感想として、Scalixのデメリットをあげておきます

  • インストールが大変
    • まぁCentOSインストールしようとしたのがいけないんでしょうが(Scalixの対応OSRedHatSUSEFedoraです)、インストール前の環境チェックでほぼ間違いなく怒られるかと思います。Tomcatを入れたのにダメと怒られたりhostsの設定がダメだと怒られたり、何度心が折れそうになったことか・・・
  • 実はデフォルトでは複数メールアドレスの管理に対応していなかった
  • もしかしたらAjaxベースは微妙に使いにくい場合があるかも
    • AjaxWeb2.0感は認めます(正しい定義ではないですが)。しかし今のクライアントPC環境においてはまだフルAjaxなWebメールクライアントは時期尚早だったのかもしれません。確かに使いやすいのは使いやすいのですが、インストール型PCと比べると微妙に・・・本当に微妙なのですがかゆいところに手が届かない感はありました。細かい点ですが、「受信ボックス」を右クリックしてもメニューが出てこないとか、一括で領域指定(一箇所をクリックしてからシフトを押しながら他の場所をクリックしたら領域指定できると思ったのですが)ができない場合が多いとか、Ajaxベースのシステムを作った経験もあるので技術的な難点が多い事は分かりますが、やや不満はありました。まぁしかしそこはScalixのバージョンが上がるたびに改善されていくと信じています。

とりあえず簡単なScalixの説明はこのくらいです。

Scalixのインストール編についてはまた近いうちに書いていきます。

2007-05-12 Tips

[]CakePHPで存在しないcontrollerが指定された際にデフォルトのcontrollerクラスを利用する方法

現在作成中のBlogシステム(http://life-hack.jp/labo/charly)はCakePHPで作られているのですが、HatenaDiaryの仕組みを元にしているため、最後の「charly」の部分はユーザーIDとなっています。

しかし、CakePHPは不便なことにルートディレクトリ(この場合は「http://life-hack.jp/labo/」)の次のディレクトリ(この場合は「charly」)をコントローラークラスの名称だと認識してCharlyControllerを探しに行きます。

当然CharlyControllerは見つからないため「Missing Controller」となるわけです。

Missing Controllerを対処するためには

  1. 指定のcontrollerクラスが存在する
  2. /app/config/routes.phpをいじって指定のcontrollerクラスに値を渡せるようにする

の2通りが考えられます。

今回の目標ははBlogControllerに引数として「charly」とそれ以降の引数を渡すことです。


この対処法として色々試してみました。

  • /app/webroot/index.phpを改造+routes.phpにRouter::connect('/blog/*', array('controller' => 'blog', 'action' => 'index'));と記述
    • /app/webroot/index.phpにてDispatcherクラスを作成しているため、Dispathcerクラスにわたる前にURLから「charly」にあたる部分を抽出する
    • →うまくはいくのですが、URLがどうしても「http://life-hack.jp/labo/blog/」となってしまうので却下
  • /cake/libs/router.phpを修正する
    • Cakeのコア部分を修正します。
    • しかし・・・断念しました。
    • URLは「http://life-hack.jp/labo/charly」になるんですが、HtmlHelperなどで使われている$html->linkなどは全てrouterクラスを通してURLを作成していました。PaginatorHelperもrouterクラスを経由しているので、うまく作らないとCakePHP1.2系の恩恵が受けられなくなります。自分の場合はrouter.phpを下手に修正しようとしたせいでPaginateの次へ・前へのURLが利用できなくなるなどの不具合がありました。
  • /app/config/routes.phpをごりごり書く
    • 無理です・・・
    • routes.php正規表現によって記述しないといけないため、「いずれかの文字列(存在するcontrollerクラス)以外にマッチする文字列の場合」という指定がとてもとても大変です。
    • 存在する全てのcontrollerクラスに対するroutes.phpへのRoute::connectを書いた後にRouter::connect('/*', array('controller' => 'blog', 'action' => 'index'));を書くことで半分うまくはいきますが、様々な箇所でひずみが生じます・・・orz..

まぁ色々試してはみたんですが、一番あっさりしてうまくいっているのは

Dispatcherクラス(/cake/dispatcher.php)の修正でした。

CakePHPのだいぶコアな部分なので修正が広範囲に広がってしまうのではないかとびくびくしていましたが、要はMissing Controller」がでる際にデフォルトのcontrollerクラスをロードして指定してしまえばいいんだ・・・ということでした。

ってなわけでどんな修正をしたかというと・・・

<?
//下準備(/app/config/bootstrap.phpに記載→bootstrapに定数を指定するべきかの議論はここでは割愛)
define('DEFAULT_CONTROLLER','blog'); //デフォルトで使用するcontroller名
define('DEFAULT_CONTROLLER_ACTION','index'); //そのcontrollerで使用するaction名

///cake/dispatcher.phpの91行目以降(Revision: 4758にて)
if (empty($params['controller'])) {
	$missingController = true;
} else {
	$ctrlName = Inflector::camelize($params['controller']);
	$ctrlClass = $ctrlName.'Controller';

	if (!loadController($ctrlName)) {
		$pluginName = Inflector::camelize($params['action']);
		if (!loadPluginController(Inflector::underscore($ctrlName), $pluginName)) {
			if(preg_match('/([\\.]+)/', $ctrlName)) {
				Router::setRequestInfo(array($params, array('base' => $this->base, 'webroot' => $this->webroot)));

				return $this->cakeError('error404',
												array(array('url' => strtolower($ctrlName),
														'message' => 'Was not found on this server',
														'base' => $this->base)));
			//controllerクラスが見つからない場合
			} elseif(!class_exists($ctrlClass)) {
				//変更ここから
				$ctrlName = Inflector::camelize(DEFAULT_CONTROLLER);
				//強引にデフォルトのcontrollerクラスをロード
				if(!loadController($ctrlName)){
					$missingController = true;
				}else{
					$ctrlClass = $ctrlName.'Controller';
					//このままではパラメータが渡らないので新しいパラメータをURLから指定(若干の疑問あり)
					//ここを変更すれば、controllerクラスへ渡るパラメータを修正できる。
					$newParams = split('/',$url);
					$params['pass'] = $newParams;
					$params['controller'] = DEFAULT_CONTROLLER;
					$params['action'] = DEFAULT_CONTROLLER_ACTION;
				}
				//変更ここまで
				//$missingController = true;   //コメントアウト
			} else {
				$params['plugin'] = null;
				$this->plugin = null;
			}
		} else {
			$params['plugin'] = Inflector::underscore($ctrlName);
		}
	} else {
		$params['plugin'] = null;
		$this->plugin = null;
	}
}
?>

しかしやはりCakeコア部分を修正するのには気が引けました。

現在はこれでうまくいっていますが、もしかすると将来的に機能追加などした際に変な歪が生じるかも知れません。

やはり一番スマートそうなのはroutes.phpの修正で、「指定したcontrollerクラス以外ならこのクラスを利用する」といった指定が簡単にできることでしょうか・・・。

なにかスマートな方法を知っている・思いついた方がおられましたら連絡いただければ幸いです。

[]データベース設定

SVNを使っていたりFTPで一括でアップロードする際にローカル環境とサーバー環境が違うためアップロードしたとたんにエラーになるのを防ぐ方法

まぁアップロードする際にdatabase.phpフィルタリングする設定にすることでも事足りるんですが。

app/config/database.php
<?php
class DATABASE_CONFIG
{
	var $default;
	function __construct() {//ローカル環境での設定
		if(FULL_BASE_URL == 'http://localhost'){
			$this->default = array('driver' => 'mysql',
				'connect' => 'mysql_connect',
				'host' => 'localhost',
				'login' => '***',
				'password' => '***',
				'database' => '***',
				'prefix' => '***');
		} else {//サーバー上の設定
			$this->default = array('driver' => 'mysql',
				'connect' => 'mysql_connect',
				'host' => 'localhost',
				'login' => '***',
				'password' => '***',
				'database' => '***',
				'prefix' => '***');
		}
	}
}
?>

どこかで同じ事をしていた人を見ましたが、メモってことで。

if(FULL_BASE_URL == 'http://localhost’)を用いることによって様々な場面でローカルならこう、サーバーならこうっていうロジックを作成できます。

ちなみに自分の場合はGoogle AdSenseをローカルでは非表示にして、サーバーでは表示するなどといった作りをしています(誤クリックで利用停止になりたくないんで・・・)。←IPやらではじくようにする方が絶対ですが、色んなPCを使ってアクセスするんで開発環境では・・・ってだけ分けてます。

2007-05-10 ちょっと便利なコード

[][]置換について

<?php
function str_replace_ex($replace,$text){
	$out = str_replace(array_keys($replace), array_values($replace), $text);
	return $out;
}
echo str_replace_ex(
	array(
		'search1' => 'replaced1',
		'search2' => 'replaced2',
	),
	'search1とsearch2'
);
//出力: replaced1とreplaced2
?>

なるほどと思うTipsでした。 From CakePHP